diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 827c956a4..208aa4678 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -790,17 +790,19 @@ jobs: desktop: - 'app/desktop/**' - 'app/shared/**' + - 'app/workbench/**' - 'app/package.json' - 'app/pnpm-lock.yaml' - '.github/workflows/checks.yml' web: - 'app/web/**' - 'app/shared/**' + - 'app/workbench/**' - 'app/package.json' - 'app/pnpm-lock.yaml' - '.github/workflows/checks.yml' shell: - - 'app/shared/src/workbench/**' + - 'app/workbench/**' - 'app/shared/src/styles/**' - 'app/shared/src/theme*.ts' - 'app/shared/src/themePresets.*' @@ -827,14 +829,15 @@ jobs: - 'app/package.json' - 'app/pnpm-lock.yaml' - '.github/workflows/checks.yml' - # frontend = desktop ∪ web ∪ shared (coverage gate + vuln-scan-js - # trigger). Splits the heavy vitest coverage job out of the - # unconditional validate lane so a Go-only PR no longer pays for - # pnpm install + coverage. + # frontend = desktop ∪ web ∪ shared ∪ workbench (coverage gate + + # vuln-scan-js trigger). Splits the heavy vitest coverage job out + # of the unconditional validate lane so a Go-only PR no longer + # pays for pnpm install + coverage. frontend: - 'app/desktop/**' - 'app/web/**' - 'app/shared/**' + - 'app/workbench/**' - 'app/package.json' - 'app/pnpm-lock.yaml' - 'app/test-config/**' @@ -1420,6 +1423,12 @@ jobs: - name: Verify Shared Edge-free boundary run: python ./scripts/verify/verify-shared-boundary.py + - name: Verify frontend package boundary (workbench -> shared only, #1759) + run: python ./scripts/verify/verify-frontend-package-boundary.py + + - name: Self-test frontend package boundary gate (negative) + run: python scripts/verify/tests/verify-frontend-package-boundary.Tests.py + - name: Verify Shared barrel Edge-export ban run: python ./scripts/verify/verify-shared-barrel.py @@ -1496,7 +1505,7 @@ jobs: # The negative include-contract self-test (mobile vitest run) executes # once, on the mobile leg. matrix: - package: ['@agenthub/shared', 'agenthub-web', 'agenthub-desktop', 'agenthub-mobile-rn'] + package: ['@agenthub/shared', '@agenthub/workbench', 'agenthub-web', 'agenthub-desktop', 'agenthub-mobile-rn'] steps: - uses: actions/checkout@v7 with: diff --git a/AGENTS.md b/AGENTS.md index de4f46e17..705eacb7f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ | 目录 | 分类 | 职责 | |---|---|---| | `api/` | 活 | API 契约 SSOT(openapi.yaml/events.md/conventions.md) | -| `app/` | 活 | 前端 monorepo(web/desktop/mobile-rn/shared) | +| `app/` | 活 | 前端 monorepo(web/desktop/mobile-rn/shared/workbench) | | artifacts/ dist/ tmp/ | 产物 | 本地构建/临时输出(gitignored) | | `docs/` | 活 | 知识库(architecture/governance/reference/archives) | | `edge-server/` | 活 | Go Edge 服务(local runtime、adapters、lifecycle) | @@ -62,7 +62,7 @@ Mobile 主线是 Expo + React Native development build。旧 Tauri Mobile 不再 共享边界: - API 契约写在 `api/`。 -- 通用 UI、transcript、composer、inspector、platform contract 放 `app/shared/`。 +- 通用 UI、transcript、composer、inspector、platform contract 放 `app/shared/`;端级 workbench shell 放 `app/workbench/`(`@agenthub/workbench`,依赖方向 workbench → shared 单向,#1759)。 - Hub REST/WS 方法与 DTO 的 SSOT 是 `app/shared/src/hub/hubClient.ts`(及拆出的 payload/extended 模块)。Desktop/Web/Mobile 的 `app/{desktop,web,mobile-rn}/src/api/hubClient.ts` 只能是 thin shell(平台默认 baseUrl、Tauri proxy、SecureStore token、fixture snapshot、WS URL 等胶水);禁止在客户端再分叉 REST 实现。 - Desktop 只能把 Tauri/Rust native 能力放在 `app/desktop/src-tauri/`。 - Web 只能通过 Hub/Web adapter 访问远端能力,不能直连 Local Edge 或 runtime。 diff --git a/app/AGENTS.md b/app/AGENTS.md index 32bbe6ba1..e1ffcbe81 100644 --- a/app/AGENTS.md +++ b/app/AGENTS.md @@ -16,6 +16,7 @@ python scripts/verify/verify-design-token-ssot.py # 设计 token 门禁(改 ## 前端边界(速查,权威在根 AGENTS §2/§5) - 通用 UI 只在 `shared/src/ui/`;Desktop/Web/Mobile 从 shared 导入,禁止复制本地副本。 +- 端级 workbench shell 只在 `workbench/src/`(`@agenthub/workbench`,#1759 独立成包);web/desktop 从 `@agenthub/workbench` 导入,mobile-rn 不依赖。依赖方向 workbench → shared 单向,shared 禁止 import workbench(eslint + `scripts/verify/verify-frontend-package-boundary.py` 双重门禁)。 - `app/shared/src/hub/hubClient.ts` 是 Hub REST/WS 唯一 SSOT;各平台 `api/hubClient.ts` 只做 thin shell,禁止分叉 REST 实现。 - Desktop 原生能力只在 `app/desktop/src-tauri/`;Web/Mobile 是 Hub-only,不直连 Local Edge。 - CSS Modules + OKLCH tokens,避免硬编码颜色。 diff --git a/app/Dockerfile b/app/Dockerfile index 266938bca..8f3d5a00e 100644 --- a/app/Dockerfile +++ b/app/Dockerfile @@ -11,11 +11,13 @@ WORKDIR /app # Install dependencies first (cache layer) COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY shared/package.json ./shared/package.json +COPY workbench/package.json ./workbench/package.json COPY web/package.json ./web/package.json RUN pnpm install --frozen-lockfile # Copy source and build (vite only — tsc runs in CI) COPY shared ./shared +COPY workbench ./workbench COPY web ./web RUN pnpm --filter agenthub-web exec vite build diff --git a/app/README.md b/app/README.md index 3f8b74621..c0f6caa17 100644 --- a/app/README.md +++ b/app/README.md @@ -12,12 +12,14 @@ AgentHub 的全部前端代码,pnpm workspace 管理。Go 后端在 `../hub-se | `desktop/` | Tauri 桌面端 + Local Edge 宿主(Vite,5173 strict) | `desktop/README.md` | | `mobile-rn/` | Expo/RN 移动端(Hub-only,5177) | `mobile-rn/README.md` | | `shared/` | **`@agenthub/shared` SSOT**:通用 UI、`hubClient`、transcript/composer/inspector、platform contract、design token | `shared/README.md` | +| `workbench/` | **`@agenthub/workbench`**:端级工作台 shell(#1759 从 shared 独立);依赖方向 workbench → shared 单向 | `workbench/README.md` | | `e2e/` | 真实全栈 E2E(`chat-real.spec.ts` 等,需 live Hub+Edge,CI 沙箱不跑) | `e2e/playwright.config.ts` | | `test-config/` | 前端 coverage 契约 factory(生产源码全量进分母) | `test-config/coverage.ts` | ## SSOT 边界 - 通用 UI 只在 `shared/src/ui/`,web/desktop 从 shared 导入,禁止复制本地 UI 副本。 +- 端级 workbench shell 只在 `workbench/src/`(`@agenthub/workbench`);web/desktop 从 `@agenthub/workbench` 导入,mobile-rn 不依赖本包。依赖方向 workbench → shared 单向,shared 禁止 import workbench(机器门禁:`scripts/verify/verify-frontend-package-boundary.py`)。 - Hub REST/WS 方法与 DTO 的 SSOT 是 `shared/src/hub/hubClient.ts`;web/desktop/mobile 的 `api/hubClient.ts` 只能是 thin shell(平台胶水),禁止分叉 REST 实现。 - 设计 token 在 `shared/src/styles/` 与 `shared/src/designTokens.ts`(`--td-*`)。 - 新 shared 组件三件套:`.test.tsx` + `.stories.tsx` + 对照 `../docs/component-acceptance.md` 验收表。 diff --git a/app/desktop/README.md b/app/desktop/README.md index 6c5197ec3..891bdd329 100644 --- a/app/desktop/README.md +++ b/app/desktop/README.md @@ -25,7 +25,7 @@ Desktop UI -> Local Edge Server -> Agent Runtime adapter -> Claude Code / Codex | Views and slots | `src/views/` | | State/hooks | `src/stores/`, `src/hooks/` | | Tauri host | `src-tauri/` | -| Shared UI/workbench/chat | `../shared/src/ui/`, `../shared/src/workbench/`, `../shared/src/chatview/` | +| Shared UI/workbench/chat | `../shared/src/ui/`, `../workbench/src/`, `../shared/src/chatview/` | ## Local Preview diff --git a/app/desktop/package.json b/app/desktop/package.json index 0b691ec96..8c6868a0a 100644 --- a/app/desktop/package.json +++ b/app/desktop/package.json @@ -36,6 +36,7 @@ }, "dependencies": { "@agenthub/shared": "workspace:*", + "@agenthub/workbench": "workspace:*", "@lobehub/icons": "^5.10.0", "@tanstack/react-query": "^5.101.4", "@tauri-apps/api": "^2.11.1", diff --git a/app/desktop/src/App.tsx b/app/desktop/src/App.tsx index 039c82d1d..abcfd815f 100644 --- a/app/desktop/src/App.tsx +++ b/app/desktop/src/App.tsx @@ -13,7 +13,7 @@ import { import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import type { UnreadDividerDescriptor } from '@shared/chatview'; import { toggleAppliedAgentHubTheme } from '@shared/theme'; -import { AgentHubWorkbench } from '@shared/workbench'; +import { AgentHubWorkbench } from '@agenthub/workbench'; import { resolveCurrentTranscriptRunId } from '@shared/transcript'; import { getAgentActivityStore } from '@shared/transcript/agentActivity'; import type { ApprovalDecisionAction } from '@shared/transcript'; @@ -46,7 +46,7 @@ import { hubAgentProfileToWorkbenchAgent, } from '@/api/agentProfileQueries'; import { getHubClient } from '@/api/hubQueries'; -import type { AgentConfig, ConnectionStatusKind, DocRow, SkillMarketItem, MCPMarketItem } from '@shared/workbench'; +import type { AgentConfig, ConnectionStatusKind, DocRow, SkillMarketItem, MCPMarketItem } from '@agenthub/workbench'; import { getDemoRuntimeEvidence } from '@/demo/demoEvidence'; import { useToastStore, ToastContainer } from '@shared/ui/toast'; import { useGlobalKeyboardShortcuts } from '@/hooks/useGlobalKeyboardShortcuts'; diff --git a/app/desktop/src/api/agentProfileQueries.ts b/app/desktop/src/api/agentProfileQueries.ts index 88569f8f6..4619d5c48 100644 --- a/app/desktop/src/api/agentProfileQueries.ts +++ b/app/desktop/src/api/agentProfileQueries.ts @@ -1,6 +1,6 @@ // React Query hooks for Edge Agent Profiles — CRUD operations. import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import type { AgentConfig } from '@shared/workbench'; +import type { AgentConfig } from '@agenthub/workbench'; import { fetchAgentProfiles, createAgentProfile, diff --git a/app/desktop/src/api/documentQueries.ts b/app/desktop/src/api/documentQueries.ts index 3c31da302..773b153b0 100644 --- a/app/desktop/src/api/documentQueries.ts +++ b/app/desktop/src/api/documentQueries.ts @@ -4,7 +4,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { createHubClient, type HubDocumentListItem } from '@/api/hubClient'; import { getAccessToken } from '@/hooks/useAuth'; -import type { DocRow } from '@shared/workbench/pages'; +import type { DocRow } from '@agenthub/workbench/pages'; // Lazy singleton — avoids creating the client on module load when Hub is not needed. let _hubClient: ReturnType | null = null; diff --git a/app/desktop/src/components/DesktopChrome.tsx b/app/desktop/src/components/DesktopChrome.tsx index 421a294f9..56fa8b428 100644 --- a/app/desktop/src/components/DesktopChrome.tsx +++ b/app/desktop/src/components/DesktopChrome.tsx @@ -1,11 +1,11 @@ import type { ReactNode } from 'react'; import { getCurrentWindow } from '@tauri-apps/api/window'; -import { DesignNavIcon } from '@shared/workbench/designIcons'; +import { DesignNavIcon } from '@agenthub/workbench/designIcons'; import { DESKTOP_NAVIGATE_BACK_EVENT, DESKTOP_NAVIGATE_FORWARD_EVENT, DESKTOP_TOGGLE_SIDEBAR_EVENT, -} from '@shared/workbench/desktopChromeEvents'; +} from '@agenthub/workbench/desktopChromeEvents'; import styles from './DesktopChrome.module.css'; type WindowCommand = 'minimize' | 'toggleMaximize' | 'close'; diff --git a/app/desktop/src/components/DesktopEntryGate.tsx b/app/desktop/src/components/DesktopEntryGate.tsx index 4d8469301..5ea983f8a 100644 --- a/app/desktop/src/components/DesktopEntryGate.tsx +++ b/app/desktop/src/components/DesktopEntryGate.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { DesignNavIcon } from '@shared/workbench'; +import { DesignNavIcon } from '@agenthub/workbench'; import { useAuth } from '@/hooks/useAuth'; import { OidcError } from '@/api/hubAuth'; import agentHubLogo from '@/assets/agenthub-product-icon-rounded.svg'; diff --git a/app/desktop/src/platform/desktopRuntimeSessions.ts b/app/desktop/src/platform/desktopRuntimeSessions.ts index ea73ebae2..ed42653cf 100644 --- a/app/desktop/src/platform/desktopRuntimeSessions.ts +++ b/app/desktop/src/platform/desktopRuntimeSessions.ts @@ -1,4 +1,4 @@ -import type { RuntimeSessionImportItem } from '@shared/workbench'; +import type { RuntimeSessionImportItem } from '@agenthub/workbench'; /** * Desktop host: Edge GET /v1/runtime-sessions via typed fetch. diff --git a/app/desktop/src/platform/desktopWorkbenchProjectsPort.ts b/app/desktop/src/platform/desktopWorkbenchProjectsPort.ts index 3c78af4bb..444a83624 100644 --- a/app/desktop/src/platform/desktopWorkbenchProjectsPort.ts +++ b/app/desktop/src/platform/desktopWorkbenchProjectsPort.ts @@ -1,8 +1,8 @@ import type { WorkbenchProjectsPage, WorkbenchProjectsPort, -} from '@shared/workbench'; -import { workspaceProjectToProjectInfo } from '@shared/workbench/hubDataMapping'; +} from '@agenthub/workbench'; +import { workspaceProjectToProjectInfo } from '@agenthub/workbench/hubDataMapping'; import { getHubClient } from '@/api/hubQueries'; /** diff --git a/app/desktop/src/platform/useDesktopWorkbenchModel.ts b/app/desktop/src/platform/useDesktopWorkbenchModel.ts index 69d30d616..92d8851c1 100644 --- a/app/desktop/src/platform/useDesktopWorkbenchModel.ts +++ b/app/desktop/src/platform/useDesktopWorkbenchModel.ts @@ -16,15 +16,15 @@ import { getAgentActivityStore, type AgentActivitySnapshot } from '@shared/trans import { computeTranscriptUnreadMarker, type TranscriptUnreadMarker } from '@/components/IM/transcriptUnreadMarker'; import type { WorkbenchAgent, WorkbenchConversation } from '@shared/platform'; import type { ThreadInfo, ThreadItemInfo, ThreadPinInfo } from '@shared/types'; -import type { ProjectDraft, ProjectInfo } from '@shared/workbench'; -import type { WorkbenchContactsData } from '@shared/workbench'; -import type { WorkbenchContactsActions } from '@shared/workbench/WorkbenchRoutes'; +import type { ProjectDraft, ProjectInfo } from '@agenthub/workbench'; +import type { WorkbenchContactsData } from '@agenthub/workbench'; +import type { WorkbenchContactsActions } from '@agenthub/workbench/WorkbenchRoutes'; import { resolveHubContacts, resolveHubProjects, hubSessionToConversation, type HubContactLike, -} from '@shared/workbench/hubDataMapping'; +} from '@agenthub/workbench/hubDataMapping'; import { useThreadMessages, useThreadPins, useThreads } from '@/api/threadQueries'; import { useHubSessions, useHubMessages, useHubPinnedMessages, useHubSendMessage, useHubRecallMessage, useHubEditMessage, useHubPinMessage, useHubUnpinMessage, useHubMarkRead } from '@/api/sessionQueries'; import { diff --git a/app/desktop/vitest.config.ts b/app/desktop/vitest.config.ts index 674074dc7..815fd9714 100644 --- a/app/desktop/vitest.config.ts +++ b/app/desktop/vitest.config.ts @@ -21,7 +21,7 @@ export default defineConfig({ test: { globals: true, environment: 'jsdom', - include: ['src/**/*.test.ts', 'src/**/*.test.tsx', '../shared/src/**/*.test.ts', '../shared/src/**/*.test.tsx'], + include: ['src/**/*.test.ts', 'src/**/*.test.tsx', '../shared/src/**/*.test.ts', '../shared/src/**/*.test.tsx', '../workbench/src/**/*.test.ts', '../workbench/src/**/*.test.tsx'], setupFiles: ['./src/__tests__/setup.ts'], execArgv: ['--max-old-space-size=8192'], maxWorkers: 4, diff --git a/app/desktop/vitest.shared-ci.config.ts b/app/desktop/vitest.shared-ci.config.ts index 38ed666d3..c6b2479d9 100644 --- a/app/desktop/vitest.shared-ci.config.ts +++ b/app/desktop/vitest.shared-ci.config.ts @@ -1,6 +1,8 @@ import { defineConfig } from 'vitest/config'; import path from 'path'; +// 兄弟包单测宿主配置(desktop 环境跑 shared + workbench 两套 suite)。 +// 名字叫 shared-ci 是历史原因(#1759 第二阶段起同时承载 workbench)。 export default defineConfig({ resolve: { dedupe: ['react', 'react-dom'], @@ -21,7 +23,12 @@ export default defineConfig({ test: { globals: true, environment: 'jsdom', - include: ['../shared/src/**/*.test.ts', '../shared/src/**/*.test.tsx'], + include: [ + '../shared/src/**/*.test.ts', + '../shared/src/**/*.test.tsx', + '../workbench/src/**/*.test.ts', + '../workbench/src/**/*.test.tsx', + ], setupFiles: ['./src/__tests__/setup.ts'], execArgv: ['--max-old-space-size=4096'], memoryLimit: '512MB', diff --git a/app/eslint.config.mjs b/app/eslint.config.mjs index 6456fa750..bb6809ec0 100644 --- a/app/eslint.config.mjs +++ b/app/eslint.config.mjs @@ -31,6 +31,27 @@ export default tseslint.config( }, }, + // 依赖方向门禁(#1759):shared 是跨端原语层,永远不得 import workbench + // 包(workbench → shared 单向)。相对路径越界由 + // scripts/verify/verify-frontend-package-boundary.py 兜底。 + { + files: ["shared/**/*.{ts,tsx,js,jsx,mjs}"], + rules: { + "no-restricted-imports": [ + "error", + { + patterns: [ + { + group: ["@agenthub/workbench", "@agenthub/workbench/*", "@workbench", "@workbench/*"], + message: + "shared must never import workbench — dependency direction is workbench -> shared only (#1759).", + }, + ], + }, + ], + }, + }, + // React Hooks rules — only for UI packages { files: ["desktop/**/*.{tsx,jsx}", "web/**/*.{tsx,jsx}", "mobile-rn/**/*.{tsx,jsx}"], diff --git a/app/mobile-rn/scripts/verify-boundaries.mjs b/app/mobile-rn/scripts/verify-boundaries.mjs index bf5ee3276..04cb9b636 100644 --- a/app/mobile-rn/scripts/verify-boundaries.mjs +++ b/app/mobile-rn/scripts/verify-boundaries.mjs @@ -40,6 +40,9 @@ const forbiddenImportFragments = [ '.module.css', '@agenthub/shared/ui', '@agenthub/shared/workbench', + // #1759:workbench 独立成包后 Mobile 同样不得依赖(Hub-only, + // bundle 不得包含端级 workbench 巨石)。 + '@agenthub/workbench', ]; const forbiddenRuntimePatterns = [ diff --git a/app/mobile-rn/src/importBoundary.test.ts b/app/mobile-rn/src/importBoundary.test.ts index 44b527d07..5e67790e8 100644 --- a/app/mobile-rn/src/importBoundary.test.ts +++ b/app/mobile-rn/src/importBoundary.test.ts @@ -9,7 +9,7 @@ const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ' const boundaryScript = path.join(projectRoot, 'scripts', 'verify-boundaries.mjs'); describe('Mobile RN import boundary verifier', () => { - it('rejects shared workbench imports and browser storage in runtime source', async () => { + it('rejects workbench package imports and browser storage in runtime source', async () => { const fixtureRoot = await mkdtemp(path.join(tmpdir(), 'agenthub-mobile-boundary-')); try { @@ -18,10 +18,12 @@ describe('Mobile RN import boundary verifier', () => { path.join(fixtureRoot, 'src', 'bad-runtime.ts'), [ ['im', "port { AgentHubWorkbench } from '@agenthub/shared/workbench';"].join(''), + ['im', "port { WorkbenchRoutes } from '@agenthub/workbench';"].join(''), 'export function readUnsafeStorage() {', ` return ${['local', 'Storage'].join('')}.getItem("hub");`, '}', 'void AgentHubWorkbench;', + 'void WorkbenchRoutes;', '', ].join('\n'), 'utf8', @@ -31,6 +33,7 @@ describe('Mobile RN import boundary verifier', () => { expect(result.exitCode).not.toBe(0); expect(result.stderr).toContain('@agenthub/shared/workbench'); + expect(result.stderr).toContain('@agenthub/workbench'); expect(result.stderr).toContain('localStorage'); } finally { await rm(fixtureRoot, { force: true, recursive: true }); diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index bf82ae7ad..fc6a1a293 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -93,7 +93,7 @@ importers: version: 10.4.0 eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0) + version: 2.32.0(eslint@10.4.0) eslint-plugin-react-hooks: specifier: ^7.1.1 version: 7.1.1(eslint@10.4.0) @@ -124,6 +124,9 @@ importers: '@agenthub/shared': specifier: workspace:* version: link:../shared + '@agenthub/workbench': + specifier: workspace:* + version: link:../workbench '@lobehub/icons': specifier: ^5.10.0 version: 5.10.0(@lobehub/ui@5.15.1)(@types/react@19.2.18)(antd@6.4.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -427,6 +430,9 @@ importers: '@agenthub/shared': specifier: workspace:* version: link:../shared + '@agenthub/workbench': + specifier: workspace:* + version: link:../workbench '@lobehub/icons': specifier: ^5.10.0 version: 5.10.0(@lobehub/ui@5.15.1)(@types/react@19.2.18)(antd@6.4.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -504,6 +510,64 @@ importers: specifier: ^4.1.10 version: 4.1.10(@types/node@25.9.1)(@vitest/coverage-istanbul@4.1.10)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1)(vite@6.4.3(@types/node@25.9.1)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) + workbench: + dependencies: + '@agenthub/shared': + specifier: workspace:* + version: link:../shared + '@lobehub/icons': + specifier: 5.10.0 + version: 5.10.0(@lobehub/ui@5.15.1)(@types/react@19.2.18)(antd@6.4.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + virtua: + specifier: ^0.50.1 + version: 0.50.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + devDependencies: + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@testing-library/user-event': + specifier: ^14.6.3 + version: 14.6.3(@testing-library/dom@10.4.1) + '@types/node': + specifier: ^25.9.1 + version: 25.9.1 + '@types/react': + specifier: ^19.2.18 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.2.4 + version: 19.2.4(@types/react@19.2.18) + '@vitest/coverage-v8': + specifier: ^4.1.10 + version: 4.1.10(vitest@4.1.10) + i18next: + specifier: ^26.3.6 + version: 26.3.6(typescript@5.8.3) + jsdom: + specifier: ^29.1.1 + version: 29.1.1 + react: + specifier: ^19.2.8 + version: 19.2.8 + react-dom: + specifier: ^19.2.8 + version: 19.2.8(react@19.2.8) + react-i18next: + specifier: ^17.0.11 + version: 17.0.11(i18next@26.3.6(typescript@5.8.3))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@5.8.3) + typescript: + specifier: ~5.8.0 + version: 5.8.3 + vite: + specifier: ^6.4.3 + version: 6.4.3(@types/node@25.9.1)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vitest: + specifier: ^4.1.10 + version: 4.1.10(@types/node@25.9.1)(@vitest/coverage-istanbul@4.1.10)(@vitest/coverage-v8@4.1.10)(jsdom@29.1.1)(vite@6.4.3(@types/node@25.9.1)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) + packages: '@adobe/css-tools@4.5.0': @@ -2218,12 +2282,6 @@ packages: react: '>=16.9.0' react-dom: '>=16.9.0' - '@rc-component/util@1.11.1': - resolution: {integrity: sha512-awVlI3ub2vqfqkYxOBc/uQ0efm3jw0wcrhtO/YWLyZfxiKXczKwNbVuhlnyxytDt7H9pbbVQiqr+O6MLATtRYg==} - peerDependencies: - react: '>=18.0.0' - react-dom: '>=18.0.0' - '@rc-component/util@1.12.0': resolution: {integrity: sha512-AEjPL8JVdohIITaiXokyjL9WQ6tKWWjAYK9QU16tGNE9JaQABBQy+hA4H2Lup5MgXy9yY3iLrbZJheuU13hTdQ==} peerDependencies: @@ -4167,9 +4225,6 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es-toolkit@1.47.0: - resolution: {integrity: sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw==} - es-toolkit@1.50.0: resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} @@ -7783,7 +7838,7 @@ snapshots: '@babel/runtime': 7.29.7 '@emotion/hash': 0.8.0 '@emotion/unitless': 0.7.5 - '@rc-component/util': 1.11.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@rc-component/util': 1.12.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) clsx: 2.1.1 csstype: 3.2.3 react: 19.2.8 @@ -7875,7 +7930,7 @@ snapshots: '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-compilation-targets@7.29.7': dependencies: @@ -7921,7 +7976,7 @@ snapshots: '@babel/helper-member-expression-to-functions@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -7943,7 +7998,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-plugin-utils@7.28.6': {} @@ -7970,7 +8025,7 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -7984,14 +8039,14 @@ snapshots: dependencies: '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/parser@7.29.3': dependencies: @@ -7999,7 +8054,7 @@ snapshots: '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/parser@7.29.8': dependencies: @@ -8295,17 +8350,17 @@ snapshots: '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@babel/traverse@7.29.0': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -8315,9 +8370,9 @@ snapshots: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -8979,7 +9034,7 @@ snapshots: debug: 4.4.3 expo-modules-autolinking: 56.0.21(typescript@6.0.3) resolve-from: 5.0.0 - semver: 7.8.1 + semver: 7.8.5 transitivePeerDependencies: - supports-color - typescript @@ -9178,7 +9233,7 @@ snapshots: '@lobehub/ui': 5.15.1(@lobehub/fluent-emoji@4.1.0(@types/react@19.2.18)(antd@6.4.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@lobehub/icons@5.10.0)(@shikijs/themes@4.4.2)(@types/mdast@4.0.4)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(antd@6.4.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(micromark-util-types@2.0.2)(micromark@4.0.2)(motion@12.40.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) antd: 6.4.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) antd-style: 4.1.0(@types/react@19.2.18)(antd@6.4.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - es-toolkit: 1.47.0 + es-toolkit: 1.50.0 lucide-react: 0.469.0(react@19.2.8) polished: 4.3.1 react: 19.2.8 @@ -9873,13 +9928,6 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@rc-component/util@1.11.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - is-mobile: 5.0.0 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-is: 18.3.1 - '@rc-component/util@1.12.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: is-mobile: 5.0.0 @@ -9909,7 +9957,7 @@ snapshots: '@react-native/codegen@0.85.3(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 hermes-parser: 0.33.3 invariant: 2.2.4 nullthrows: 1.1.1 @@ -9924,7 +9972,7 @@ snapshots: metro: 0.84.4 metro-config: 0.84.4 metro-core: 0.84.4 - semver: 7.8.1 + semver: 7.8.5 transitivePeerDependencies: - bufferutil - supports-color @@ -10415,7 +10463,7 @@ snapshots: '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.29.7 - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -10483,16 +10531,16 @@ snapshots: '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/chai@5.2.3': dependencies: @@ -10708,10 +10756,10 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@5.8.3))(eslint@10.4.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.4(eslint@10.4.0)(typescript@6.0.3) + '@typescript-eslint/parser': 8.59.4(eslint@10.4.0)(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.59.4 '@typescript-eslint/type-utils': 8.59.4(eslint@10.4.0)(typescript@5.8.3) '@typescript-eslint/utils': 8.59.4(eslint@10.4.0)(typescript@5.8.3) @@ -12202,8 +12250,6 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es-toolkit@1.47.0: {} - es-toolkit@1.50.0: {} esast-util-from-estree@2.0.0: @@ -12274,17 +12320,16 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.4.0): + eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.10)(eslint@10.4.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.59.4(eslint@10.4.0)(typescript@6.0.3) eslint: 10.4.0 eslint-import-resolver-node: 0.3.10 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0): + eslint-plugin-import@2.32.0(eslint@10.4.0): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -12295,7 +12340,7 @@ snapshots: doctrine: 2.1.0 eslint: 10.4.0 eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint@10.4.0) + eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.10)(eslint@10.4.0) hasown: 2.0.3 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -12306,8 +12351,6 @@ snapshots: semver: 6.3.1 string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.59.4(eslint@10.4.0)(typescript@6.0.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -14043,13 +14086,13 @@ snapshots: metro-runtime@0.84.4: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 flow-enums-runtime: 0.0.6 metro-source-map@0.84.4: dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.84.4 @@ -14086,8 +14129,8 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 metro: 0.84.4 metro-babel-transformer: 0.84.4 @@ -14107,10 +14150,10 @@ snapshots: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7 '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 accepts: 2.0.0 ci-info: 2.0.0 connect: 3.7.0 @@ -14541,7 +14584,7 @@ snapshots: dependencies: hosted-git-info: 7.0.2 proc-log: 4.2.0 - semver: 7.8.1 + semver: 7.8.5 validate-npm-package-name: 5.0.1 nth-check@2.1.1: @@ -16107,7 +16150,7 @@ snapshots: typescript-eslint@8.59.4(eslint@10.4.0)(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@6.0.3))(eslint@10.4.0)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.4.0)(typescript@5.8.3))(eslint@10.4.0)(typescript@5.8.3) '@typescript-eslint/parser': 8.59.4(eslint@10.4.0)(typescript@5.8.3) '@typescript-eslint/typescript-estree': 8.59.4(typescript@5.8.3) '@typescript-eslint/utils': 8.59.4(eslint@10.4.0)(typescript@5.8.3) diff --git a/app/pnpm-workspace.yaml b/app/pnpm-workspace.yaml index 43486b18c..418a31d7a 100644 --- a/app/pnpm-workspace.yaml +++ b/app/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - "shared" + - "workbench" - "desktop" - "web" - "mobile-rn" diff --git a/app/shared/README.md b/app/shared/README.md index b50bb5ea5..e28a8c6f7 100644 --- a/app/shared/README.md +++ b/app/shared/README.md @@ -17,7 +17,8 @@ | `@shared/context/*` | `src/context/` | 上下文用量和 token breakdown 工具 | | `@shared/ui` | `src/ui/` | Desktop/Web 共用 UI 组件、测试和 Storybook story | | `@shared/chatview` | `src/chatview/` | ChatView 消息卡片渲染系统:adapter、TranscriptItem、RowItem 状态机、i18n、CSS tokens | -| `@shared/workbench` | `src/workbench/` | v4 共享工作台 shell、状态管理、platform adapter 接口 | + +端级 workbench shell 已于 #1759 独立为第 6 个包 `@agenthub/workbench`(`app/workbench/`),依赖方向固定为 workbench → shared 单向;shared 不再包含也不再 re-export workbench。 ## 使用 diff --git a/app/shared/package.json b/app/shared/package.json index 174399004..62d398618 100644 --- a/app/shared/package.json +++ b/app/shared/package.json @@ -21,10 +21,8 @@ "./notificationIntents": "./src/notificationIntents.ts", "./platform": "./src/platform/index.ts", "./transcript": "./src/transcript/index.ts", - "./workbench": "./src/workbench/index.ts", "./agentSpec": "./src/agentSpec.ts", "./surfaceMetadata": "./src/surfaceMetadata.ts", - "./workbenchState": "./src/workbenchState.ts", "./tree": "./src/tree.ts", "./diff": "./src/diff.ts", "./eventClient": "./src/eventClient.ts", diff --git a/app/shared/src/__tests__/setup.ts b/app/shared/src/__tests__/setup.ts index c6ec134f9..d0e99aef7 100644 --- a/app/shared/src/__tests__/setup.ts +++ b/app/shared/src/__tests__/setup.ts @@ -1,5 +1,5 @@ import '@testing-library/jest-dom/vitest'; -import { beforeAll, vi } from 'vitest'; +import { vi } from 'vitest'; // Shared test i18next instance (Issue #1717): real chatview + sharedWorkbench // resources, isolated per project via i18next.createInstance(). Registered @@ -38,39 +38,3 @@ installJsdomPolyfills(); // Re-export testing utilities so shared component tests import from one place. export { render, screen, fireEvent, waitFor, within } from '@testing-library/react'; export { default as userEvent } from '@testing-library/user-event'; - -/* ────────────────────────────────────────────────────────────── - Synchronous React.lazy preload for tests - - WorkbenchRoutes wraps every page component in React.lazy() + - Suspense. In jsdom lazy imports resolve asynchronously which - causes flaky "element not found" failures when tests navigate - between rail pages and immediately query page content. - - Pre-resolving all lazy pages ensures the import Promise is - fulfilled before any test renders a Suspense boundary. - ────────────────────────────────────────────────────────────── */ - -const WORKBENCH_PRELOAD_TIMEOUT_MS = 5_000; - -async function preloadWorkbenchPage(load: () => Promise): Promise { - let timeout: ReturnType | undefined; - await Promise.race([ - load(), - new Promise((resolve) => { - timeout = setTimeout(resolve, WORKBENCH_PRELOAD_TIMEOUT_MS); - }), - ]); - if (timeout) clearTimeout(timeout); -} - -beforeAll(async () => { - await Promise.allSettled([ - preloadWorkbenchPage(() => import('../workbench/pages/ProjectsPage')), - preloadWorkbenchPage(() => import('../workbench/pages/ContactsPage')), - preloadWorkbenchPage(() => import('../workbench/pages/DocsPage')), - preloadWorkbenchPage(() => import('../workbench/pages/AgentsPage')), - preloadWorkbenchPage(() => import('../workbench/pages/TasksPage')), - preloadWorkbenchPage(() => import('../workbench/pages/SettingsPage')), - ]); -}, 30_000); diff --git a/app/shared/src/index.ts b/app/shared/src/index.ts index 826fe5e45..80034ef70 100644 --- a/app/shared/src/index.ts +++ b/app/shared/src/index.ts @@ -252,32 +252,6 @@ export type { StreamHandle, } from './eventClient'; -export { - createWorkbenchState, - initialWorkbenchState, - workbenchReducer, -} from './workbench/workbenchState'; -export type { - WorkbenchAction, - WorkbenchConnectionStatus, - WorkbenchSnapshot, - WorkbenchState, -} from './workbench/workbenchState'; -export { - getWorkbenchCatalogState, - getWorkbenchDataMode, - getWorkbenchSectionSource, - workbenchDataModeLabels, - workbenchDataModeTones, -} from './workbench/workbenchDataMode'; -export type { - WorkbenchCatalogState, - WorkbenchCatalogTone, - WorkbenchCatalogMode, - WorkbenchSectionSource, - WorkbenchSectionSourceInput, -} from './workbench/workbenchDataMode'; - export { SURFACE_METADATA, SURFACE_STATUS_METADATA, @@ -403,20 +377,3 @@ export type { TranscriptAuthorRole, TranscriptBlock, } from './transcript'; - -export { - AgentHubWorkbench, - ConversationSidebar, - GlobalRail, - RightInspector, - UnifiedComposer, - WorkspaceHeader, - agentConfigToAgentSpecFixture, -} from './workbench'; -export type { - AgentHubWorkbenchProps, - ConversationSidebarProps, - RightInspectorProps, - UnifiedComposerProps, - WorkspaceHeaderProps, -} from './workbench'; diff --git a/app/shared/src/testing/lobehubIcons.ts b/app/shared/src/testing/lobehubIcons.ts index e21bf5094..dd283c628 100644 --- a/app/shared/src/testing/lobehubIcons.ts +++ b/app/shared/src/testing/lobehubIcons.ts @@ -56,7 +56,7 @@ export const lobehubIconsMock = { /** * Mock for the deep Antigravity color-icon module (imported by - * `shared/src/workbench/designIcons.tsx`). The real module chain pulls in + * `workbench/src/designIcons.tsx`). The real module chain pulls in * assets Node cannot process in a jsdom test run. */ export const antigravityColorIconMock = { default: NullLobehubIcon }; diff --git a/app/shared/vitest.config.ts b/app/shared/vitest.config.ts index 631ca3df4..2cf25fe0c 100644 --- a/app/shared/vitest.config.ts +++ b/app/shared/vitest.config.ts @@ -41,14 +41,12 @@ export default defineConfig({ // events.ts — 事件名常量(无逻辑) // mock.ts — 测试 mock 工具(不参与被测逻辑) // errors.ts — 错误类型定义 - // workbench/workbenchProjectsPort.ts — 纯类型契约(#1546),零运行时语句 exclude: [ 'src/index.ts', 'src/types.ts', 'src/events.ts', 'src/mock.ts', 'src/errors.ts', - 'src/workbench/workbenchProjectsPort.ts', ], }), }, diff --git a/app/vitest.config.ts b/app/vitest.config.ts index 1caf05fcf..818d50c8a 100644 --- a/app/vitest.config.ts +++ b/app/vitest.config.ts @@ -14,20 +14,25 @@ export default defineConfig({ root: projectRoot('shared'), test: { name: 'shared', sequence: { groupOrder: 1 } }, }, + { + extends: 'workbench/vitest.config.ts', + root: projectRoot('workbench'), + test: { name: 'workbench', sequence: { groupOrder: 2 } }, + }, { extends: 'desktop/vitest.desktop-ci.config.ts', root: projectRoot('desktop'), - test: { name: 'desktop', sequence: { groupOrder: 2 } }, + test: { name: 'desktop', sequence: { groupOrder: 3 } }, }, { extends: 'web/vitest.config.ts', root: projectRoot('web'), - test: { name: 'web', sequence: { groupOrder: 3 } }, + test: { name: 'web', sequence: { groupOrder: 4 } }, }, { extends: 'mobile-rn/vitest.config.ts', root: projectRoot('mobile-rn'), - test: { name: 'mobile-rn', sequence: { groupOrder: 4 } }, + test: { name: 'mobile-rn', sequence: { groupOrder: 5 } }, }, ], }, diff --git a/app/web/README.md b/app/web/README.md index 03928be3d..c031a7821 100644 --- a/app/web/README.md +++ b/app/web/README.md @@ -25,7 +25,7 @@ Web UI -> Hub Server -> Edge relay / sync -> Edge Server -> Agent Runtime adapte | Shell slots | `src/viewRegistryConfig.ts`, `src/views/viewRegistry.tsx` | | Hub client | `src/api/hubClient.ts`, `src/api/` | | i18n | `src/i18n/`, `src/i18n/README.md` | -| Shared UI/workbench/chat | `../shared/src/ui/`, `../shared/src/workbench/`, `../shared/src/chatview/` | +| Shared UI/workbench/chat | `../shared/src/ui/`, `../workbench/src/`, `../shared/src/chatview/` | | Legacy route bridge | `src/router.tsx`, `src/pages/*` -> `App` | Shared components must come from `app/shared/`; do not copy Desktop/shared UI locally in Web. diff --git a/app/web/package.json b/app/web/package.json index b2698819d..f02e2ed29 100644 --- a/app/web/package.json +++ b/app/web/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "@agenthub/shared": "workspace:*", + "@agenthub/workbench": "workspace:*", "@lobehub/icons": "^5.10.0", "@tanstack/react-query": "^5.101.4", "i18next": "^26.3.6", diff --git a/app/web/src/App.test.tsx b/app/web/src/App.test.tsx index b569ff3e9..723a6ca7a 100644 --- a/app/web/src/App.test.tsx +++ b/app/web/src/App.test.tsx @@ -7,11 +7,11 @@ import { queryClient } from '@/api/queryClient'; import { useHubStore } from '@/stores/hubStore'; /** - * These tests render the REAL shared v4 workbench (`@shared/workbench`). + * These tests render the REAL shared v4 workbench (`@agenthub/workbench`). * Only the data/network layer below App is mocked — Hub queries, the web * workbench projection hook, web auth, and the auth modal. * - * Previously this file replaced `@shared/workbench` with a ~50-line hand-written + * Previously this file replaced `@agenthub/workbench` with a ~50-line hand-written * stub and then asserted against the DOM that stub itself rendered, which made * every assertion vacuously true against the real shell. Keep the mock surface * at the data layer; do not stub the workbench. diff --git a/app/web/src/App.tsx b/app/web/src/App.tsx index 119b9ebb1..cbdba3f8d 100644 --- a/app/web/src/App.tsx +++ b/app/web/src/App.tsx @@ -1,9 +1,9 @@ import { QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback, useMemo, useState, useSyncExternalStore } from 'react'; import { useTranslation } from 'react-i18next'; -import { AgentHubWorkbench } from '@shared/workbench'; +import { AgentHubWorkbench } from '@agenthub/workbench'; import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; -import type { AgentConfig, ProjectDraft, ProjectInfo, SkillMarketItem, MCPMarketItem } from '@shared/workbench'; +import type { AgentConfig, ProjectDraft, ProjectInfo, SkillMarketItem, MCPMarketItem } from '@agenthub/workbench'; import { allowsWorkbenchDemoRuntimeMutation, getWorkbenchDataModeContract, diff --git a/app/web/src/api/agentQueries.ts b/app/web/src/api/agentQueries.ts index a2a193af3..5165a6ecd 100644 --- a/app/web/src/api/agentQueries.ts +++ b/app/web/src/api/agentQueries.ts @@ -5,7 +5,7 @@ import { getAccessToken } from '@/hooks/useAuth'; import { useHubStore } from '@/stores/hubStore'; import { hubQueryKeys } from '@shared/stores/queryKeys'; import type { AgentCapabilities, AgentInfo, ListResponse } from '@shared/types'; -import type { AgentConfig } from '@shared/workbench'; +import type { AgentConfig } from '@agenthub/workbench'; const hubClient = createHubClient({ getToken: getAccessToken }); diff --git a/app/web/src/platform/useWebWorkbenchModel.ts b/app/web/src/platform/useWebWorkbenchModel.ts index a38013311..062c08713 100644 --- a/app/web/src/platform/useWebWorkbenchModel.ts +++ b/app/web/src/platform/useWebWorkbenchModel.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; -import type { ProjectDraft } from '@shared/workbench'; +import type { ProjectDraft } from '@agenthub/workbench'; import { getWorkbenchDataModeContract, getWorkbenchDataModeOverrideSnapshot, @@ -12,7 +12,7 @@ import { import { resolveHubContacts, type HubContactLike, -} from '@shared/workbench/hubDataMapping'; +} from '@agenthub/workbench/hubDataMapping'; import { getAgentActivityStore, getPinMapStore, @@ -712,6 +712,6 @@ export { type WebExecutionTargetStatus, type WebExecutionTargetStatusState, } from './webWorkbenchExecutionTargets'; -export { hubEmptyContacts as webHubEmptyContacts } from '@shared/workbench/hubDataMapping'; -export { contactInfoToMember } from '@shared/workbench/hubDataMapping'; -export { resolveHubContacts as resolveWebWorkbenchContacts } from '@shared/workbench/hubDataMapping'; +export { hubEmptyContacts as webHubEmptyContacts } from '@agenthub/workbench/hubDataMapping'; +export { contactInfoToMember } from '@agenthub/workbench/hubDataMapping'; +export { resolveHubContacts as resolveWebWorkbenchContacts } from '@agenthub/workbench/hubDataMapping'; diff --git a/app/web/src/platform/webHubRealtime.test.ts b/app/web/src/platform/webHubRealtime.test.ts index 536e684ee..d48f722f6 100644 --- a/app/web/src/platform/webHubRealtime.test.ts +++ b/app/web/src/platform/webHubRealtime.test.ts @@ -2,7 +2,7 @@ import { createElement, type ReactNode } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { HUB_EVENTS } from '@shared/hubEvents'; import { getAgentActivityStore, getPinMapStore, type HubRuntimeEventTranscriptInput } from '@shared/transcript'; -import { getMessageDelegationStore, getSubagentStreamStore } from '@shared/workbench'; +import { getMessageDelegationStore, getSubagentStreamStore } from '@agenthub/workbench'; import { act, renderHook, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createHubWS, type HubWSHandle, type HubWSOptions } from '@shared/hub/hubWS'; diff --git a/app/web/src/platform/webHubRealtime.ts b/app/web/src/platform/webHubRealtime.ts index d731c63ab..03951a235 100644 --- a/app/web/src/platform/webHubRealtime.ts +++ b/app/web/src/platform/webHubRealtime.ts @@ -5,7 +5,7 @@ import { HUB_EVENTS } from '@shared/hubEvents'; import { hubRuntimeEventFromPayload, type HubRuntimeEventTranscriptInput } from '@shared/transcript'; import { getPinMapStore } from '@shared/transcript'; import { getAgentActivityStore } from '@shared/transcript/agentActivity'; -import { getMessageDelegationStore, getSubagentStreamStore } from '@shared/workbench'; +import { getMessageDelegationStore, getSubagentStreamStore } from '@agenthub/workbench'; import { handleIncomingTyping } from '@shared/chatview/typingPresence'; import { buildWSAuthProtocols, createHubWS, type HubWSHandle, type HubWSOptions } from '@shared/hub/hubWS'; import { WebSocketTransport, type Transport, type TransportStatus } from '@/api/transport'; diff --git a/app/web/src/platform/webWorkbenchProjects.ts b/app/web/src/platform/webWorkbenchProjects.ts index 0fc38deca..3ce8cab05 100644 --- a/app/web/src/platform/webWorkbenchProjects.ts +++ b/app/web/src/platform/webWorkbenchProjects.ts @@ -1,4 +1,4 @@ -import type { ProjectDraft, ProjectInfo } from '@shared/workbench'; +import type { ProjectDraft, ProjectInfo } from '@agenthub/workbench'; import type { WorkbenchDataMode } from '@shared/demo'; import { getWorkbenchDataModeContract, diff --git a/app/web/src/platform/webWorkbenchProjectsPort.ts b/app/web/src/platform/webWorkbenchProjectsPort.ts index a7cfb78fa..2e844331b 100644 --- a/app/web/src/platform/webWorkbenchProjectsPort.ts +++ b/app/web/src/platform/webWorkbenchProjectsPort.ts @@ -1,8 +1,8 @@ import type { WorkbenchProjectsPage, WorkbenchProjectsPort, -} from '@shared/workbench'; -import { workspaceProjectToProjectInfo } from '@shared/workbench/hubDataMapping'; +} from '@agenthub/workbench'; +import { workspaceProjectToProjectInfo } from '@agenthub/workbench/hubDataMapping'; import { createHubClient } from '@/api/hubClient'; import { getAccessToken } from '@/hooks/useAuth'; import { projectDraftToHubRequest } from './webWorkbenchProjects'; diff --git a/app/web/src/platform/webWorkbenchTranscript.ts b/app/web/src/platform/webWorkbenchTranscript.ts index dc35339cd..d5d943a30 100644 --- a/app/web/src/platform/webWorkbenchTranscript.ts +++ b/app/web/src/platform/webWorkbenchTranscript.ts @@ -1,4 +1,4 @@ -import type { RuntimeEvidenceSnapshot } from '@shared/workbench'; +import type { RuntimeEvidenceSnapshot } from '@agenthub/workbench'; import { WORKBENCH_DEMO_FALLBACK_CONVERSATION_ID, isWorkbenchRealDataMode, diff --git a/app/web/src/stores/connectionStore.ts b/app/web/src/stores/connectionStore.ts index 71172ee8c..bf14dee86 100644 --- a/app/web/src/stores/connectionStore.ts +++ b/app/web/src/stores/connectionStore.ts @@ -2,7 +2,7 @@ // 参考: OpCode agentStore caching + Kanna connection tracking import { create } from 'zustand'; import { subscribeWithSelector } from 'zustand/middleware'; -import type { ConnectionStatusKind } from '@shared/workbench'; +import type { ConnectionStatusKind } from '@agenthub/workbench'; import type { HealthResponse } from '@shared/types'; /** Recovery state for stream recovery after WebSocket reconnection. */ diff --git a/app/workbench/README.md b/app/workbench/README.md new file mode 100644 index 000000000..d5b62f2c2 --- /dev/null +++ b/app/workbench/README.md @@ -0,0 +1,32 @@ +# @agenthub/workbench + +端级工作台(workbench shell):`AgentHubWorkbench` 主壳、GlobalRail/会话侧栏/ +右侧 Inspector、UnifiedComposer、WorkbenchRoutes 页面路由、agents/contacts/ +docs/projects/tasks/settings 页面、mainchain 状态条、terminal host、floating +组件与 team subagent stream 等。 + +由 `app/shared/src/workbench/` 独立而来(#1759):`shared` 是跨端原语层, +workbench 是端级巨石,两者拆包后依赖方向固定为 **workbench → shared 单向**。 + +## 边界 + +- 依赖方向:`workbench` 可以 import `shared`(深导入走 `@shared/*` 别名, + 与 web/desktop 约定一致);**shared 永远不得 import workbench** + (`scripts/verify/verify-frontend-package-boundary.py` + eslint + `no-restricted-imports` 双重机器门禁)。 +- 消费者:web / desktop 从 `@agenthub/workbench`(及显式子路径 + `./hubDataMapping`、`./pages`、`./designIcons`、`./desktopChromeEvents`、 + `./WorkbenchRoutes`)导入。 +- mobile-rn 不依赖本包(Hub-only,boundary verifier 禁止 workbench 导入)。 + +## 常用命令 + +```bash +pnpm --filter @agenthub/workbench test # vitest 全量 +pnpm --filter @agenthub/workbench test:coverage # 覆盖率(门禁同源阈值) +pnpm --filter @agenthub/workbench typecheck # tsc --noEmit +``` + +图标治理:workbench 内图标一律走 `designIcons.tsx` registry +(`icon-governance.test.ts` 机器把关,禁止直连 lucide-react/@lobehub/icons +或裸 ``)。 diff --git a/app/workbench/package.json b/app/workbench/package.json new file mode 100644 index 000000000..355fce55b --- /dev/null +++ b/app/workbench/package.json @@ -0,0 +1,50 @@ +{ + "name": "@agenthub/workbench", + "version": "0.5.0", + "private": true, + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": "./src/index.ts", + "./hubDataMapping": "./src/hubDataMapping.ts", + "./pages": "./src/pages/index.ts", + "./designIcons": "./src/designIcons.tsx", + "./desktopChromeEvents": "./src/desktopChromeEvents.ts", + "./WorkbenchRoutes": "./src/WorkbenchRoutes.tsx" + }, + "scripts": { + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "typecheck": "tsc --noEmit", + "lint": "tsc --noEmit" + }, + "peerDependencies": { + "react": ">=19.2.7", + "react-dom": ">=19.2.7", + "react-i18next": ">=15.0.0" + }, + "dependencies": { + "@agenthub/shared": "workspace:*", + "@lobehub/icons": "5.10.0", + "virtua": "^0.50.1" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.3", + "@types/node": "^25.9.1", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", + "@vitest/coverage-v8": "^4.1.10", + "i18next": "^26.3.6", + "jsdom": "^29.1.1", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-i18next": "^17.0.11", + "typescript": "~5.8.0", + "vite": "^6.4.3", + "vitest": "^4.1.10" + } +} diff --git a/app/shared/src/workbench/AgentHubWorkbench.module.css b/app/workbench/src/AgentHubWorkbench.module.css similarity index 100% rename from app/shared/src/workbench/AgentHubWorkbench.module.css rename to app/workbench/src/AgentHubWorkbench.module.css diff --git a/app/shared/src/workbench/AgentHubWorkbench.module.test.ts b/app/workbench/src/AgentHubWorkbench.module.test.ts similarity index 100% rename from app/shared/src/workbench/AgentHubWorkbench.module.test.ts rename to app/workbench/src/AgentHubWorkbench.module.test.ts diff --git a/app/shared/src/workbench/AgentHubWorkbench.tsx b/app/workbench/src/AgentHubWorkbench.tsx similarity index 97% rename from app/shared/src/workbench/AgentHubWorkbench.tsx rename to app/workbench/src/AgentHubWorkbench.tsx index e75457497..497211467 100644 --- a/app/shared/src/workbench/AgentHubWorkbench.tsx +++ b/app/workbench/src/AgentHubWorkbench.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; -import Modal from '../ui/Modal'; -import { getResolvedShortcutGroups } from '../utils/keyboardShortcuts'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import Modal from '@shared/ui/Modal'; +import { getResolvedShortcutGroups } from '@shared/utils/keyboardShortcuts'; import type { GlobalRailPage } from './GlobalRail'; import styles from './AgentHubWorkbench.module.css'; import { useWorkbenchPanelLayout } from './useWorkbenchPanelLayout'; diff --git a/app/shared/src/workbench/AgentHubWorkbenchHelpers.test.ts b/app/workbench/src/AgentHubWorkbenchHelpers.test.ts similarity index 99% rename from app/shared/src/workbench/AgentHubWorkbenchHelpers.test.ts rename to app/workbench/src/AgentHubWorkbenchHelpers.test.ts index 0f04d9b36..7f2d85ac9 100644 --- a/app/shared/src/workbench/AgentHubWorkbenchHelpers.test.ts +++ b/app/workbench/src/AgentHubWorkbenchHelpers.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import type { AgentHubPlatform, WorkbenchConversation } from '../platform'; -import type { TranscriptBlock } from '../transcript'; +import type { AgentHubPlatform, WorkbenchConversation } from '@shared/platform'; +import type { TranscriptBlock } from '@shared/transcript'; import type { GlobalRailPage } from './GlobalRail'; import type { WorkbenchSessionChrome } from './workbenchSessionChromeHelpers'; import type { WorkbenchTranscriptChrome } from './useWorkbenchTranscriptChrome'; diff --git a/app/shared/src/workbench/AgentHubWorkbenchHelpers.ts b/app/workbench/src/AgentHubWorkbenchHelpers.ts similarity index 99% rename from app/shared/src/workbench/AgentHubWorkbenchHelpers.ts rename to app/workbench/src/AgentHubWorkbenchHelpers.ts index bcec3ccc0..57844d113 100644 --- a/app/shared/src/workbench/AgentHubWorkbenchHelpers.ts +++ b/app/workbench/src/AgentHubWorkbenchHelpers.ts @@ -1,5 +1,5 @@ import type { Dispatch, SetStateAction } from 'react'; -import type { WorkbenchConversation } from '../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import type { GlobalRailPage } from './GlobalRail'; import type { WorkbenchFrameProps } from './workbenchFrameTypes'; import type { UseWorkbenchSessionChromeOptions, WorkbenchSessionChrome } from './workbenchSessionChromeHelpers'; diff --git a/app/shared/src/workbench/AgentHubWorkbenchTypes.ts b/app/workbench/src/AgentHubWorkbenchTypes.ts similarity index 97% rename from app/shared/src/workbench/AgentHubWorkbenchTypes.ts rename to app/workbench/src/AgentHubWorkbenchTypes.ts index 34759a66e..209520067 100644 --- a/app/shared/src/workbench/AgentHubWorkbenchTypes.ts +++ b/app/workbench/src/AgentHubWorkbenchTypes.ts @@ -2,8 +2,8 @@ import type { AgentHubPlatform, WorkbenchAgent, WorkbenchConversation, -} from '../platform'; -import type { TranscriptBlock, ApprovalDecisionAction } from '../transcript'; +} from '@shared/platform'; +import type { TranscriptBlock, ApprovalDecisionAction } from '@shared/transcript'; import type { ConnectionStatusKind } from './GlobalRail'; import type { RuntimeEvidenceSnapshot } from './RightInspector'; import type { @@ -108,7 +108,7 @@ export interface AgentHubWorkbenchProps { * watermark (unread_count = next_seq − last_read_seq); absent for execution * threads, demos, and the web app. */ - transcriptUnreadDivider?: import('../chatview').UnreadDividerDescriptor | undefined; + transcriptUnreadDivider?: import('@shared/chatview').UnreadDividerDescriptor | undefined; /** Current user profile info, shown in GlobalRail avatar and profile popover. */ userDisplayName?: string | undefined; userAvatarUrl?: string | undefined; diff --git a/app/shared/src/workbench/ChatEngineeringColumn.module.css b/app/workbench/src/ChatEngineeringColumn.module.css similarity index 100% rename from app/shared/src/workbench/ChatEngineeringColumn.module.css rename to app/workbench/src/ChatEngineeringColumn.module.css diff --git a/app/shared/src/workbench/ChatEngineeringColumn.tsx b/app/workbench/src/ChatEngineeringColumn.tsx similarity index 99% rename from app/shared/src/workbench/ChatEngineeringColumn.tsx rename to app/workbench/src/ChatEngineeringColumn.tsx index 475a27758..cc21e5852 100644 --- a/app/shared/src/workbench/ChatEngineeringColumn.tsx +++ b/app/workbench/src/ChatEngineeringColumn.tsx @@ -4,7 +4,7 @@ import type { WorkspaceFileEntry, WorkspaceGitChange, WorkspaceGitCommit, -} from '../platform'; +} from '@shared/platform'; import { AuxPanel, resolveAvailableAuxTabs, diff --git a/app/shared/src/workbench/ChatViewBridge.tsx b/app/workbench/src/ChatViewBridge.tsx similarity index 93% rename from app/shared/src/workbench/ChatViewBridge.tsx rename to app/workbench/src/ChatViewBridge.tsx index 712297f71..3bb64439e 100644 --- a/app/shared/src/workbench/ChatViewBridge.tsx +++ b/app/workbench/src/ChatViewBridge.tsx @@ -7,14 +7,14 @@ import React, { useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import type { TranscriptBlock } from '../transcript'; -import type { WorkbenchConversation } from '../platform'; +import type { TranscriptBlock } from '@shared/transcript'; +import type { WorkbenchConversation } from '@shared/platform'; import type { ConnectionStatusKind } from './GlobalRail'; -import { ChatViewTranscript } from '../chatview/components/ChatViewTranscript'; -import type { TranscriptUserItem } from '../chatview/transcript-item'; -import type { UnreadDividerDescriptor } from '../chatview'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; -import { useTypingPresence } from '../chatview/typingPresence'; +import { ChatViewTranscript } from '@shared/chatview/components/ChatViewTranscript'; +import type { TranscriptUserItem } from '@shared/chatview/transcript-item'; +import type { UnreadDividerDescriptor } from '@shared/chatview'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { useTypingPresence } from '@shared/chatview/typingPresence'; import { SubagentStreamOverlay } from './team/SubagentStreamOverlay'; import { InlineDelegationCard } from './team/InlineDelegationCard'; diff --git a/app/shared/src/workbench/ComposerAttachmentParts.test.tsx b/app/workbench/src/ComposerAttachmentParts.test.tsx similarity index 97% rename from app/shared/src/workbench/ComposerAttachmentParts.test.tsx rename to app/workbench/src/ComposerAttachmentParts.test.tsx index 25cd778dc..77c510893 100644 --- a/app/shared/src/workbench/ComposerAttachmentParts.test.tsx +++ b/app/workbench/src/ComposerAttachmentParts.test.tsx @@ -1,12 +1,12 @@ import '@testing-library/jest-dom/vitest'; import { fireEvent, render, screen } from '@testing-library/react'; import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; -import type { ComposerAttachment } from '../composer'; +import type { ComposerAttachment } from '@shared/composer'; import { ComposerAttachmentChip } from './ComposerAttachmentParts'; // These assertions use the en chatview literals; opt into the en bundle of // the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('en'); diff --git a/app/shared/src/workbench/ComposerAttachmentParts.tsx b/app/workbench/src/ComposerAttachmentParts.tsx similarity index 96% rename from app/shared/src/workbench/ComposerAttachmentParts.tsx rename to app/workbench/src/ComposerAttachmentParts.tsx index 923b28b6f..59a952b99 100644 --- a/app/shared/src/workbench/ComposerAttachmentParts.tsx +++ b/app/workbench/src/ComposerAttachmentParts.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { ComposerAttachment } from '../composer'; -import { CodePreviewCard } from '../ui/CodePreviewCard'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import type { ComposerAttachment } from '@shared/composer'; +import { CodePreviewCard } from '@shared/ui/CodePreviewCard'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import styles from './AgentHubWorkbench.module.css'; import { buildAttachmentChipViewModel, diff --git a/app/shared/src/workbench/ComposerContextParts.test.tsx b/app/workbench/src/ComposerContextParts.test.tsx similarity index 97% rename from app/shared/src/workbench/ComposerContextParts.test.tsx rename to app/workbench/src/ComposerContextParts.test.tsx index 777e4f000..0b66f3efc 100644 --- a/app/shared/src/workbench/ComposerContextParts.test.tsx +++ b/app/workbench/src/ComposerContextParts.test.tsx @@ -11,7 +11,7 @@ import { // These assertions use the en chatview literals; opt into the en bundle of // the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('en'); diff --git a/app/shared/src/workbench/ComposerContextParts.tsx b/app/workbench/src/ComposerContextParts.tsx similarity index 97% rename from app/shared/src/workbench/ComposerContextParts.tsx rename to app/workbench/src/ComposerContextParts.tsx index 9a5b1ccf1..55ad57e18 100644 --- a/app/shared/src/workbench/ComposerContextParts.tsx +++ b/app/workbench/src/ComposerContextParts.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next'; import type { ComposerMention, ComposerState, -} from '../composer'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +} from '@shared/composer'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import styles from './AgentHubWorkbench.module.css'; import { formatMainchainAgentLabel, diff --git a/app/shared/src/workbench/ComposerControlParts.tsx b/app/workbench/src/ComposerControlParts.tsx similarity index 97% rename from app/shared/src/workbench/ComposerControlParts.tsx rename to app/workbench/src/ComposerControlParts.tsx index 401dcee1a..d391411fc 100644 --- a/app/shared/src/workbench/ComposerControlParts.tsx +++ b/app/workbench/src/ComposerControlParts.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import type { ComposerMention } from '../composer'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import type { ComposerMention } from '@shared/composer'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { DesignNavIcon } from './designIcons'; import styles from './AgentHubWorkbench.module.css'; import { diff --git a/app/shared/src/workbench/ComposerMentionPopover.tsx b/app/workbench/src/ComposerMentionPopover.tsx similarity index 96% rename from app/shared/src/workbench/ComposerMentionPopover.tsx rename to app/workbench/src/ComposerMentionPopover.tsx index 75691c281..65c2eb027 100644 --- a/app/shared/src/workbench/ComposerMentionPopover.tsx +++ b/app/workbench/src/ComposerMentionPopover.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import type { ComposerMention } from '../composer'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import type { ComposerMention } from '@shared/composer'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { formatMentionChipLabel } from './ComposerPartsHelpers'; import type { MentionPopoverCoords } from './composerMentionPopoverHelpers'; import styles from './AgentHubWorkbench.module.css'; diff --git a/app/shared/src/workbench/ComposerPartsHelpers.test.ts b/app/workbench/src/ComposerPartsHelpers.test.ts similarity index 98% rename from app/shared/src/workbench/ComposerPartsHelpers.test.ts rename to app/workbench/src/ComposerPartsHelpers.test.ts index 62ba3ede5..8535118c6 100644 --- a/app/shared/src/workbench/ComposerPartsHelpers.test.ts +++ b/app/workbench/src/ComposerPartsHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { ComposerAttachment, ComposerMention } from '../composer'; +import type { ComposerAttachment, ComposerMention } from '@shared/composer'; import { agentPickerPlaceholder, buildAttachmentChipViewModel, diff --git a/app/shared/src/workbench/ComposerPartsHelpers.ts b/app/workbench/src/ComposerPartsHelpers.ts similarity index 96% rename from app/shared/src/workbench/ComposerPartsHelpers.ts rename to app/workbench/src/ComposerPartsHelpers.ts index f9ff2ef3e..a49a29797 100644 --- a/app/shared/src/workbench/ComposerPartsHelpers.ts +++ b/app/workbench/src/ComposerPartsHelpers.ts @@ -1,5 +1,5 @@ -import type { ComposerAttachment, ComposerMention } from '../composer'; -import { formatComposerAttachmentSize } from '../composer'; +import type { ComposerAttachment, ComposerMention } from '@shared/composer'; +import { formatComposerAttachmentSize } from '@shared/composer'; import { type AttachmentUploadState, type ComposerExecutionTarget, diff --git a/app/shared/src/workbench/ConversationHost.tsx b/app/workbench/src/ConversationHost.tsx similarity index 97% rename from app/shared/src/workbench/ConversationHost.tsx rename to app/workbench/src/ConversationHost.tsx index dbaadf0a4..4dcf425ae 100644 --- a/app/shared/src/workbench/ConversationHost.tsx +++ b/app/workbench/src/ConversationHost.tsx @@ -1,9 +1,9 @@ import React, { FormEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import type { TranscriptBlock, TextTranscriptBlock } from '../transcript'; -import { isSidebarOnlyTranscriptBlock, orderTranscriptBlocks } from '../transcript'; -import type { ComposerIntent, ComposerMention } from '../composer'; -import { buildComposerIntent, composerReducer, createInitialComposerState } from '../composer'; +import type { TranscriptBlock, TextTranscriptBlock } from '@shared/transcript'; +import { isSidebarOnlyTranscriptBlock, orderTranscriptBlocks } from '@shared/transcript'; +import type { ComposerIntent, ComposerMention } from '@shared/composer'; +import { buildComposerIntent, composerReducer, createInitialComposerState } from '@shared/composer'; import { enqueuePendingIntent, MAX_PENDING_DISPATCH_RETRIES, @@ -13,8 +13,8 @@ import { removePendingIntent, type PendingDispatchIntent, } from './composer/pendingIntents'; -import type { AgentHubPlatform, WorkbenchConversation } from '../platform'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import type { AgentHubPlatform, WorkbenchConversation } from '@shared/platform'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import type { AttachmentUploadState } from './UnifiedComposer'; import type { FileItem } from './inspector'; import type { ConnectionStatusKind } from './GlobalRail'; @@ -23,7 +23,7 @@ import { MainchainStatusStrip } from './MainchainStatusStrip'; import type { MainchainSummary } from './mainchain'; import { UnifiedComposer } from './UnifiedComposer'; import { WorkspaceHeader } from './WorkspaceHeader'; -import type { UnreadDividerDescriptor } from '../chatview'; +import type { UnreadDividerDescriptor } from '@shared/chatview'; import MessageSearchPanel from './MessageSearchPanel'; import { PageErrorBoundary } from './PageErrorBoundary'; import { useComposerSubmitBehavior } from './workbenchPreferences'; diff --git a/app/shared/src/workbench/ConversationSidebar.test.tsx b/app/workbench/src/ConversationSidebar.test.tsx similarity index 98% rename from app/shared/src/workbench/ConversationSidebar.test.tsx rename to app/workbench/src/ConversationSidebar.test.tsx index ce822e30c..34defe1fb 100644 --- a/app/shared/src/workbench/ConversationSidebar.test.tsx +++ b/app/workbench/src/ConversationSidebar.test.tsx @@ -2,7 +2,7 @@ import { act, fireEvent, render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; import type { ReactNode } from 'react'; -import type { WorkbenchConversation } from '../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import { ConversationSidebar } from './ConversationSidebar'; /* ────────────────────────────────────────────────────────────────────── @@ -12,7 +12,7 @@ import { ConversationSidebar } from './ConversationSidebar'; // These assertions use the en chatview literals; opt into the en bundle of // the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('en'); diff --git a/app/shared/src/workbench/ConversationSidebar.tsx b/app/workbench/src/ConversationSidebar.tsx similarity index 99% rename from app/shared/src/workbench/ConversationSidebar.tsx rename to app/workbench/src/ConversationSidebar.tsx index 87758f4f6..ca4d2b9d2 100644 --- a/app/shared/src/workbench/ConversationSidebar.tsx +++ b/app/workbench/src/ConversationSidebar.tsx @@ -1,12 +1,12 @@ import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Virtualizer, type VirtualizerHandle } from 'virtua'; -import type { WorkbenchConversation } from '../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import { DesignNavIcon } from './designIcons'; import { ContextMenu, type ContextMenuItem } from './floating'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; -import Modal from '../ui/Modal'; -import { Tooltip } from '../ui/Tooltip'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import Modal from '@shared/ui/Modal'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from './AgentHubWorkbench.module.css'; const SORT_STORAGE_KEY = 'agenthub.conversationSort'; diff --git a/app/shared/src/workbench/GlobalRail.tsx b/app/workbench/src/GlobalRail.tsx similarity index 98% rename from app/shared/src/workbench/GlobalRail.tsx rename to app/workbench/src/GlobalRail.tsx index 5efbaa1e7..8a4cc2e6d 100644 --- a/app/shared/src/workbench/GlobalRail.tsx +++ b/app/workbench/src/GlobalRail.tsx @@ -2,8 +2,8 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ProfilePopover, DemoToast } from './floating'; import { DesignNavIcon, type DesignNavIconName } from './designIcons'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; -import { Tooltip } from '../ui/Tooltip'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from './AgentHubWorkbench.module.css'; /* ═══ Page routing ═══ */ diff --git a/app/shared/src/workbench/MainchainStatusStrip.test.tsx b/app/workbench/src/MainchainStatusStrip.test.tsx similarity index 100% rename from app/shared/src/workbench/MainchainStatusStrip.test.tsx rename to app/workbench/src/MainchainStatusStrip.test.tsx diff --git a/app/shared/src/workbench/MainchainStatusStrip.tsx b/app/workbench/src/MainchainStatusStrip.tsx similarity index 94% rename from app/shared/src/workbench/MainchainStatusStrip.tsx rename to app/workbench/src/MainchainStatusStrip.tsx index e43781a16..9c526e17a 100644 --- a/app/shared/src/workbench/MainchainStatusStrip.tsx +++ b/app/workbench/src/MainchainStatusStrip.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import styles from './AgentHubWorkbench.module.css'; import type { MainchainSummary } from './mainchain'; diff --git a/app/shared/src/workbench/MessageSearchPanel.module.css b/app/workbench/src/MessageSearchPanel.module.css similarity index 100% rename from app/shared/src/workbench/MessageSearchPanel.module.css rename to app/workbench/src/MessageSearchPanel.module.css diff --git a/app/shared/src/workbench/MessageSearchPanel.stories.tsx b/app/workbench/src/MessageSearchPanel.stories.tsx similarity index 96% rename from app/shared/src/workbench/MessageSearchPanel.stories.tsx rename to app/workbench/src/MessageSearchPanel.stories.tsx index de9069790..c2e54ff58 100644 --- a/app/shared/src/workbench/MessageSearchPanel.stories.tsx +++ b/app/workbench/src/MessageSearchPanel.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; import MessageSearchPanel from './MessageSearchPanel'; -import type { ChatMessage } from '../types/chat'; +import type { ChatMessage } from '@shared/types/chat'; const messages: ChatMessage[] = [ { id: 'm1', role: 'user', timestamp: '2025-06-01T10:00:00Z', blocks: [{ kind: 'text', content: 'Fix the login bug' }] }, diff --git a/app/shared/src/workbench/MessageSearchPanel.test.tsx b/app/workbench/src/MessageSearchPanel.test.tsx similarity index 99% rename from app/shared/src/workbench/MessageSearchPanel.test.tsx rename to app/workbench/src/MessageSearchPanel.test.tsx index 5c240fb21..e253e424d 100644 --- a/app/shared/src/workbench/MessageSearchPanel.test.tsx +++ b/app/workbench/src/MessageSearchPanel.test.tsx @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { render, screen, fireEvent, act } from '@testing-library/react'; import MessageSearchPanel from './MessageSearchPanel'; -import type { ChatMessage } from '../types/chat'; +import type { ChatMessage } from '@shared/types/chat'; const baseMessages: ChatMessage[] = [ { diff --git a/app/shared/src/workbench/MessageSearchPanel.tsx b/app/workbench/src/MessageSearchPanel.tsx similarity index 98% rename from app/shared/src/workbench/MessageSearchPanel.tsx rename to app/workbench/src/MessageSearchPanel.tsx index f9da57131..89c042238 100644 --- a/app/shared/src/workbench/MessageSearchPanel.tsx +++ b/app/workbench/src/MessageSearchPanel.tsx @@ -1,8 +1,8 @@ import { useState, useMemo, useCallback, useEffect, useRef } from 'react'; -import type { ChatMessage } from '../types/chat'; -import type { TranscriptBlock } from '../transcript'; -import { useFocusTrap } from '../ui/focusTrap'; -import { Button } from '../ui/Button'; +import type { ChatMessage } from '@shared/types/chat'; +import type { TranscriptBlock } from '@shared/transcript'; +import { useFocusTrap } from '@shared/ui/focusTrap'; +import { Button } from '@shared/ui/Button'; import { DesignNavIcon } from './designIcons'; import styles from './MessageSearchPanel.module.css'; diff --git a/app/shared/src/workbench/PageErrorBoundary.test.tsx b/app/workbench/src/PageErrorBoundary.test.tsx similarity index 100% rename from app/shared/src/workbench/PageErrorBoundary.test.tsx rename to app/workbench/src/PageErrorBoundary.test.tsx diff --git a/app/shared/src/workbench/PageErrorBoundary.tsx b/app/workbench/src/PageErrorBoundary.tsx similarity index 95% rename from app/shared/src/workbench/PageErrorBoundary.tsx rename to app/workbench/src/PageErrorBoundary.tsx index d4450156b..3d7be4146 100644 --- a/app/shared/src/workbench/PageErrorBoundary.tsx +++ b/app/workbench/src/PageErrorBoundary.tsx @@ -1,5 +1,5 @@ import { type ReactNode } from 'react'; -import ErrorBoundary, { type ErrorBoundaryExtension } from '../ui/ErrorBoundary'; +import ErrorBoundary, { type ErrorBoundaryExtension } from '@shared/ui/ErrorBoundary'; /** * Page-level error boundary for workbench route gates. diff --git a/app/shared/src/workbench/RightInspector.tsx b/app/workbench/src/RightInspector.tsx similarity index 96% rename from app/shared/src/workbench/RightInspector.tsx rename to app/workbench/src/RightInspector.tsx index 7bb0eb631..e42c81560 100644 --- a/app/shared/src/workbench/RightInspector.tsx +++ b/app/workbench/src/RightInspector.tsx @@ -1,8 +1,8 @@ import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../i18n'; -import { buildInspectorEvidenceModel } from '../inspector'; -import type { FileDiff } from '../types/chat'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; +import { buildInspectorEvidenceModel } from '@shared/inspector'; +import type { FileDiff } from '@shared/types/chat'; import { type FileItem, type PreviewFile, @@ -31,7 +31,7 @@ import { } from './rightInspectorHelpers'; import type { RightInspectorProps } from './rightInspectorTypes'; -export type { RuntimeEvidenceSnapshot } from '../inspector'; +export type { RuntimeEvidenceSnapshot } from '@shared/inspector'; export type { RightInspectorProps } from './rightInspectorTypes'; /* ═══ Component ═══ */ diff --git a/app/shared/src/workbench/RightInspectorModePanel.tsx b/app/workbench/src/RightInspectorModePanel.tsx similarity index 93% rename from app/shared/src/workbench/RightInspectorModePanel.tsx rename to app/workbench/src/RightInspectorModePanel.tsx index 500c7edb0..cd31b95f2 100644 --- a/app/shared/src/workbench/RightInspectorModePanel.tsx +++ b/app/workbench/src/RightInspectorModePanel.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import type { RuntimeEvidenceSnapshot } from '../inspector'; -import type { PreviewPort } from '../platform'; -import type { EvidenceRef, ContextUsageTranscriptBlock } from '../transcript'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { PreviewPort } from '@shared/platform'; +import type { EvidenceRef, ContextUsageTranscriptBlock } from '@shared/transcript'; import { BrowserModeBody, FilesModeBody, @@ -12,8 +12,8 @@ import { type RunResultInfo, type TaskItem, } from './inspector'; -import type { DagNode } from '../ui/DagTree'; -import type { FileDiff } from '../types/chat'; +import type { DagNode } from '@shared/ui/DagTree'; +import type { FileDiff } from '@shared/types/chat'; import styles from './AgentHubWorkbench.module.css'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/RightInspectorResizer.tsx b/app/workbench/src/RightInspectorResizer.tsx similarity index 95% rename from app/shared/src/workbench/RightInspectorResizer.tsx rename to app/workbench/src/RightInspectorResizer.tsx index d0be50c4f..3d5de9168 100644 --- a/app/shared/src/workbench/RightInspectorResizer.tsx +++ b/app/workbench/src/RightInspectorResizer.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import styles from './AgentHubWorkbench.module.css'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/RuntimeBrandIcon.module.css b/app/workbench/src/RuntimeBrandIcon.module.css similarity index 100% rename from app/shared/src/workbench/RuntimeBrandIcon.module.css rename to app/workbench/src/RuntimeBrandIcon.module.css diff --git a/app/shared/src/workbench/RuntimeBrandIcon.test.tsx b/app/workbench/src/RuntimeBrandIcon.test.tsx similarity index 99% rename from app/shared/src/workbench/RuntimeBrandIcon.test.tsx rename to app/workbench/src/RuntimeBrandIcon.test.tsx index b5c7c9954..63c19729d 100644 --- a/app/shared/src/workbench/RuntimeBrandIcon.test.tsx +++ b/app/workbench/src/RuntimeBrandIcon.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { describe, expect, it, vi } from 'vitest'; -import { render, screen } from '../__tests__/setup'; +import { render, screen } from './__tests__/setup'; import { RuntimeBrandIcon, resolveRuntimeBrandIcon, diff --git a/app/shared/src/workbench/RuntimeBrandIcon.tsx b/app/workbench/src/RuntimeBrandIcon.tsx similarity index 75% rename from app/shared/src/workbench/RuntimeBrandIcon.tsx rename to app/workbench/src/RuntimeBrandIcon.tsx index d023a1209..ad5b45830 100644 --- a/app/shared/src/workbench/RuntimeBrandIcon.tsx +++ b/app/workbench/src/RuntimeBrandIcon.tsx @@ -1,11 +1,11 @@ export { RuntimeBrandIcon, resolveRuntimeBrandIcon, -} from '../ui/RuntimeIcon'; +} from '@shared/ui/RuntimeIcon'; export type { RuntimeBrandIconKind, RuntimeBrandIconProps, RuntimeBrandIconResolution, RuntimeBrandIconSize, RuntimeBrandIconSource, -} from '../ui/RuntimeIcon'; +} from '@shared/ui/RuntimeIcon'; diff --git a/app/shared/src/workbench/UnifiedComposer.test.tsx b/app/workbench/src/UnifiedComposer.test.tsx similarity index 98% rename from app/shared/src/workbench/UnifiedComposer.test.tsx rename to app/workbench/src/UnifiedComposer.test.tsx index af4293087..7b9aa0f4a 100644 --- a/app/shared/src/workbench/UnifiedComposer.test.tsx +++ b/app/workbench/src/UnifiedComposer.test.tsx @@ -2,8 +2,8 @@ import '@testing-library/jest-dom/vitest'; import { fireEvent, render, screen, within } from '@testing-library/react'; import type { FormEvent } from 'react'; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; -import { clearDraft, loadDraft, saveDraft } from '../composer'; -import type { ComposerMention, ComposerState } from '../composer'; +import { clearDraft, loadDraft, saveDraft } from '@shared/composer'; +import type { ComposerMention, ComposerState } from '@shared/composer'; import { UnifiedComposer } from './UnifiedComposer'; vi.mock('@lobehub/icons', () => ({ @@ -18,7 +18,7 @@ vi.mock('@lobehub/icons/es/Antigravity/components/Color.js', () => ({ default: ( // Composer copy assertions use the en chatview literals; opt into the en // bundle of the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('en'); diff --git a/app/shared/src/workbench/UnifiedComposer.tsx b/app/workbench/src/UnifiedComposer.tsx similarity index 99% rename from app/shared/src/workbench/UnifiedComposer.tsx rename to app/workbench/src/UnifiedComposer.tsx index 5a2c70756..da93d7be6 100644 --- a/app/shared/src/workbench/UnifiedComposer.tsx +++ b/app/workbench/src/UnifiedComposer.tsx @@ -1,8 +1,8 @@ import React, { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { browserFilesToComposerAttachments, clearDraft, loadDraft, saveDraft } from '../composer'; -import type { ComposerMention } from '../composer'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import { browserFilesToComposerAttachments, clearDraft, loadDraft, saveDraft } from '@shared/composer'; +import type { ComposerMention } from '@shared/composer'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { detectMentionTrigger, filterMentionCandidates, diff --git a/app/shared/src/workbench/UnifiedComposerParts.tsx b/app/workbench/src/UnifiedComposerParts.tsx similarity index 100% rename from app/shared/src/workbench/UnifiedComposerParts.tsx rename to app/workbench/src/UnifiedComposerParts.tsx diff --git a/app/shared/src/workbench/WorkbenchAgentsRouteView.tsx b/app/workbench/src/WorkbenchAgentsRouteView.tsx similarity index 98% rename from app/shared/src/workbench/WorkbenchAgentsRouteView.tsx rename to app/workbench/src/WorkbenchAgentsRouteView.tsx index 031cc421b..988231ab6 100644 --- a/app/shared/src/workbench/WorkbenchAgentsRouteView.tsx +++ b/app/workbench/src/WorkbenchAgentsRouteView.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import type { WorkbenchAgent } from '../platform'; +import type { WorkbenchAgent } from '@shared/platform'; import type { AgentConfig, SkillMarketItem, MCPMarketItem } from './pages/AgentsPage'; import { AgentsPage } from './pages/AgentsPage'; import { diff --git a/app/shared/src/workbench/WorkbenchContactsRouteView.tsx b/app/workbench/src/WorkbenchContactsRouteView.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchContactsRouteView.tsx rename to app/workbench/src/WorkbenchContactsRouteView.tsx diff --git a/app/shared/src/workbench/WorkbenchDocsRouteView.tsx b/app/workbench/src/WorkbenchDocsRouteView.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchDocsRouteView.tsx rename to app/workbench/src/WorkbenchDocsRouteView.tsx diff --git a/app/shared/src/workbench/WorkbenchFrame.terminalDock.test.tsx b/app/workbench/src/WorkbenchFrame.terminalDock.test.tsx similarity index 98% rename from app/shared/src/workbench/WorkbenchFrame.terminalDock.test.tsx rename to app/workbench/src/WorkbenchFrame.terminalDock.test.tsx index 8324d615c..3c999e0e1 100644 --- a/app/shared/src/workbench/WorkbenchFrame.terminalDock.test.tsx +++ b/app/workbench/src/WorkbenchFrame.terminalDock.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import type { AgentHubPlatform } from '../platform'; +import type { AgentHubPlatform } from '@shared/platform'; import { WorkbenchFrame } from './WorkbenchFrame'; vi.mock('./GlobalRail', () => ({ diff --git a/app/shared/src/workbench/WorkbenchFrame.tsx b/app/workbench/src/WorkbenchFrame.tsx similarity index 99% rename from app/shared/src/workbench/WorkbenchFrame.tsx rename to app/workbench/src/WorkbenchFrame.tsx index 222c304e1..46ba98204 100644 --- a/app/shared/src/workbench/WorkbenchFrame.tsx +++ b/app/workbench/src/WorkbenchFrame.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { GlobalRail, type GlobalRailPage } from './GlobalRail'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { buildTerminalPanelDockProps, buildWorkbenchShellDataAttrs, diff --git a/app/shared/src/workbench/WorkbenchFrameChromeParts.tsx b/app/workbench/src/WorkbenchFrameChromeParts.tsx similarity index 96% rename from app/shared/src/workbench/WorkbenchFrameChromeParts.tsx rename to app/workbench/src/WorkbenchFrameChromeParts.tsx index b6e9cc0ca..4624a0c3c 100644 --- a/app/shared/src/workbench/WorkbenchFrameChromeParts.tsx +++ b/app/workbench/src/WorkbenchFrameChromeParts.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { RecoveryPanel } from '../ui'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; +import { RecoveryPanel } from '@shared/ui'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { DesignNavIcon } from './designIcons'; import type { WorkspaceLoadErrorStateProps, diff --git a/app/shared/src/workbench/WorkbenchFrameParts.test.tsx b/app/workbench/src/WorkbenchFrameParts.test.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchFrameParts.test.tsx rename to app/workbench/src/WorkbenchFrameParts.test.tsx diff --git a/app/shared/src/workbench/WorkbenchFrameParts.tsx b/app/workbench/src/WorkbenchFrameParts.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchFrameParts.tsx rename to app/workbench/src/WorkbenchFrameParts.tsx diff --git a/app/shared/src/workbench/WorkbenchProfileOverlays.tsx b/app/workbench/src/WorkbenchProfileOverlays.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchProfileOverlays.tsx rename to app/workbench/src/WorkbenchProfileOverlays.tsx diff --git a/app/shared/src/workbench/WorkbenchProjectsRouteView.tsx b/app/workbench/src/WorkbenchProjectsRouteView.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchProjectsRouteView.tsx rename to app/workbench/src/WorkbenchProjectsRouteView.tsx diff --git a/app/shared/src/workbench/WorkbenchRoutes.tsx b/app/workbench/src/WorkbenchRoutes.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchRoutes.tsx rename to app/workbench/src/WorkbenchRoutes.tsx diff --git a/app/shared/src/workbench/WorkbenchSettingsRouteView.tsx b/app/workbench/src/WorkbenchSettingsRouteView.tsx similarity index 98% rename from app/shared/src/workbench/WorkbenchSettingsRouteView.tsx rename to app/workbench/src/WorkbenchSettingsRouteView.tsx index 8b1cd7edc..670cc03b7 100644 --- a/app/shared/src/workbench/WorkbenchSettingsRouteView.tsx +++ b/app/workbench/src/WorkbenchSettingsRouteView.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import type { LocalCliDiscoveryManifest, RuntimeSessionSummary } from '../platform'; +import type { LocalCliDiscoveryManifest, RuntimeSessionSummary } from '@shared/platform'; import { SettingsPage } from './pages/SettingsPage'; import type { WorkbenchSettingsRoute } from './useWorkbenchSettingsRoute'; import { buildSettingsPageProps } from './workbenchRoutesHelpers'; diff --git a/app/shared/src/workbench/WorkbenchTasksRouteView.tsx b/app/workbench/src/WorkbenchTasksRouteView.tsx similarity index 100% rename from app/shared/src/workbench/WorkbenchTasksRouteView.tsx rename to app/workbench/src/WorkbenchTasksRouteView.tsx diff --git a/app/shared/src/workbench/WorkbenchTranscriptOverlays.tsx b/app/workbench/src/WorkbenchTranscriptOverlays.tsx similarity index 96% rename from app/shared/src/workbench/WorkbenchTranscriptOverlays.tsx rename to app/workbench/src/WorkbenchTranscriptOverlays.tsx index 26c1ec64a..00c22f5e8 100644 --- a/app/shared/src/workbench/WorkbenchTranscriptOverlays.tsx +++ b/app/workbench/src/WorkbenchTranscriptOverlays.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import type { WorkbenchConversation } from '../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import { ContextMenu, MultiSelectBar, diff --git a/app/shared/src/workbench/WorkspaceHeader.tsx b/app/workbench/src/WorkspaceHeader.tsx similarity index 96% rename from app/shared/src/workbench/WorkspaceHeader.tsx rename to app/workbench/src/WorkspaceHeader.tsx index 32854c640..42eb015b5 100644 --- a/app/shared/src/workbench/WorkspaceHeader.tsx +++ b/app/workbench/src/WorkspaceHeader.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import type { WorkbenchConversation } from '../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import { DesignNavIcon } from './designIcons'; -import { CHATVIEW_I18N_NAMESPACE } from '../chatview/i18n/resources'; -import { Tooltip } from '../ui/Tooltip'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from './AgentHubWorkbench.module.css'; export interface WorkspaceHeaderProps { diff --git a/app/shared/src/workbench/__tests__/composer.test.tsx b/app/workbench/src/__tests__/composer.test.tsx similarity index 99% rename from app/shared/src/workbench/__tests__/composer.test.tsx rename to app/workbench/src/__tests__/composer.test.tsx index b48fe7ad2..6acf213cf 100644 --- a/app/shared/src/workbench/__tests__/composer.test.tsx +++ b/app/workbench/src/__tests__/composer.test.tsx @@ -14,7 +14,7 @@ import { within, } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { createMockPlatform } from '../../platform/createMockPlatform'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; import { workbenchAgents as agents, diff --git a/app/shared/src/workbench/__tests__/dispatchQueue.test.tsx b/app/workbench/src/__tests__/dispatchQueue.test.tsx similarity index 98% rename from app/shared/src/workbench/__tests__/dispatchQueue.test.tsx rename to app/workbench/src/__tests__/dispatchQueue.test.tsx index e9f941d71..ee7d50f13 100644 --- a/app/shared/src/workbench/__tests__/dispatchQueue.test.tsx +++ b/app/workbench/src/__tests__/dispatchQueue.test.tsx @@ -13,7 +13,7 @@ import './helpers'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { MAX_PENDING_DISPATCH_RETRIES } from '../composer/pendingIntents'; -import { createMockPlatform } from '../../platform/createMockPlatform'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; describe('pending dispatch queue (CF22)', () => { diff --git a/app/shared/src/workbench/__tests__/helpers.tsx b/app/workbench/src/__tests__/helpers.tsx similarity index 98% rename from app/shared/src/workbench/__tests__/helpers.tsx rename to app/workbench/src/__tests__/helpers.tsx index a9c6d80c8..b44f89703 100644 --- a/app/shared/src/workbench/__tests__/helpers.tsx +++ b/app/workbench/src/__tests__/helpers.tsx @@ -12,7 +12,7 @@ import { afterEach, beforeAll, vi } from 'vitest'; // Workbench chrome + transcript copy resolve through the shared test // i18next instance; opt into the zh bundle for this suite (Issue #1717). -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/__tests__/hubPages.test.tsx b/app/workbench/src/__tests__/hubPages.test.tsx similarity index 99% rename from app/shared/src/workbench/__tests__/hubPages.test.tsx rename to app/workbench/src/__tests__/hubPages.test.tsx index 5ade31f28..a7d0b43e6 100644 --- a/app/shared/src/workbench/__tests__/hubPages.test.tsx +++ b/app/workbench/src/__tests__/hubPages.test.tsx @@ -14,7 +14,7 @@ import { within, } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { createMockPlatform } from '../../platform/createMockPlatform'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; import { workbenchAgents as agents, diff --git a/app/shared/src/workbench/__tests__/inspector.test.tsx b/app/workbench/src/__tests__/inspector.test.tsx similarity index 99% rename from app/shared/src/workbench/__tests__/inspector.test.tsx rename to app/workbench/src/__tests__/inspector.test.tsx index 0c1ad1c29..28ba78083 100644 --- a/app/shared/src/workbench/__tests__/inspector.test.tsx +++ b/app/workbench/src/__tests__/inspector.test.tsx @@ -8,8 +8,8 @@ import { installWorkbenchTestHooks, restoreInspectorTab } from './helpers'; import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { createMockPlatform } from '../../platform/createMockPlatform'; -import type { TranscriptBlock } from '../../transcript/types'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; +import type { TranscriptBlock } from '@shared/transcript/types'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; import { DESIGN_NAV_GLYPH_STROKE_WIDTH } from '../designIcons'; import { diff --git a/app/shared/src/workbench/__tests__/pages.test.tsx b/app/workbench/src/__tests__/pages.test.tsx similarity index 98% rename from app/shared/src/workbench/__tests__/pages.test.tsx rename to app/workbench/src/__tests__/pages.test.tsx index 06e5c3caf..4cbd69e4d 100644 --- a/app/shared/src/workbench/__tests__/pages.test.tsx +++ b/app/workbench/src/__tests__/pages.test.tsx @@ -13,9 +13,9 @@ import { within, } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { WORKBENCH_DATA_MODE_STORAGE_KEY } from '../../demo'; -import { createMockPlatform } from '../../platform/createMockPlatform'; -import type { TranscriptBlock } from '../../transcript/types'; +import { WORKBENCH_DATA_MODE_STORAGE_KEY } from '@shared/demo'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; +import type { TranscriptBlock } from '@shared/transcript/types'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; import { workbenchAgents as agents, diff --git a/app/workbench/src/__tests__/setup.ts b/app/workbench/src/__tests__/setup.ts new file mode 100644 index 000000000..2805a7030 --- /dev/null +++ b/app/workbench/src/__tests__/setup.ts @@ -0,0 +1,67 @@ +import '@testing-library/jest-dom/vitest'; +import { beforeAll, vi } from 'vitest'; + +// Shared test i18next instance (Issue #1717): real chatview + sharedWorkbench +// resources, isolated per project via i18next.createInstance(). Registered +// so every suite renders through the real `useTranslation` path without +// per-file react-i18next mocks and without NO_I18NEXT_INSTANCE noise. +// The default language is the helper's key-echo pseudo-language: suites +// outside the #1717 frozen-file boundary keep their raw-key visible +// behavior, while frozen suites opt into zh/en per file via +// useTestI18nLanguage(). +import { installTestI18n } from '@shared/testing/i18n'; +import { installJsdomPolyfills } from '@shared/testing/jsdomPolyfills'; + +installTestI18n(); + +// Global fetch mock for tests that hit network-bound code paths (mirrors the +// shared package setup). Tests can reset/override per suite via +// vi.mocked(fetch).mockImplementation. +globalThis.fetch = vi.fn(); + +// ────────────────────────────────────────────────────────────── +// Polyfills for jsdom — required once virtualization (virtua) landed. +// jsdom has no layout engine; see @shared/testing/jsdomPolyfills (#1678). +// ────────────────────────────────────────────────────────────── +installJsdomPolyfills(); + +// Re-export testing utilities so workbench component tests import from one +// place (same contract the shared package setup provided before #1759). +export { render, screen, fireEvent, waitFor, within } from '@testing-library/react'; +export { default as userEvent } from '@testing-library/user-event'; + +/* ────────────────────────────────────────────────────────────── + Synchronous React.lazy preload for tests + + WorkbenchRoutes wraps every page component in React.lazy() + + Suspense. In jsdom lazy imports resolve asynchronously which + causes flaky "element not found" failures when tests navigate + between rail pages and immediately query page content. + + Pre-resolving all lazy pages ensures the import Promise is + fulfilled before any test renders a Suspense boundary. + ────────────────────────────────────────────────────────────── */ + +const WORKBENCH_PRELOAD_TIMEOUT_MS = 5_000; + +async function preloadWorkbenchPage(load: () => Promise): Promise { + let timeout: ReturnType | undefined; + await Promise.race([ + load(), + new Promise((resolve) => { + timeout = setTimeout(resolve, WORKBENCH_PRELOAD_TIMEOUT_MS); + }), + ]); + if (timeout) clearTimeout(timeout); +} + +beforeAll(async () => { + await Promise.allSettled([ + preloadWorkbenchPage(() => import('../pages/ProjectsPage')), + preloadWorkbenchPage(() => import('../pages/ContactsPage')), + preloadWorkbenchPage(() => import('../pages/DocsPage')), + preloadWorkbenchPage(() => import('../pages/AgentsPage')), + preloadWorkbenchPage(() => import('../pages/TasksPage')), + preloadWorkbenchPage(() => import('../pages/SettingsPage')), + ]); +}, 30_000); diff --git a/app/shared/src/workbench/__tests__/shell.test.tsx b/app/workbench/src/__tests__/shell.test.tsx similarity index 99% rename from app/shared/src/workbench/__tests__/shell.test.tsx rename to app/workbench/src/__tests__/shell.test.tsx index 369dc0cc7..917b49b74 100644 --- a/app/shared/src/workbench/__tests__/shell.test.tsx +++ b/app/workbench/src/__tests__/shell.test.tsx @@ -7,7 +7,7 @@ import { installWorkbenchTestHooks } from './helpers'; import { fireEvent, render, screen, within } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; -import { createMockPlatform } from '../../platform/createMockPlatform'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; import { DESIGN_NAV_GLYPH_SIZE, diff --git a/app/shared/src/workbench/__tests__/sidebar.test.tsx b/app/workbench/src/__tests__/sidebar.test.tsx similarity index 99% rename from app/shared/src/workbench/__tests__/sidebar.test.tsx rename to app/workbench/src/__tests__/sidebar.test.tsx index 98002e3b6..01f27e655 100644 --- a/app/shared/src/workbench/__tests__/sidebar.test.tsx +++ b/app/workbench/src/__tests__/sidebar.test.tsx @@ -8,7 +8,7 @@ import { installWorkbenchTestHooks } from './helpers'; import { act, fireEvent, render, screen, within } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { createMockPlatform } from '../../platform/createMockPlatform'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; import { workbenchAgents as agents, diff --git a/app/shared/src/workbench/__tests__/transcript.test.tsx b/app/workbench/src/__tests__/transcript.test.tsx similarity index 98% rename from app/shared/src/workbench/__tests__/transcript.test.tsx rename to app/workbench/src/__tests__/transcript.test.tsx index badc00493..7598e344b 100644 --- a/app/shared/src/workbench/__tests__/transcript.test.tsx +++ b/app/workbench/src/__tests__/transcript.test.tsx @@ -13,9 +13,9 @@ import { within, } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { projectGroupMessageLoopTranscript } from '../../demo'; -import { createMockPlatform } from '../../platform/createMockPlatform'; -import type { TranscriptBlock } from '../../transcript/types'; +import { projectGroupMessageLoopTranscript } from '@shared/demo'; +import { createMockPlatform } from '@shared/platform/createMockPlatform'; +import type { TranscriptBlock } from '@shared/transcript/types'; import { AgentHubWorkbench } from '../AgentHubWorkbench'; import { workbenchAgents as agents, diff --git a/app/shared/src/workbench/agentCapabilities.test.ts b/app/workbench/src/agentCapabilities.test.ts similarity index 100% rename from app/shared/src/workbench/agentCapabilities.test.ts rename to app/workbench/src/agentCapabilities.test.ts diff --git a/app/shared/src/workbench/agentCapabilities.ts b/app/workbench/src/agentCapabilities.ts similarity index 100% rename from app/shared/src/workbench/agentCapabilities.ts rename to app/workbench/src/agentCapabilities.ts diff --git a/app/shared/src/workbench/agentProfileCatalog.test.ts b/app/workbench/src/agentProfileCatalog.test.ts similarity index 100% rename from app/shared/src/workbench/agentProfileCatalog.test.ts rename to app/workbench/src/agentProfileCatalog.test.ts diff --git a/app/shared/src/workbench/agentProfileCatalog.ts b/app/workbench/src/agentProfileCatalog.ts similarity index 100% rename from app/shared/src/workbench/agentProfileCatalog.ts rename to app/workbench/src/agentProfileCatalog.ts diff --git a/app/shared/src/workbench/agentProfileCatalogData.ts b/app/workbench/src/agentProfileCatalogData.ts similarity index 100% rename from app/shared/src/workbench/agentProfileCatalogData.ts rename to app/workbench/src/agentProfileCatalogData.ts diff --git a/app/shared/src/workbench/agentProfileCatalogHelpers.test.ts b/app/workbench/src/agentProfileCatalogHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/agentProfileCatalogHelpers.test.ts rename to app/workbench/src/agentProfileCatalogHelpers.test.ts diff --git a/app/shared/src/workbench/agentProfileCatalogHelpers.ts b/app/workbench/src/agentProfileCatalogHelpers.ts similarity index 99% rename from app/shared/src/workbench/agentProfileCatalogHelpers.ts rename to app/workbench/src/agentProfileCatalogHelpers.ts index 633763d02..c7f013f76 100644 --- a/app/shared/src/workbench/agentProfileCatalogHelpers.ts +++ b/app/workbench/src/agentProfileCatalogHelpers.ts @@ -9,7 +9,7 @@ import { type AgentHubAgentSpecMCPServerV1, type AgentHubAgentSpecMemoryPolicyV1, type AgentHubAgentSpecV1, -} from '../agentSpec'; +} from '@shared/agentSpec'; import type { AgentProfileCatalogItem } from './agentProfileCatalogTypes'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/agentProfileCatalogTypes.ts b/app/workbench/src/agentProfileCatalogTypes.ts similarity index 100% rename from app/shared/src/workbench/agentProfileCatalogTypes.ts rename to app/workbench/src/agentProfileCatalogTypes.ts diff --git a/app/shared/src/workbench/agentsProductCopyContract.test.ts b/app/workbench/src/agentsProductCopyContract.test.ts similarity index 99% rename from app/shared/src/workbench/agentsProductCopyContract.test.ts rename to app/workbench/src/agentsProductCopyContract.test.ts index 609f8251e..96e65a3f6 100644 --- a/app/shared/src/workbench/agentsProductCopyContract.test.ts +++ b/app/workbench/src/agentsProductCopyContract.test.ts @@ -15,7 +15,7 @@ import { isAllowedTechnicalId, } from './agentsProductCopyContract'; import { workbenchAgentToAgentConfig } from './workbenchAgentMapping'; -import type { WorkbenchAgent } from '../platform'; +import type { WorkbenchAgent } from '@shared/platform'; const WORKBENCH_ROOT = dirname(fileURLToPath(import.meta.url)); const AGENTS_PAGES_ROOT = join(WORKBENCH_ROOT, 'pages', 'agents'); diff --git a/app/shared/src/workbench/agentsProductCopyContract.ts b/app/workbench/src/agentsProductCopyContract.ts similarity index 100% rename from app/shared/src/workbench/agentsProductCopyContract.ts rename to app/workbench/src/agentsProductCopyContract.ts diff --git a/app/shared/src/workbench/auxPanel/AuxPanel.module.css b/app/workbench/src/auxPanel/AuxPanel.module.css similarity index 100% rename from app/shared/src/workbench/auxPanel/AuxPanel.module.css rename to app/workbench/src/auxPanel/AuxPanel.module.css diff --git a/app/shared/src/workbench/auxPanel/AuxPanel.tsx b/app/workbench/src/auxPanel/AuxPanel.tsx similarity index 100% rename from app/shared/src/workbench/auxPanel/AuxPanel.tsx rename to app/workbench/src/auxPanel/AuxPanel.tsx diff --git a/app/shared/src/workbench/auxPanel/index.ts b/app/workbench/src/auxPanel/index.ts similarity index 100% rename from app/shared/src/workbench/auxPanel/index.ts rename to app/workbench/src/auxPanel/index.ts diff --git a/app/shared/src/workbench/auxPanel/resolveAuxTabs.test.ts b/app/workbench/src/auxPanel/resolveAuxTabs.test.ts similarity index 100% rename from app/shared/src/workbench/auxPanel/resolveAuxTabs.test.ts rename to app/workbench/src/auxPanel/resolveAuxTabs.test.ts diff --git a/app/shared/src/workbench/auxPanel/resolveAuxTabs.ts b/app/workbench/src/auxPanel/resolveAuxTabs.ts similarity index 100% rename from app/shared/src/workbench/auxPanel/resolveAuxTabs.ts rename to app/workbench/src/auxPanel/resolveAuxTabs.ts diff --git a/app/shared/src/workbench/auxPanel/types.ts b/app/workbench/src/auxPanel/types.ts similarity index 100% rename from app/shared/src/workbench/auxPanel/types.ts rename to app/workbench/src/auxPanel/types.ts diff --git a/app/shared/src/workbench/composer/pendingIntents.test.ts b/app/workbench/src/composer/pendingIntents.test.ts similarity index 100% rename from app/shared/src/workbench/composer/pendingIntents.test.ts rename to app/workbench/src/composer/pendingIntents.test.ts diff --git a/app/shared/src/workbench/composer/pendingIntents.ts b/app/workbench/src/composer/pendingIntents.ts similarity index 98% rename from app/shared/src/workbench/composer/pendingIntents.ts rename to app/workbench/src/composer/pendingIntents.ts index 20b805478..bff2485e3 100644 --- a/app/shared/src/workbench/composer/pendingIntents.ts +++ b/app/workbench/src/composer/pendingIntents.ts @@ -1,4 +1,4 @@ -import type { ComposerIntent } from '../../composer'; +import type { ComposerIntent } from '@shared/composer'; /** * Client-side pending dispatch queue (CF22 / composer streaming spike ②). diff --git a/app/shared/src/workbench/composerMentionPopoverHelpers.test.ts b/app/workbench/src/composerMentionPopoverHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/composerMentionPopoverHelpers.test.ts rename to app/workbench/src/composerMentionPopoverHelpers.test.ts diff --git a/app/shared/src/workbench/composerMentionPopoverHelpers.ts b/app/workbench/src/composerMentionPopoverHelpers.ts similarity index 100% rename from app/shared/src/workbench/composerMentionPopoverHelpers.ts rename to app/workbench/src/composerMentionPopoverHelpers.ts diff --git a/app/shared/src/workbench/composerMentionTrigger.test.ts b/app/workbench/src/composerMentionTrigger.test.ts similarity index 98% rename from app/shared/src/workbench/composerMentionTrigger.test.ts rename to app/workbench/src/composerMentionTrigger.test.ts index a8f497efe..ead1699ed 100644 --- a/app/shared/src/workbench/composerMentionTrigger.test.ts +++ b/app/workbench/src/composerMentionTrigger.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { ComposerMention } from '../composer'; +import type { ComposerMention } from '@shared/composer'; import { detectMentionTrigger, filterMentionCandidates, diff --git a/app/shared/src/workbench/composerMentionTrigger.ts b/app/workbench/src/composerMentionTrigger.ts similarity index 98% rename from app/shared/src/workbench/composerMentionTrigger.ts rename to app/workbench/src/composerMentionTrigger.ts index 9013ec06e..6c916ac57 100644 --- a/app/shared/src/workbench/composerMentionTrigger.ts +++ b/app/workbench/src/composerMentionTrigger.ts @@ -1,4 +1,4 @@ -import type { ComposerMention } from '../composer'; +import type { ComposerMention } from '@shared/composer'; /* ═══════════════════════════════════════════════════════════════════════ composerMentionTrigger — pure @mention trigger detection for the diff --git a/app/workbench/src/css-modules.d.ts b/app/workbench/src/css-modules.d.ts new file mode 100644 index 000000000..8e6de1a4c --- /dev/null +++ b/app/workbench/src/css-modules.d.ts @@ -0,0 +1,11 @@ +declare module '*.css'; + +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/app/shared/src/workbench/designIcons.test.tsx b/app/workbench/src/designIcons.test.tsx similarity index 100% rename from app/shared/src/workbench/designIcons.test.tsx rename to app/workbench/src/designIcons.test.tsx diff --git a/app/shared/src/workbench/designIcons.tsx b/app/workbench/src/designIcons.tsx similarity index 100% rename from app/shared/src/workbench/designIcons.tsx rename to app/workbench/src/designIcons.tsx diff --git a/app/shared/src/workbench/designIconsHelpers.test.ts b/app/workbench/src/designIconsHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/designIconsHelpers.test.ts rename to app/workbench/src/designIconsHelpers.test.ts diff --git a/app/shared/src/workbench/designIconsHelpers.ts b/app/workbench/src/designIconsHelpers.ts similarity index 100% rename from app/shared/src/workbench/designIconsHelpers.ts rename to app/workbench/src/designIconsHelpers.ts diff --git a/app/shared/src/workbench/designIconsNavPaths.tsx b/app/workbench/src/designIconsNavPaths.tsx similarity index 100% rename from app/shared/src/workbench/designIconsNavPaths.tsx rename to app/workbench/src/designIconsNavPaths.tsx diff --git a/app/shared/src/workbench/desktopChromeEvents.ts b/app/workbench/src/desktopChromeEvents.ts similarity index 100% rename from app/shared/src/workbench/desktopChromeEvents.ts rename to app/workbench/src/desktopChromeEvents.ts diff --git a/app/shared/src/workbench/documentPreview.ts b/app/workbench/src/documentPreview.ts similarity index 100% rename from app/shared/src/workbench/documentPreview.ts rename to app/workbench/src/documentPreview.ts diff --git a/app/shared/src/workbench/floating/ContextMenu.module.css b/app/workbench/src/floating/ContextMenu.module.css similarity index 100% rename from app/shared/src/workbench/floating/ContextMenu.module.css rename to app/workbench/src/floating/ContextMenu.module.css diff --git a/app/shared/src/workbench/floating/ContextMenu.test.tsx b/app/workbench/src/floating/ContextMenu.test.tsx similarity index 100% rename from app/shared/src/workbench/floating/ContextMenu.test.tsx rename to app/workbench/src/floating/ContextMenu.test.tsx diff --git a/app/shared/src/workbench/floating/ContextMenu.tsx b/app/workbench/src/floating/ContextMenu.tsx similarity index 98% rename from app/shared/src/workbench/floating/ContextMenu.tsx rename to app/workbench/src/floating/ContextMenu.tsx index 3e783869a..bd5086b80 100644 --- a/app/shared/src/workbench/floating/ContextMenu.tsx +++ b/app/workbench/src/floating/ContextMenu.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { DesignNavIcon, type DesignNavIconName } from '../designIcons'; -import { FOCUSABLE, useFocusTrap } from '../../ui/focusTrap'; +import { FOCUSABLE, useFocusTrap } from '@shared/ui/focusTrap'; import styles from './ContextMenu.module.css'; export interface ContextMenuItem { diff --git a/app/shared/src/workbench/floating/DemoToast.tsx b/app/workbench/src/floating/DemoToast.tsx similarity index 100% rename from app/shared/src/workbench/floating/DemoToast.tsx rename to app/workbench/src/floating/DemoToast.tsx diff --git a/app/shared/src/workbench/floating/EmojiPicker.module.css b/app/workbench/src/floating/EmojiPicker.module.css similarity index 100% rename from app/shared/src/workbench/floating/EmojiPicker.module.css rename to app/workbench/src/floating/EmojiPicker.module.css diff --git a/app/shared/src/workbench/floating/EmojiPicker.test.tsx b/app/workbench/src/floating/EmojiPicker.test.tsx similarity index 100% rename from app/shared/src/workbench/floating/EmojiPicker.test.tsx rename to app/workbench/src/floating/EmojiPicker.test.tsx diff --git a/app/shared/src/workbench/floating/EmojiPicker.tsx b/app/workbench/src/floating/EmojiPicker.tsx similarity index 100% rename from app/shared/src/workbench/floating/EmojiPicker.tsx rename to app/workbench/src/floating/EmojiPicker.tsx diff --git a/app/shared/src/workbench/floating/ForwardConversationPicker.module.css b/app/workbench/src/floating/ForwardConversationPicker.module.css similarity index 100% rename from app/shared/src/workbench/floating/ForwardConversationPicker.module.css rename to app/workbench/src/floating/ForwardConversationPicker.module.css diff --git a/app/shared/src/workbench/floating/ForwardConversationPicker.test.tsx b/app/workbench/src/floating/ForwardConversationPicker.test.tsx similarity index 99% rename from app/shared/src/workbench/floating/ForwardConversationPicker.test.tsx rename to app/workbench/src/floating/ForwardConversationPicker.test.tsx index 7a32bfe8e..aa6d40d56 100644 --- a/app/shared/src/workbench/floating/ForwardConversationPicker.test.tsx +++ b/app/workbench/src/floating/ForwardConversationPicker.test.tsx @@ -1,7 +1,7 @@ import { describe, it, expect, vi } from 'vitest'; import { fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import type { WorkbenchConversation } from '../../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import { ForwardConversationPicker } from './ForwardConversationPicker'; const conversations: WorkbenchConversation[] = [ diff --git a/app/shared/src/workbench/floating/ForwardConversationPicker.tsx b/app/workbench/src/floating/ForwardConversationPicker.tsx similarity index 98% rename from app/shared/src/workbench/floating/ForwardConversationPicker.tsx rename to app/workbench/src/floating/ForwardConversationPicker.tsx index 2d65b09fb..5e72ec13b 100644 --- a/app/shared/src/workbench/floating/ForwardConversationPicker.tsx +++ b/app/workbench/src/floating/ForwardConversationPicker.tsx @@ -13,7 +13,7 @@ ═══════════════════════════════════════════════════════════════════════ */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import type { WorkbenchConversation } from '../../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import styles from './ForwardConversationPicker.module.css'; export interface ForwardConversationPickerProps { diff --git a/app/shared/src/workbench/floating/MultiSelectBar.module.css b/app/workbench/src/floating/MultiSelectBar.module.css similarity index 100% rename from app/shared/src/workbench/floating/MultiSelectBar.module.css rename to app/workbench/src/floating/MultiSelectBar.module.css diff --git a/app/shared/src/workbench/floating/MultiSelectBar.tsx b/app/workbench/src/floating/MultiSelectBar.tsx similarity index 94% rename from app/shared/src/workbench/floating/MultiSelectBar.tsx rename to app/workbench/src/floating/MultiSelectBar.tsx index fc1bdcbc7..9c3ad196f 100644 --- a/app/shared/src/workbench/floating/MultiSelectBar.tsx +++ b/app/workbench/src/floating/MultiSelectBar.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { DesignNavIcon, type DesignNavIconName } from '../designIcons'; -import { Tooltip } from '../../ui/Tooltip'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from './MultiSelectBar.module.css'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; export interface MultiSelectBarAction { label: string; diff --git a/app/shared/src/workbench/floating/PersonPanel.module.css b/app/workbench/src/floating/PersonPanel.module.css similarity index 100% rename from app/shared/src/workbench/floating/PersonPanel.module.css rename to app/workbench/src/floating/PersonPanel.module.css diff --git a/app/shared/src/workbench/floating/PersonPanel.tsx b/app/workbench/src/floating/PersonPanel.tsx similarity index 100% rename from app/shared/src/workbench/floating/PersonPanel.tsx rename to app/workbench/src/floating/PersonPanel.tsx diff --git a/app/shared/src/workbench/floating/ProfilePopover.module.css b/app/workbench/src/floating/ProfilePopover.module.css similarity index 100% rename from app/shared/src/workbench/floating/ProfilePopover.module.css rename to app/workbench/src/floating/ProfilePopover.module.css diff --git a/app/shared/src/workbench/floating/ProfilePopover.test.tsx b/app/workbench/src/floating/ProfilePopover.test.tsx similarity index 100% rename from app/shared/src/workbench/floating/ProfilePopover.test.tsx rename to app/workbench/src/floating/ProfilePopover.test.tsx diff --git a/app/shared/src/workbench/floating/ProfilePopover.tsx b/app/workbench/src/floating/ProfilePopover.tsx similarity index 99% rename from app/shared/src/workbench/floating/ProfilePopover.tsx rename to app/workbench/src/floating/ProfilePopover.tsx index 73aa87c4a..31c5ea445 100644 --- a/app/shared/src/workbench/floating/ProfilePopover.tsx +++ b/app/workbench/src/floating/ProfilePopover.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useCallback } from 'react'; import { createPortal } from 'react-dom'; -import { useFocusTrap } from '../../ui/focusTrap'; +import { useFocusTrap } from '@shared/ui/focusTrap'; import type { AccountMenuRow, AccountSpace, diff --git a/app/shared/src/workbench/floating/ProfilePopoverHelpers.test.ts b/app/workbench/src/floating/ProfilePopoverHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/floating/ProfilePopoverHelpers.test.ts rename to app/workbench/src/floating/ProfilePopoverHelpers.test.ts diff --git a/app/shared/src/workbench/floating/ProfilePopoverHelpers.ts b/app/workbench/src/floating/ProfilePopoverHelpers.ts similarity index 100% rename from app/shared/src/workbench/floating/ProfilePopoverHelpers.ts rename to app/workbench/src/floating/ProfilePopoverHelpers.ts diff --git a/app/shared/src/workbench/floating/ProfilePopoverParts.tsx b/app/workbench/src/floating/ProfilePopoverParts.tsx similarity index 100% rename from app/shared/src/workbench/floating/ProfilePopoverParts.tsx rename to app/workbench/src/floating/ProfilePopoverParts.tsx diff --git a/app/shared/src/workbench/floating/Toast.module.css b/app/workbench/src/floating/Toast.module.css similarity index 100% rename from app/shared/src/workbench/floating/Toast.module.css rename to app/workbench/src/floating/Toast.module.css diff --git a/app/shared/src/workbench/floating/Toast.test.tsx b/app/workbench/src/floating/Toast.test.tsx similarity index 100% rename from app/shared/src/workbench/floating/Toast.test.tsx rename to app/workbench/src/floating/Toast.test.tsx diff --git a/app/shared/src/workbench/floating/index.ts b/app/workbench/src/floating/index.ts similarity index 100% rename from app/shared/src/workbench/floating/index.ts rename to app/workbench/src/floating/index.ts diff --git a/app/shared/src/workbench/hubDataMapping.test.ts b/app/workbench/src/hubDataMapping.test.ts similarity index 100% rename from app/shared/src/workbench/hubDataMapping.test.ts rename to app/workbench/src/hubDataMapping.test.ts diff --git a/app/shared/src/workbench/hubDataMapping.ts b/app/workbench/src/hubDataMapping.ts similarity index 99% rename from app/shared/src/workbench/hubDataMapping.ts rename to app/workbench/src/hubDataMapping.ts index a427dbd29..6584d37cf 100644 --- a/app/shared/src/workbench/hubDataMapping.ts +++ b/app/workbench/src/hubDataMapping.ts @@ -11,11 +11,11 @@ import type { ContactMember, ProjectInfo } from './pages'; import type { WorkbenchContactsData } from './WorkbenchRoutes'; import type { DocRow } from './pages/DocsPage'; -import type { WorkbenchConversation } from '../platform'; +import type { WorkbenchConversation } from '@shared/platform'; import { isWorkbenchFixtureDataMode, type WorkbenchDataMode, -} from '../demo'; +} from '@shared/demo'; // ── Contact mapping ──────────────────────────────────────────────── diff --git a/app/shared/src/workbench/icon-governance.test.ts b/app/workbench/src/icon-governance.test.ts similarity index 100% rename from app/shared/src/workbench/icon-governance.test.ts rename to app/workbench/src/icon-governance.test.ts diff --git a/app/shared/src/workbench/index.ts b/app/workbench/src/index.ts similarity index 100% rename from app/shared/src/workbench/index.ts rename to app/workbench/src/index.ts diff --git a/app/shared/src/workbench/inspector/BrowserPreview.module.css b/app/workbench/src/inspector/BrowserPreview.module.css similarity index 100% rename from app/shared/src/workbench/inspector/BrowserPreview.module.css rename to app/workbench/src/inspector/BrowserPreview.module.css diff --git a/app/shared/src/workbench/inspector/BrowserPreview.test.ts b/app/workbench/src/inspector/BrowserPreview.test.ts similarity index 100% rename from app/shared/src/workbench/inspector/BrowserPreview.test.ts rename to app/workbench/src/inspector/BrowserPreview.test.ts diff --git a/app/shared/src/workbench/inspector/BrowserPreview.tsx b/app/workbench/src/inspector/BrowserPreview.tsx similarity index 97% rename from app/shared/src/workbench/inspector/BrowserPreview.tsx rename to app/workbench/src/inspector/BrowserPreview.tsx index 933492a2a..d300f2ded 100644 --- a/app/shared/src/workbench/inspector/BrowserPreview.tsx +++ b/app/workbench/src/inspector/BrowserPreview.tsx @@ -2,12 +2,12 @@ import React, { useSyncExternalStore } from 'react'; import { getAppliedAgentHubTheme, type AgentHubTheme, -} from '../../theme'; +} from '@shared/theme'; import { DESIGN_NAV_GLYPH_STROKE_WIDTH, DesignNavIcon } from '../designIcons'; -import { Button } from '../../ui/Button'; -import { Tooltip } from '../../ui/Tooltip'; +import { Button } from '@shared/ui/Button'; +import { Tooltip } from '@shared/ui/Tooltip'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import styles from './BrowserPreview.module.css'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/inspector/FilePreview.module.css b/app/workbench/src/inspector/FilePreview.module.css similarity index 100% rename from app/shared/src/workbench/inspector/FilePreview.module.css rename to app/workbench/src/inspector/FilePreview.module.css diff --git a/app/shared/src/workbench/inspector/FilePreview.rendering.test.tsx b/app/workbench/src/inspector/FilePreview.rendering.test.tsx similarity index 100% rename from app/shared/src/workbench/inspector/FilePreview.rendering.test.tsx rename to app/workbench/src/inspector/FilePreview.rendering.test.tsx diff --git a/app/shared/src/workbench/inspector/FilePreview.tsx b/app/workbench/src/inspector/FilePreview.tsx similarity index 98% rename from app/shared/src/workbench/inspector/FilePreview.tsx rename to app/workbench/src/inspector/FilePreview.tsx index 8325f9832..86716a6b4 100644 --- a/app/shared/src/workbench/inspector/FilePreview.tsx +++ b/app/workbench/src/inspector/FilePreview.tsx @@ -1,10 +1,10 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { languageFromPath } from '../../ui/syntaxHighlight'; +import { languageFromPath } from '@shared/ui/syntaxHighlight'; import { DesignFileIcon, DesignNavIcon, DesignOpenWithIcon } from '../designIcons'; -import { CHATVIEW_I18N_NAMESPACE } from '../../chatview/i18n/resources'; -import { Button } from '../../ui/Button'; -import { Tooltip } from '../../ui/Tooltip'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { Button } from '@shared/ui/Button'; +import { Tooltip } from '@shared/ui/Tooltip'; import { defaultPreviewMode, fileTypeLabel, diff --git a/app/shared/src/workbench/inspector/FilePreviewHelpers.test.ts b/app/workbench/src/inspector/FilePreviewHelpers.test.ts similarity index 98% rename from app/shared/src/workbench/inspector/FilePreviewHelpers.test.ts rename to app/workbench/src/inspector/FilePreviewHelpers.test.ts index c1f4808fd..b539c9dc7 100644 --- a/app/shared/src/workbench/inspector/FilePreviewHelpers.test.ts +++ b/app/workbench/src/inspector/FilePreviewHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import type { PreviewPort } from '../../platform'; +import type { PreviewPort } from '@shared/platform'; import { defaultPreviewMode, diffLineClass, diff --git a/app/shared/src/workbench/inspector/FilePreviewHelpers.ts b/app/workbench/src/inspector/FilePreviewHelpers.ts similarity index 98% rename from app/shared/src/workbench/inspector/FilePreviewHelpers.ts rename to app/workbench/src/inspector/FilePreviewHelpers.ts index c1783fd83..fd340a328 100644 --- a/app/shared/src/workbench/inspector/FilePreviewHelpers.ts +++ b/app/workbench/src/inspector/FilePreviewHelpers.ts @@ -1,5 +1,5 @@ -import type { PreviewPort } from '../../platform'; -import { highlightLine } from '../../ui/syntaxHighlight'; +import type { PreviewPort } from '@shared/platform'; +import { highlightLine } from '@shared/ui/syntaxHighlight'; import type { DesignOpenWithIconName } from '../designIcons'; import styles from './FilePreview.module.css'; diff --git a/app/shared/src/workbench/inspector/FilePreviewParts.tsx b/app/workbench/src/inspector/FilePreviewParts.tsx similarity index 94% rename from app/shared/src/workbench/inspector/FilePreviewParts.tsx rename to app/workbench/src/inspector/FilePreviewParts.tsx index dfcf1b6cf..eb0fc66e3 100644 --- a/app/shared/src/workbench/inspector/FilePreviewParts.tsx +++ b/app/workbench/src/inspector/FilePreviewParts.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { highlightLine } from '../../ui/syntaxHighlight'; -import MarkdownContent from '../../ui/Markdown'; -import { PREVIEW_SANDBOX_SRCDOC } from '../../ui/previewSandbox'; +import { highlightLine } from '@shared/ui/syntaxHighlight'; +import MarkdownContent from '@shared/ui/Markdown'; +import { PREVIEW_SANDBOX_SRCDOC } from '@shared/ui/previewSandbox'; import { DesignFileIcon } from '../designIcons'; -import { CHATVIEW_I18N_NAMESPACE } from '../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { diffLineClass, highlightDiffLine } from './FilePreviewHelpers'; import styles from './FilePreview.module.css'; diff --git a/app/shared/src/workbench/inspector/FilePreviewRouter.apply.test.tsx b/app/workbench/src/inspector/FilePreviewRouter.apply.test.tsx similarity index 98% rename from app/shared/src/workbench/inspector/FilePreviewRouter.apply.test.tsx rename to app/workbench/src/inspector/FilePreviewRouter.apply.test.tsx index badd5e735..138bfce21 100644 --- a/app/shared/src/workbench/inspector/FilePreviewRouter.apply.test.tsx +++ b/app/workbench/src/inspector/FilePreviewRouter.apply.test.tsx @@ -6,8 +6,8 @@ // degrade to explicit read-only feedback instead of silent console errors. import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import type { PreviewPort } from '../../platform'; -import { useToastStore } from '../../ui/toast/toastStore'; +import type { PreviewPort } from '@shared/platform'; +import { useToastStore } from '@shared/ui/toast/toastStore'; import { FilePreviewRouter, type PreviewFile } from './FilePreviewRouter'; function interactiveDiffFile(): PreviewFile { diff --git a/app/shared/src/workbench/inspector/FilePreviewRouter.tsx b/app/workbench/src/inspector/FilePreviewRouter.tsx similarity index 96% rename from app/shared/src/workbench/inspector/FilePreviewRouter.tsx rename to app/workbench/src/inspector/FilePreviewRouter.tsx index 23f929775..1efe72fce 100644 --- a/app/shared/src/workbench/inspector/FilePreviewRouter.tsx +++ b/app/workbench/src/inspector/FilePreviewRouter.tsx @@ -1,16 +1,16 @@ import React, { useCallback, useMemo, useState } from 'react'; -import type { PreviewPort, RuntimeEvidenceContentRef } from '../../platform'; -import type { FileDiff } from '../../types/chat'; +import type { PreviewPort, RuntimeEvidenceContentRef } from '@shared/platform'; +import type { FileDiff } from '@shared/types/chat'; import { DiffReviewPanel, type DiffHunkDecision, type DiffReviewFile, -} from '../../ui/DiffReviewPanel'; -import { DocxPreview } from '../../ui/DocxPreview'; -import { SlideshowPreview } from '../../ui/SlideshowPreview'; -import { TablePreview } from '../../ui/TablePreview'; -import { PREVIEW_SANDBOX_SRCDOC } from '../../ui/previewSandbox'; -import { useToastStore } from '../../ui/toast/toastStore'; +} from '@shared/ui/DiffReviewPanel'; +import { DocxPreview } from '@shared/ui/DocxPreview'; +import { SlideshowPreview } from '@shared/ui/SlideshowPreview'; +import { TablePreview } from '@shared/ui/TablePreview'; +import { PREVIEW_SANDBOX_SRCDOC } from '@shared/ui/previewSandbox'; +import { useToastStore } from '@shared/ui/toast/toastStore'; import { DesignFileIcon } from '../designIcons'; import styles from '../AgentHubWorkbench.module.css'; import { FilePreview } from './FilePreview'; diff --git a/app/shared/src/workbench/inspector/InspectorModeBodies.tsx b/app/workbench/src/inspector/InspectorModeBodies.tsx similarity index 93% rename from app/shared/src/workbench/inspector/InspectorModeBodies.tsx rename to app/workbench/src/inspector/InspectorModeBodies.tsx index c41c32578..b42807ce5 100644 --- a/app/shared/src/workbench/inspector/InspectorModeBodies.tsx +++ b/app/workbench/src/inspector/InspectorModeBodies.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import type { EvidenceRef, ContextUsageTranscriptBlock } from '../../transcript'; -import type { RuntimeEvidenceSnapshot } from '../../inspector'; -import type { PreviewPort } from '../../platform'; -import type { FileDiff } from '../../types/chat'; -import { AgentStreamingBar } from '../../ui/AgentStreamingBar'; -import type { DagNode } from '../../ui/DagTree'; +import type { EvidenceRef, ContextUsageTranscriptBlock } from '@shared/transcript'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { PreviewPort } from '@shared/platform'; +import type { FileDiff } from '@shared/types/chat'; +import { AgentStreamingBar } from '@shared/ui/AgentStreamingBar'; +import type { DagNode } from '@shared/ui/DagTree'; import styles from '../AgentHubWorkbench.module.css'; import { BrowserPreview } from './BrowserPreview'; import { FilePreviewRouter, type PreviewFile } from './FilePreviewRouter'; diff --git a/app/shared/src/workbench/inspector/InspectorModePanelHelpers.test.ts b/app/workbench/src/inspector/InspectorModePanelHelpers.test.ts similarity index 98% rename from app/shared/src/workbench/inspector/InspectorModePanelHelpers.test.ts rename to app/workbench/src/inspector/InspectorModePanelHelpers.test.ts index 1c9fd3a62..f122365ec 100644 --- a/app/shared/src/workbench/inspector/InspectorModePanelHelpers.test.ts +++ b/app/workbench/src/inspector/InspectorModePanelHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import type { ContextUsageTranscriptBlock, EvidenceRef } from '../../transcript'; +import type { ContextUsageTranscriptBlock, EvidenceRef } from '@shared/transcript'; import { canOpenEvidence, contextBarFillWidth, diff --git a/app/shared/src/workbench/inspector/InspectorModePanelHelpers.ts b/app/workbench/src/inspector/InspectorModePanelHelpers.ts similarity index 98% rename from app/shared/src/workbench/inspector/InspectorModePanelHelpers.ts rename to app/workbench/src/inspector/InspectorModePanelHelpers.ts index 66e81e5d1..937b6af78 100644 --- a/app/shared/src/workbench/inspector/InspectorModePanelHelpers.ts +++ b/app/workbench/src/inspector/InspectorModePanelHelpers.ts @@ -1,4 +1,4 @@ -import type { EvidenceRef, ContextUsageTranscriptBlock } from '../../transcript'; +import type { EvidenceRef, ContextUsageTranscriptBlock } from '@shared/transcript'; import type { PreviewFile } from './FilePreviewRouter'; import type { TaskItem } from './OverviewPanel'; diff --git a/app/shared/src/workbench/inspector/InspectorModePanelParts.tsx b/app/workbench/src/inspector/InspectorModePanelParts.tsx similarity index 98% rename from app/shared/src/workbench/inspector/InspectorModePanelParts.tsx rename to app/workbench/src/inspector/InspectorModePanelParts.tsx index 4692461fb..c6b9077ed 100644 --- a/app/shared/src/workbench/inspector/InspectorModePanelParts.tsx +++ b/app/workbench/src/inspector/InspectorModePanelParts.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { EvidenceRef, ContextUsageTranscriptBlock } from '../../transcript'; -import { formatTokens } from '../../context/breakdown'; +import type { EvidenceRef, ContextUsageTranscriptBlock } from '@shared/transcript'; +import { formatTokens } from '@shared/context/breakdown'; import { DesignFileIcon, DesignNavIcon, diff --git a/app/shared/src/workbench/inspector/InspectorModePanels.tsx b/app/workbench/src/inspector/InspectorModePanels.tsx similarity index 100% rename from app/shared/src/workbench/inspector/InspectorModePanels.tsx rename to app/workbench/src/inspector/InspectorModePanels.tsx diff --git a/app/shared/src/workbench/inspector/InspectorTabChrome.test.ts b/app/workbench/src/inspector/InspectorTabChrome.test.ts similarity index 100% rename from app/shared/src/workbench/inspector/InspectorTabChrome.test.ts rename to app/workbench/src/inspector/InspectorTabChrome.test.ts diff --git a/app/shared/src/workbench/inspector/InspectorTabChrome.tsx b/app/workbench/src/inspector/InspectorTabChrome.tsx similarity index 99% rename from app/shared/src/workbench/inspector/InspectorTabChrome.tsx rename to app/workbench/src/inspector/InspectorTabChrome.tsx index 6937dafbd..3953a29f7 100644 --- a/app/shared/src/workbench/inspector/InspectorTabChrome.tsx +++ b/app/workbench/src/inspector/InspectorTabChrome.tsx @@ -5,7 +5,7 @@ import { DesignNavIcon, type DesignNavIconName, } from '../designIcons'; -import { Tooltip } from '../../ui/Tooltip'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from '../AgentHubWorkbench.module.css'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/inspector/OverviewPanel.module.css b/app/workbench/src/inspector/OverviewPanel.module.css similarity index 100% rename from app/shared/src/workbench/inspector/OverviewPanel.module.css rename to app/workbench/src/inspector/OverviewPanel.module.css diff --git a/app/shared/src/workbench/inspector/OverviewPanel.tsx b/app/workbench/src/inspector/OverviewPanel.tsx similarity index 98% rename from app/shared/src/workbench/inspector/OverviewPanel.tsx rename to app/workbench/src/inspector/OverviewPanel.tsx index db3aa24c2..66859907b 100644 --- a/app/shared/src/workbench/inspector/OverviewPanel.tsx +++ b/app/workbench/src/inspector/OverviewPanel.tsx @@ -1,6 +1,6 @@ import React, { useState, useCallback } from 'react'; -import type { DagNode } from '../../ui/DagTree'; -import { DagTree } from '../../ui/DagTree'; +import type { DagNode } from '@shared/ui/DagTree'; +import { DagTree } from '@shared/ui/DagTree'; import { DesignFileIcon, DesignNavIcon } from '../designIcons'; import styles from './OverviewPanel.module.css'; diff --git a/app/shared/src/workbench/inspector/RuntimeEvidenceHelpers.test.ts b/app/workbench/src/inspector/RuntimeEvidenceHelpers.test.ts similarity index 97% rename from app/shared/src/workbench/inspector/RuntimeEvidenceHelpers.test.ts rename to app/workbench/src/inspector/RuntimeEvidenceHelpers.test.ts index 99089437e..1cb95db92 100644 --- a/app/shared/src/workbench/inspector/RuntimeEvidenceHelpers.test.ts +++ b/app/workbench/src/inspector/RuntimeEvidenceHelpers.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import type { RuntimeEvidenceSnapshot } from '../../inspector'; -import type { Artifact, Preview } from '../../types'; -import type { FileDiff } from '../../types/chat'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { Artifact, Preview } from '@shared/types'; +import type { FileDiff } from '@shared/types/chat'; import { artifactWorkspaceDiffLabel, artifactWorkspacePreviewStatus, diff --git a/app/shared/src/workbench/inspector/RuntimeEvidenceHelpers.ts b/app/workbench/src/inspector/RuntimeEvidenceHelpers.ts similarity index 97% rename from app/shared/src/workbench/inspector/RuntimeEvidenceHelpers.ts rename to app/workbench/src/inspector/RuntimeEvidenceHelpers.ts index 3f5976475..f4825951c 100644 --- a/app/shared/src/workbench/inspector/RuntimeEvidenceHelpers.ts +++ b/app/workbench/src/inspector/RuntimeEvidenceHelpers.ts @@ -1,6 +1,6 @@ -import type { RuntimeEvidenceSnapshot } from '../../inspector'; -import type { RuntimeEvidenceContentRef } from '../../platform'; -import type { FileDiff } from '../../types/chat'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { RuntimeEvidenceContentRef } from '@shared/platform'; +import type { FileDiff } from '@shared/types/chat'; import type { PreviewFile } from './FilePreviewRouter'; import type { TaskItem } from './OverviewPanel'; diff --git a/app/shared/src/workbench/inspector/RuntimeEvidencePanel.tsx b/app/workbench/src/inspector/RuntimeEvidencePanel.tsx similarity index 94% rename from app/shared/src/workbench/inspector/RuntimeEvidencePanel.tsx rename to app/workbench/src/inspector/RuntimeEvidencePanel.tsx index 8865e0712..fed9f6c21 100644 --- a/app/shared/src/workbench/inspector/RuntimeEvidencePanel.tsx +++ b/app/workbench/src/inspector/RuntimeEvidencePanel.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { buildRuntimeEvidenceInspectorModel } from '../../inspector'; -import type { RuntimeEvidenceSnapshot } from '../../inspector'; -import type { FileDiff } from '../../types/chat'; +import { buildRuntimeEvidenceInspectorModel } from '@shared/inspector'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { FileDiff } from '@shared/types/chat'; import styles from '../AgentHubWorkbench.module.css'; import { RuntimeEvidenceArtifactsSection, diff --git a/app/shared/src/workbench/inspector/RuntimeEvidenceParts.tsx b/app/workbench/src/inspector/RuntimeEvidenceParts.tsx similarity index 98% rename from app/shared/src/workbench/inspector/RuntimeEvidenceParts.tsx rename to app/workbench/src/inspector/RuntimeEvidenceParts.tsx index 284f0e20c..3eb7ff3f4 100644 --- a/app/shared/src/workbench/inspector/RuntimeEvidenceParts.tsx +++ b/app/workbench/src/inspector/RuntimeEvidenceParts.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import type { RuntimeEvidenceChannel, RuntimeEvidenceSnapshot } from '../../inspector'; -import type { FileDiff } from '../../types/chat'; +import type { RuntimeEvidenceChannel, RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { FileDiff } from '@shared/types/chat'; import { DesignFileIcon, DesignNavIcon } from '../designIcons'; import styles from '../AgentHubWorkbench.module.css'; import { diff --git a/app/shared/src/workbench/inspector/index.ts b/app/workbench/src/inspector/index.ts similarity index 100% rename from app/shared/src/workbench/inspector/index.ts rename to app/workbench/src/inspector/index.ts diff --git a/app/shared/src/workbench/mainchain/buildMainchainSummary.test.ts b/app/workbench/src/mainchain/buildMainchainSummary.test.ts similarity index 99% rename from app/shared/src/workbench/mainchain/buildMainchainSummary.test.ts rename to app/workbench/src/mainchain/buildMainchainSummary.test.ts index 23c5d12cb..b22aee106 100644 --- a/app/shared/src/workbench/mainchain/buildMainchainSummary.test.ts +++ b/app/workbench/src/mainchain/buildMainchainSummary.test.ts @@ -1,6 +1,6 @@ // real_tested=true import { describe, expect, it } from 'vitest'; -import type { AgentHubPlatform } from '../../platform'; +import type { AgentHubPlatform } from '@shared/platform'; import type { AgentTimelineTranscriptBlock, ApprovalTranscriptBlock, @@ -14,10 +14,10 @@ import type { SubtaskTranscriptBlock, TextTranscriptBlock, ToolCallTranscriptBlock, -} from '../../transcript'; -import type { Artifact, Preview } from '../../types'; -import type { FileDiff } from '../../types/chat'; -import type { RuntimeEvidenceSnapshot } from '../../inspector'; +} from '@shared/transcript'; +import type { Artifact, Preview } from '@shared/types'; +import type { FileDiff } from '@shared/types/chat'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; import { buildMainchainSummary, runtimeEvidenceSourceSummary } from './buildMainchainSummary'; import type { MainchainSummary } from './types'; diff --git a/app/shared/src/workbench/mainchain/buildMainchainSummary.ts b/app/workbench/src/mainchain/buildMainchainSummary.ts similarity index 98% rename from app/shared/src/workbench/mainchain/buildMainchainSummary.ts rename to app/workbench/src/mainchain/buildMainchainSummary.ts index b5ce54a1e..db73ae120 100644 --- a/app/shared/src/workbench/mainchain/buildMainchainSummary.ts +++ b/app/workbench/src/mainchain/buildMainchainSummary.ts @@ -1,5 +1,5 @@ -import type { AgentHubPlatform } from '../../platform'; -import type { EvidenceRef, TranscriptBlock } from '../../transcript'; +import type { AgentHubPlatform } from '@shared/platform'; +import type { EvidenceRef, TranscriptBlock } from '@shared/transcript'; import type { RuntimeEvidenceSnapshot } from '../RightInspector'; import type { MainchainNode, MainchainSummary } from './types'; diff --git a/app/shared/src/workbench/mainchain/index.ts b/app/workbench/src/mainchain/index.ts similarity index 100% rename from app/shared/src/workbench/mainchain/index.ts rename to app/workbench/src/mainchain/index.ts diff --git a/app/shared/src/workbench/mainchain/types.ts b/app/workbench/src/mainchain/types.ts similarity index 100% rename from app/shared/src/workbench/mainchain/types.ts rename to app/workbench/src/mainchain/types.ts diff --git a/app/shared/src/workbench/mockData.ts b/app/workbench/src/mockData.ts similarity index 100% rename from app/shared/src/workbench/mockData.ts rename to app/workbench/src/mockData.ts diff --git a/app/shared/src/workbench/pages/AgentsPage.module.css b/app/workbench/src/pages/AgentsPage.module.css similarity index 100% rename from app/shared/src/workbench/pages/AgentsPage.module.css rename to app/workbench/src/pages/AgentsPage.module.css diff --git a/app/shared/src/workbench/pages/AgentsPage.test.tsx b/app/workbench/src/pages/AgentsPage.test.tsx similarity index 98% rename from app/shared/src/workbench/pages/AgentsPage.test.tsx rename to app/workbench/src/pages/AgentsPage.test.tsx index 12bf65d03..983ec8ea1 100644 --- a/app/shared/src/workbench/pages/AgentsPage.test.tsx +++ b/app/workbench/src/pages/AgentsPage.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { beforeAll, describe, expect, it, vi } from 'vitest'; -import { fireEvent, render, screen, within } from '../../__tests__/setup'; +import { fireEvent, render, screen, within } from '../__tests__/setup'; import { WORKBENCH_AGENT_MARKET_FIXTURES, WORKBENCH_AGENT_PROFILE_FIXTURES, @@ -13,7 +13,7 @@ import { AgentsPage } from './AgentsPage'; // the registered test instance provides the real sharedWorkbench zh strings // once this suite opts into zh (Issue #1717). Other AgentsPage text is // hardcoded in components. -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/pages/AgentsPage.tsx b/app/workbench/src/pages/AgentsPage.tsx similarity index 98% rename from app/shared/src/workbench/pages/AgentsPage.tsx rename to app/workbench/src/pages/AgentsPage.tsx index 9b976eb98..248addfe3 100644 --- a/app/shared/src/workbench/pages/AgentsPage.tsx +++ b/app/workbench/src/pages/AgentsPage.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignNavIcon, DESIGN_NAV_GLYPH_SIZE, diff --git a/app/shared/src/workbench/pages/ContactsPage.module.css b/app/workbench/src/pages/ContactsPage.module.css similarity index 100% rename from app/shared/src/workbench/pages/ContactsPage.module.css rename to app/workbench/src/pages/ContactsPage.module.css diff --git a/app/shared/src/workbench/pages/ContactsPage.test.tsx b/app/workbench/src/pages/ContactsPage.test.tsx similarity index 97% rename from app/shared/src/workbench/pages/ContactsPage.test.tsx rename to app/workbench/src/pages/ContactsPage.test.tsx index ce42d5b2c..abb37f089 100644 --- a/app/shared/src/workbench/pages/ContactsPage.test.tsx +++ b/app/workbench/src/pages/ContactsPage.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { describe, expect, it, vi } from 'vitest'; -import { fireEvent, render, screen } from '../../__tests__/setup'; +import { fireEvent, render, screen } from '../__tests__/setup'; import { ContactsPage } from './ContactsPage'; describe('ContactsPage empty states', () => { diff --git a/app/shared/src/workbench/pages/ContactsPage.tsx b/app/workbench/src/pages/ContactsPage.tsx similarity index 100% rename from app/shared/src/workbench/pages/ContactsPage.tsx rename to app/workbench/src/pages/ContactsPage.tsx diff --git a/app/shared/src/workbench/pages/DocsPage.module.css b/app/workbench/src/pages/DocsPage.module.css similarity index 100% rename from app/shared/src/workbench/pages/DocsPage.module.css rename to app/workbench/src/pages/DocsPage.module.css diff --git a/app/shared/src/workbench/pages/DocsPage.test.tsx b/app/workbench/src/pages/DocsPage.test.tsx similarity index 93% rename from app/shared/src/workbench/pages/DocsPage.test.tsx rename to app/workbench/src/pages/DocsPage.test.tsx index 9b27eeb4f..fb55d710f 100644 --- a/app/shared/src/workbench/pages/DocsPage.test.tsx +++ b/app/workbench/src/pages/DocsPage.test.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { beforeAll, describe, expect, it, vi } from 'vitest'; -import { fireEvent, render, screen, within } from '../../__tests__/setup'; +import { fireEvent, render, screen, within } from '../__tests__/setup'; import { DocsPage } from './DocsPage'; // Docs copy resolves via the sharedWorkbench namespace; opt into the zh // bundle of the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/pages/DocsPage.tsx b/app/workbench/src/pages/DocsPage.tsx similarity index 100% rename from app/shared/src/workbench/pages/DocsPage.tsx rename to app/workbench/src/pages/DocsPage.tsx diff --git a/app/shared/src/workbench/pages/ProjectsPage.module.css b/app/workbench/src/pages/ProjectsPage.module.css similarity index 100% rename from app/shared/src/workbench/pages/ProjectsPage.module.css rename to app/workbench/src/pages/ProjectsPage.module.css diff --git a/app/shared/src/workbench/pages/ProjectsPage.test.tsx b/app/workbench/src/pages/ProjectsPage.test.tsx similarity index 98% rename from app/shared/src/workbench/pages/ProjectsPage.test.tsx rename to app/workbench/src/pages/ProjectsPage.test.tsx index fd761d01e..30130cb93 100644 --- a/app/shared/src/workbench/pages/ProjectsPage.test.tsx +++ b/app/workbench/src/pages/ProjectsPage.test.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest'; -import { fireEvent, render, screen, within } from '../../__tests__/setup'; +import { fireEvent, render, screen, within } from '../__tests__/setup'; import { ProjectsPage } from './ProjectsPage'; import { DEFAULT_PROJECTS } from './projects'; import type { ProjectInfo, ProjectsPageProps } from './projects'; // Projects copy resolves via the sharedWorkbench namespace; opt into the zh // bundle of the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/pages/ProjectsPage.tsx b/app/workbench/src/pages/ProjectsPage.tsx similarity index 98% rename from app/shared/src/workbench/pages/ProjectsPage.tsx rename to app/workbench/src/pages/ProjectsPage.tsx index f1f410f4f..6f7768064 100644 --- a/app/shared/src/workbench/pages/ProjectsPage.tsx +++ b/app/workbench/src/pages/ProjectsPage.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import styles from './ProjectsPage.module.css'; -import { applyFolderThemeColor } from '../../folderThemeColors'; +import { applyFolderThemeColor } from '@shared/folderThemeColors'; import { ProjectMain, ProjectNav, diff --git a/app/shared/src/workbench/pages/SettingsPage.module.css b/app/workbench/src/pages/SettingsPage.module.css similarity index 100% rename from app/shared/src/workbench/pages/SettingsPage.module.css rename to app/workbench/src/pages/SettingsPage.module.css diff --git a/app/shared/src/workbench/pages/SettingsPage.test.tsx b/app/workbench/src/pages/SettingsPage.test.tsx similarity index 97% rename from app/shared/src/workbench/pages/SettingsPage.test.tsx rename to app/workbench/src/pages/SettingsPage.test.tsx index a5312cd42..9c9adce69 100644 --- a/app/shared/src/workbench/pages/SettingsPage.test.tsx +++ b/app/workbench/src/pages/SettingsPage.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { describe, expect, it, vi } from 'vitest'; -import { render, screen } from '../../__tests__/setup'; +import { render, screen } from '../__tests__/setup'; import { SettingsPage, type SettingsPageProps } from './SettingsPage'; const BASE_PROPS: SettingsPageProps = { diff --git a/app/shared/src/workbench/pages/SettingsPage.tsx b/app/workbench/src/pages/SettingsPage.tsx similarity index 100% rename from app/shared/src/workbench/pages/SettingsPage.tsx rename to app/workbench/src/pages/SettingsPage.tsx diff --git a/app/shared/src/workbench/pages/TasksPage.module.css b/app/workbench/src/pages/TasksPage.module.css similarity index 100% rename from app/shared/src/workbench/pages/TasksPage.module.css rename to app/workbench/src/pages/TasksPage.module.css diff --git a/app/shared/src/workbench/pages/TasksPage.test.tsx b/app/workbench/src/pages/TasksPage.test.tsx similarity index 96% rename from app/shared/src/workbench/pages/TasksPage.test.tsx rename to app/workbench/src/pages/TasksPage.test.tsx index ade486f20..87ba6d387 100644 --- a/app/shared/src/workbench/pages/TasksPage.test.tsx +++ b/app/workbench/src/pages/TasksPage.test.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { beforeAll, describe, expect, it, vi } from 'vitest'; -import { fireEvent, render, screen, within } from '../../__tests__/setup'; +import { fireEvent, render, screen, within } from '../__tests__/setup'; import { TasksPage } from './TasksPage'; // Tasks copy resolves via the sharedWorkbench namespace; opt into the zh // bundle of the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/pages/TasksPage.tsx b/app/workbench/src/pages/TasksPage.tsx similarity index 100% rename from app/shared/src/workbench/pages/TasksPage.tsx rename to app/workbench/src/pages/TasksPage.tsx diff --git a/app/shared/src/workbench/pages/agents/AgentEditHelpers.test.ts b/app/workbench/src/pages/agents/AgentEditHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentEditHelpers.test.ts rename to app/workbench/src/pages/agents/AgentEditHelpers.test.ts diff --git a/app/shared/src/workbench/pages/agents/AgentEditHelpers.ts b/app/workbench/src/pages/agents/AgentEditHelpers.ts similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentEditHelpers.ts rename to app/workbench/src/pages/agents/AgentEditHelpers.ts diff --git a/app/shared/src/workbench/pages/agents/AgentEditItemParts.tsx b/app/workbench/src/pages/agents/AgentEditItemParts.tsx similarity index 99% rename from app/shared/src/workbench/pages/agents/AgentEditItemParts.tsx rename to app/workbench/src/pages/agents/AgentEditItemParts.tsx index 548e7164e..7c20d95be 100644 --- a/app/shared/src/workbench/pages/agents/AgentEditItemParts.tsx +++ b/app/workbench/src/pages/agents/AgentEditItemParts.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { RuntimeBrandIcon } from '../../RuntimeBrandIcon'; import { agentConfigToAgentSpecFixture } from '../../agentProfileCatalog'; -import { formatAgentHubAgentSpecV1 } from '../../../agentSpec'; -import { Button, Select } from '../../../ui'; +import { formatAgentHubAgentSpecV1 } from '@shared/agentSpec'; +import { Button, Select } from '@shared/ui'; import styles from '../AgentsPage.module.css'; import { ConfigSummaryRow, formatList } from './shared'; import { diff --git a/app/shared/src/workbench/pages/agents/AgentEditPanel.tsx b/app/workbench/src/pages/agents/AgentEditPanel.tsx similarity index 98% rename from app/shared/src/workbench/pages/agents/AgentEditPanel.tsx rename to app/workbench/src/pages/agents/AgentEditPanel.tsx index c742d2664..cf8843de4 100644 --- a/app/shared/src/workbench/pages/agents/AgentEditPanel.tsx +++ b/app/workbench/src/pages/agents/AgentEditPanel.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { DesignNavIcon } from '../../designIcons'; import { buildAgentCapabilityContractFromConfig, buildAgentCapabilitySummary } from '../../agentCapabilities'; -import { StatusNotice } from '../../../ui'; +import { StatusNotice } from '@shared/ui'; import styles from '../AgentsPage.module.css'; import { AgentCapabilityStrip, AgentDetailHead, AgentEditActions, AgentEditGrid, AgentMcpMemorySection, AgentMiniLog, AgentRuntimeLine, AgentSkillChipGrid, AgentToolPermissions } from './AgentEditItemParts'; import { buildStatusNoticeClassName } from './AgentEditHelpers'; diff --git a/app/shared/src/workbench/pages/agents/AgentInstalledParts.tsx b/app/workbench/src/pages/agents/AgentInstalledParts.tsx similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentInstalledParts.tsx rename to app/workbench/src/pages/agents/AgentInstalledParts.tsx diff --git a/app/shared/src/workbench/pages/agents/AgentInstalledViews.test.tsx b/app/workbench/src/pages/agents/AgentInstalledViews.test.tsx similarity index 97% rename from app/shared/src/workbench/pages/agents/AgentInstalledViews.test.tsx rename to app/workbench/src/pages/agents/AgentInstalledViews.test.tsx index 4dace0486..bfb05d372 100644 --- a/app/shared/src/workbench/pages/agents/AgentInstalledViews.test.tsx +++ b/app/workbench/src/pages/agents/AgentInstalledViews.test.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { beforeAll, describe, expect, it, vi } from 'vitest'; -import { render, screen, within } from '../../../__tests__/setup'; +import { render, screen, within } from '../../__tests__/setup'; import { AgentInstalledView } from './AgentInstalledViews'; import type { AgentConfig, AgentsPageProps } from './types'; // Empty-state copy resolves via the sharedWorkbench namespace; opt into the // zh bundle of the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/pages/agents/AgentInstalledViews.tsx b/app/workbench/src/pages/agents/AgentInstalledViews.tsx similarity index 98% rename from app/shared/src/workbench/pages/agents/AgentInstalledViews.tsx rename to app/workbench/src/pages/agents/AgentInstalledViews.tsx index 16c6fe005..58a2336f3 100644 --- a/app/shared/src/workbench/pages/agents/AgentInstalledViews.tsx +++ b/app/workbench/src/pages/agents/AgentInstalledViews.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignNavIcon } from '../../designIcons'; -import { EmptyState, RecoveryPanel, StatusNotice } from '../../../ui'; -import { SkeletonBar } from '../../../ui/SkeletonBar'; +import { EmptyState, RecoveryPanel, StatusNotice } from '@shared/ui'; +import { SkeletonBar } from '@shared/ui/SkeletonBar'; import styles from '../AgentsPage.module.css'; import { AgentEditPanel } from './AgentEditPanel'; import { diff --git a/app/shared/src/workbench/pages/agents/AgentMarketHelpers.test.ts b/app/workbench/src/pages/agents/AgentMarketHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentMarketHelpers.test.ts rename to app/workbench/src/pages/agents/AgentMarketHelpers.test.ts diff --git a/app/shared/src/workbench/pages/agents/AgentMarketHelpers.ts b/app/workbench/src/pages/agents/AgentMarketHelpers.ts similarity index 99% rename from app/shared/src/workbench/pages/agents/AgentMarketHelpers.ts rename to app/workbench/src/pages/agents/AgentMarketHelpers.ts index e797a8a36..e7c318de6 100644 --- a/app/shared/src/workbench/pages/agents/AgentMarketHelpers.ts +++ b/app/workbench/src/pages/agents/AgentMarketHelpers.ts @@ -1,5 +1,5 @@ import type { MarketTemplate, MCPMarketItem, SkillMarketItem } from './types'; -import type { EmptyStateKind } from '../../../ui'; +import type { EmptyStateKind } from '@shared/ui'; /* ═══════════════════════════════════════════════════════════════════════ AgentMarketHelpers — pure residual slices from AgentMarketParts (#671). diff --git a/app/shared/src/workbench/pages/agents/AgentMarketItemParts.tsx b/app/workbench/src/pages/agents/AgentMarketItemParts.tsx similarity index 99% rename from app/shared/src/workbench/pages/agents/AgentMarketItemParts.tsx rename to app/workbench/src/pages/agents/AgentMarketItemParts.tsx index 5d4c6dd4b..85ad78f35 100644 --- a/app/shared/src/workbench/pages/agents/AgentMarketItemParts.tsx +++ b/app/workbench/src/pages/agents/AgentMarketItemParts.tsx @@ -7,7 +7,7 @@ import { type EmptyStateAction, type EmptyStateCopyMatrix, type EmptyStateKind, -} from '../../../ui'; +} from '@shared/ui'; import styles from '../AgentsPage.module.css'; import { ConfigSummaryRow, formatList } from './shared'; import { diff --git a/app/shared/src/workbench/pages/agents/AgentMarketParts.tsx b/app/workbench/src/pages/agents/AgentMarketParts.tsx similarity index 99% rename from app/shared/src/workbench/pages/agents/AgentMarketParts.tsx rename to app/workbench/src/pages/agents/AgentMarketParts.tsx index 7fcf9d50f..97165e737 100644 --- a/app/shared/src/workbench/pages/agents/AgentMarketParts.tsx +++ b/app/workbench/src/pages/agents/AgentMarketParts.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import type { EmptyStateAction, EmptyStateCopyMatrix } from '../../../ui'; +import type { EmptyStateAction, EmptyStateCopyMatrix } from '@shared/ui'; import styles from '../AgentsPage.module.css'; import { MarketCard, diff --git a/app/shared/src/workbench/pages/agents/AgentMarketViews.tsx b/app/workbench/src/pages/agents/AgentMarketViews.tsx similarity index 99% rename from app/shared/src/workbench/pages/agents/AgentMarketViews.tsx rename to app/workbench/src/pages/agents/AgentMarketViews.tsx index c3e17c4ba..d03c42efa 100644 --- a/app/shared/src/workbench/pages/agents/AgentMarketViews.tsx +++ b/app/workbench/src/pages/agents/AgentMarketViews.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import styles from '../AgentsPage.module.css'; import { AgentMarketToolbar, diff --git a/app/shared/src/workbench/pages/agents/AgentOpsHelpers.test.ts b/app/workbench/src/pages/agents/AgentOpsHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentOpsHelpers.test.ts rename to app/workbench/src/pages/agents/AgentOpsHelpers.test.ts diff --git a/app/shared/src/workbench/pages/agents/AgentOpsHelpers.ts b/app/workbench/src/pages/agents/AgentOpsHelpers.ts similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentOpsHelpers.ts rename to app/workbench/src/pages/agents/AgentOpsHelpers.ts diff --git a/app/shared/src/workbench/pages/agents/AgentOpsItemParts.tsx b/app/workbench/src/pages/agents/AgentOpsItemParts.tsx similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentOpsItemParts.tsx rename to app/workbench/src/pages/agents/AgentOpsItemParts.tsx diff --git a/app/shared/src/workbench/pages/agents/AgentOpsParts.tsx b/app/workbench/src/pages/agents/AgentOpsParts.tsx similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentOpsParts.tsx rename to app/workbench/src/pages/agents/AgentOpsParts.tsx diff --git a/app/shared/src/workbench/pages/agents/AgentOpsViews.tsx b/app/workbench/src/pages/agents/AgentOpsViews.tsx similarity index 100% rename from app/shared/src/workbench/pages/agents/AgentOpsViews.tsx rename to app/workbench/src/pages/agents/AgentOpsViews.tsx diff --git a/app/shared/src/workbench/pages/agents/index.ts b/app/workbench/src/pages/agents/index.ts similarity index 100% rename from app/shared/src/workbench/pages/agents/index.ts rename to app/workbench/src/pages/agents/index.ts diff --git a/app/shared/src/workbench/pages/agents/shared.tsx b/app/workbench/src/pages/agents/shared.tsx similarity index 100% rename from app/shared/src/workbench/pages/agents/shared.tsx rename to app/workbench/src/pages/agents/shared.tsx diff --git a/app/shared/src/workbench/pages/agents/types.ts b/app/workbench/src/pages/agents/types.ts similarity index 100% rename from app/shared/src/workbench/pages/agents/types.ts rename to app/workbench/src/pages/agents/types.ts diff --git a/app/shared/src/workbench/pages/contacts/AddContactModal.test.tsx b/app/workbench/src/pages/contacts/AddContactModal.test.tsx similarity index 98% rename from app/shared/src/workbench/pages/contacts/AddContactModal.test.tsx rename to app/workbench/src/pages/contacts/AddContactModal.test.tsx index 5299c0c44..a7017ad05 100644 --- a/app/shared/src/workbench/pages/contacts/AddContactModal.test.tsx +++ b/app/workbench/src/pages/contacts/AddContactModal.test.tsx @@ -2,7 +2,7 @@ import { describe, it, expect, vi, beforeAll } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/vitest'; import { AddContactModal } from './AddContactModal'; -import { useTestI18nLanguage } from '../../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/pages/contacts/AddContactModal.tsx b/app/workbench/src/pages/contacts/AddContactModal.tsx similarity index 98% rename from app/shared/src/workbench/pages/contacts/AddContactModal.tsx rename to app/workbench/src/pages/contacts/AddContactModal.tsx index 3b5278391..8f186f35c 100644 --- a/app/shared/src/workbench/pages/contacts/AddContactModal.tsx +++ b/app/workbench/src/pages/contacts/AddContactModal.tsx @@ -1,12 +1,12 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { DesignNavIcon } from '../../designIcons'; -import { Select } from '../../../ui'; -import { useFocusTrap } from '../../../ui/focusTrap'; +import { Select } from '@shared/ui'; +import { useFocusTrap } from '@shared/ui/focusTrap'; import styles from '../ContactsPage.module.css'; import { MODAL_TABS } from './shared'; import type { ContactModalTab } from './types'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; /* ═══════════════════════════════════════════════════════════════════════ Add-contact modal + invite panels. diff --git a/app/shared/src/workbench/pages/contacts/ContactMainHelpers.test.ts b/app/workbench/src/pages/contacts/ContactMainHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/pages/contacts/ContactMainHelpers.test.ts rename to app/workbench/src/pages/contacts/ContactMainHelpers.test.ts diff --git a/app/shared/src/workbench/pages/contacts/ContactMainHelpers.ts b/app/workbench/src/pages/contacts/ContactMainHelpers.ts similarity index 100% rename from app/shared/src/workbench/pages/contacts/ContactMainHelpers.ts rename to app/workbench/src/pages/contacts/ContactMainHelpers.ts diff --git a/app/shared/src/workbench/pages/contacts/ContactMainItemParts.tsx b/app/workbench/src/pages/contacts/ContactMainItemParts.tsx similarity index 98% rename from app/shared/src/workbench/pages/contacts/ContactMainItemParts.tsx rename to app/workbench/src/pages/contacts/ContactMainItemParts.tsx index e0782d78b..406ad4538 100644 --- a/app/shared/src/workbench/pages/contacts/ContactMainItemParts.tsx +++ b/app/workbench/src/pages/contacts/ContactMainItemParts.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; -import { EmptyState } from '../../../ui'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; +import { EmptyState } from '@shared/ui'; import styles from '../ContactsPage.module.css'; import { FriendRequestCard, diff --git a/app/shared/src/workbench/pages/contacts/ContactMainParts.tsx b/app/workbench/src/pages/contacts/ContactMainParts.tsx similarity index 98% rename from app/shared/src/workbench/pages/contacts/ContactMainParts.tsx rename to app/workbench/src/pages/contacts/ContactMainParts.tsx index 88078c30f..c3c63ee87 100644 --- a/app/shared/src/workbench/pages/contacts/ContactMainParts.tsx +++ b/app/workbench/src/pages/contacts/ContactMainParts.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; -import { StatusNotice } from '../../../ui'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; +import { StatusNotice } from '@shared/ui'; import { DesignNavIcon } from '../../designIcons'; import { ProfilePopover } from '../../floating'; import styles from '../ContactsPage.module.css'; diff --git a/app/shared/src/workbench/pages/contacts/ContactMainSections.tsx b/app/workbench/src/pages/contacts/ContactMainSections.tsx similarity index 98% rename from app/shared/src/workbench/pages/contacts/ContactMainSections.tsx rename to app/workbench/src/pages/contacts/ContactMainSections.tsx index b98114b4b..d66a02781 100644 --- a/app/shared/src/workbench/pages/contacts/ContactMainSections.tsx +++ b/app/workbench/src/pages/contacts/ContactMainSections.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import styles from '../ContactsPage.module.css'; import { shouldShowNewContactsEmpty } from './ContactMainHelpers'; import { diff --git a/app/shared/src/workbench/pages/contacts/ContactMainViews.tsx b/app/workbench/src/pages/contacts/ContactMainViews.tsx similarity index 99% rename from app/shared/src/workbench/pages/contacts/ContactMainViews.tsx rename to app/workbench/src/pages/contacts/ContactMainViews.tsx index 78f940c1e..a7fc9dd4e 100644 --- a/app/shared/src/workbench/pages/contacts/ContactMainViews.tsx +++ b/app/workbench/src/pages/contacts/ContactMainViews.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { ContactGroupsPane, ContactListPage, diff --git a/app/shared/src/workbench/pages/contacts/ContactNav.tsx b/app/workbench/src/pages/contacts/ContactNav.tsx similarity index 97% rename from app/shared/src/workbench/pages/contacts/ContactNav.tsx rename to app/workbench/src/pages/contacts/ContactNav.tsx index 708e652bc..daaf97051 100644 --- a/app/shared/src/workbench/pages/contacts/ContactNav.tsx +++ b/app/workbench/src/pages/contacts/ContactNav.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import styles from '../ContactsPage.module.css'; import { NavGlyph, NAV_ITEMS } from './shared'; import type { ContactsPageProps } from './types'; diff --git a/app/shared/src/workbench/pages/contacts/ContactRows.tsx b/app/workbench/src/pages/contacts/ContactRows.tsx similarity index 100% rename from app/shared/src/workbench/pages/contacts/ContactRows.tsx rename to app/workbench/src/pages/contacts/ContactRows.tsx diff --git a/app/shared/src/workbench/pages/contacts/index.ts b/app/workbench/src/pages/contacts/index.ts similarity index 100% rename from app/shared/src/workbench/pages/contacts/index.ts rename to app/workbench/src/pages/contacts/index.ts diff --git a/app/shared/src/workbench/pages/contacts/shared.tsx b/app/workbench/src/pages/contacts/shared.tsx similarity index 100% rename from app/shared/src/workbench/pages/contacts/shared.tsx rename to app/workbench/src/pages/contacts/shared.tsx diff --git a/app/shared/src/workbench/pages/contacts/types.ts b/app/workbench/src/pages/contacts/types.ts similarity index 100% rename from app/shared/src/workbench/pages/contacts/types.ts rename to app/workbench/src/pages/contacts/types.ts diff --git a/app/shared/src/workbench/pages/docs/DocMain.tsx b/app/workbench/src/pages/docs/DocMain.tsx similarity index 98% rename from app/shared/src/workbench/pages/docs/DocMain.tsx rename to app/workbench/src/pages/docs/DocMain.tsx index 177539d70..b32940c88 100644 --- a/app/shared/src/workbench/pages/docs/DocMain.tsx +++ b/app/workbench/src/pages/docs/DocMain.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignNavIcon } from '../../designIcons'; import styles from '../DocsPage.module.css'; import { DocPreviewPanel, DocTable } from './DocTableViews'; diff --git a/app/shared/src/workbench/pages/docs/DocTableViews.tsx b/app/workbench/src/pages/docs/DocTableViews.tsx similarity index 97% rename from app/shared/src/workbench/pages/docs/DocTableViews.tsx rename to app/workbench/src/pages/docs/DocTableViews.tsx index 2ee058d1d..cac93a5d7 100644 --- a/app/shared/src/workbench/pages/docs/DocTableViews.tsx +++ b/app/workbench/src/pages/docs/DocTableViews.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignNavIcon } from '../../designIcons'; import type { WorkbenchDocumentPreview } from '../../documentPreview'; import { FilePreview } from '../../inspector'; @@ -12,9 +12,9 @@ import { resolveWorkbenchProfile, type WorkbenchProfileSource, } from '../../profileRegistry'; -import { EmptyState } from '../../../ui'; -import { SkeletonBar } from '../../../ui/SkeletonBar'; -import { Tooltip } from '../../../ui/Tooltip'; +import { EmptyState } from '@shared/ui'; +import { SkeletonBar } from '@shared/ui/SkeletonBar'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from '../DocsPage.module.css'; import type { DocRow } from './types'; diff --git a/app/shared/src/workbench/pages/docs/DocsNav.tsx b/app/workbench/src/pages/docs/DocsNav.tsx similarity index 97% rename from app/shared/src/workbench/pages/docs/DocsNav.tsx rename to app/workbench/src/pages/docs/DocsNav.tsx index abe120eea..75f44a30d 100644 --- a/app/shared/src/workbench/pages/docs/DocsNav.tsx +++ b/app/workbench/src/pages/docs/DocsNav.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import styles from '../DocsPage.module.css'; import { DEFAULT_NAV_ITEMS, NAV_ICONS, NavGlyph } from './shared'; import type { DocsPageNavItem } from './types'; diff --git a/app/shared/src/workbench/pages/docs/index.ts b/app/workbench/src/pages/docs/index.ts similarity index 100% rename from app/shared/src/workbench/pages/docs/index.ts rename to app/workbench/src/pages/docs/index.ts diff --git a/app/shared/src/workbench/pages/docs/shared.tsx b/app/workbench/src/pages/docs/shared.tsx similarity index 100% rename from app/shared/src/workbench/pages/docs/shared.tsx rename to app/workbench/src/pages/docs/shared.tsx diff --git a/app/shared/src/workbench/pages/docs/types.ts b/app/workbench/src/pages/docs/types.ts similarity index 100% rename from app/shared/src/workbench/pages/docs/types.ts rename to app/workbench/src/pages/docs/types.ts diff --git a/app/shared/src/workbench/pages/index.ts b/app/workbench/src/pages/index.ts similarity index 100% rename from app/shared/src/workbench/pages/index.ts rename to app/workbench/src/pages/index.ts diff --git a/app/shared/src/workbench/pages/projects/ProjectAnnouncement.tsx b/app/workbench/src/pages/projects/ProjectAnnouncement.tsx similarity index 100% rename from app/shared/src/workbench/pages/projects/ProjectAnnouncement.tsx rename to app/workbench/src/pages/projects/ProjectAnnouncement.tsx diff --git a/app/shared/src/workbench/pages/projects/ProjectChromeViews.tsx b/app/workbench/src/pages/projects/ProjectChromeViews.tsx similarity index 98% rename from app/shared/src/workbench/pages/projects/ProjectChromeViews.tsx rename to app/workbench/src/pages/projects/ProjectChromeViews.tsx index 908435e7a..a26be8f62 100644 --- a/app/shared/src/workbench/pages/projects/ProjectChromeViews.tsx +++ b/app/workbench/src/pages/projects/ProjectChromeViews.tsx @@ -7,12 +7,12 @@ import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignNavIcon } from '../../designIcons'; import { FOLDER_THEME_COLORS, FOLDER_THEME_COLOR_META, -} from '../../../folderThemeColors'; +} from '@shared/folderThemeColors'; import styles from '../ProjectsPage.module.css'; import type { ProjectDraft, diff --git a/app/shared/src/workbench/pages/projects/ProjectFeedPanel.tsx b/app/workbench/src/pages/projects/ProjectFeedPanel.tsx similarity index 95% rename from app/shared/src/workbench/pages/projects/ProjectFeedPanel.tsx rename to app/workbench/src/pages/projects/ProjectFeedPanel.tsx index 3a4388023..dd3027ee1 100644 --- a/app/shared/src/workbench/pages/projects/ProjectFeedPanel.tsx +++ b/app/workbench/src/pages/projects/ProjectFeedPanel.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import styles from '../ProjectsPage.module.css'; import { ProjectSectionHead } from './ProjectPanelHelpers'; import type { ProjectFeedItem } from './types'; diff --git a/app/shared/src/workbench/pages/projects/ProjectMain.test.tsx b/app/workbench/src/pages/projects/ProjectMain.test.tsx similarity index 95% rename from app/shared/src/workbench/pages/projects/ProjectMain.test.tsx rename to app/workbench/src/pages/projects/ProjectMain.test.tsx index 03392ed5e..acabb2ad5 100644 --- a/app/shared/src/workbench/pages/projects/ProjectMain.test.tsx +++ b/app/workbench/src/pages/projects/ProjectMain.test.tsx @@ -4,13 +4,13 @@ import React from 'react'; import { beforeAll, describe, expect, it } from 'vitest'; -import { render, screen } from '../../../__tests__/setup'; +import { render, screen } from '../../__tests__/setup'; import { ProjectMain } from './ProjectMain'; import type { ProjectMainProps } from './ProjectMain'; // Empty-state copy resolves via the sharedWorkbench namespace; opt into the // zh bundle of the shared test i18next instance (Issue #1717). -import { useTestI18nLanguage } from '../../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/pages/projects/ProjectMain.tsx b/app/workbench/src/pages/projects/ProjectMain.tsx similarity index 97% rename from app/shared/src/workbench/pages/projects/ProjectMain.tsx rename to app/workbench/src/pages/projects/ProjectMain.tsx index a94912b3e..a67b46e2d 100644 --- a/app/shared/src/workbench/pages/projects/ProjectMain.tsx +++ b/app/workbench/src/pages/projects/ProjectMain.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; -import { EmptyState } from '../../../ui'; -import { SkeletonBar } from '../../../ui/SkeletonBar'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; +import { EmptyState } from '@shared/ui'; +import { SkeletonBar } from '@shared/ui/SkeletonBar'; import type { WorkbenchDocumentPreview } from '../../documentPreview'; import type { WorkbenchProfileSource } from '../../profileRegistry'; import styles from '../ProjectsPage.module.css'; diff --git a/app/shared/src/workbench/pages/projects/ProjectMembers.tsx b/app/workbench/src/pages/projects/ProjectMembers.tsx similarity index 100% rename from app/shared/src/workbench/pages/projects/ProjectMembers.tsx rename to app/workbench/src/pages/projects/ProjectMembers.tsx diff --git a/app/shared/src/workbench/pages/projects/ProjectNav.tsx b/app/workbench/src/pages/projects/ProjectNav.tsx similarity index 98% rename from app/shared/src/workbench/pages/projects/ProjectNav.tsx rename to app/workbench/src/pages/projects/ProjectNav.tsx index 6ed517af9..f004a0d21 100644 --- a/app/shared/src/workbench/pages/projects/ProjectNav.tsx +++ b/app/workbench/src/pages/projects/ProjectNav.tsx @@ -7,8 +7,8 @@ import React, { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; -import { StatusNotice } from '../../../ui'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; +import { StatusNotice } from '@shared/ui'; import { DesignNavIcon } from '../../designIcons'; import styles from '../ProjectsPage.module.css'; import { FilterList, ProjectNavRow } from './ProjectChromeViews'; diff --git a/app/shared/src/workbench/pages/projects/ProjectPanelHelpers.test.tsx b/app/workbench/src/pages/projects/ProjectPanelHelpers.test.tsx similarity index 98% rename from app/shared/src/workbench/pages/projects/ProjectPanelHelpers.test.tsx rename to app/workbench/src/pages/projects/ProjectPanelHelpers.test.tsx index c7c4486c6..f224e3973 100644 --- a/app/shared/src/workbench/pages/projects/ProjectPanelHelpers.test.tsx +++ b/app/workbench/src/pages/projects/ProjectPanelHelpers.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { describe, expect, it } from 'vitest'; -import { render, screen } from '../../../__tests__/setup'; +import { render, screen } from '../../__tests__/setup'; import { ProjectMemberChip, ProjectSectionHead, diff --git a/app/shared/src/workbench/pages/projects/ProjectPanelHelpers.tsx b/app/workbench/src/pages/projects/ProjectPanelHelpers.tsx similarity index 100% rename from app/shared/src/workbench/pages/projects/ProjectPanelHelpers.tsx rename to app/workbench/src/pages/projects/ProjectPanelHelpers.tsx diff --git a/app/shared/src/workbench/pages/projects/ProjectPanelParts.tsx b/app/workbench/src/pages/projects/ProjectPanelParts.tsx similarity index 99% rename from app/shared/src/workbench/pages/projects/ProjectPanelParts.tsx rename to app/workbench/src/pages/projects/ProjectPanelParts.tsx index 97f5214b0..37ab80687 100644 --- a/app/shared/src/workbench/pages/projects/ProjectPanelParts.tsx +++ b/app/workbench/src/pages/projects/ProjectPanelParts.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignFileIcon } from '../../designIcons'; import type { WorkbenchDocumentPreview } from '../../documentPreview'; import { FilePreview } from '../../inspector'; diff --git a/app/shared/src/workbench/pages/projects/ProjectPanelViews.tsx b/app/workbench/src/pages/projects/ProjectPanelViews.tsx similarity index 100% rename from app/shared/src/workbench/pages/projects/ProjectPanelViews.tsx rename to app/workbench/src/pages/projects/ProjectPanelViews.tsx diff --git a/app/shared/src/workbench/pages/projects/ProjectTabViews.tsx b/app/workbench/src/pages/projects/ProjectTabViews.tsx similarity index 99% rename from app/shared/src/workbench/pages/projects/ProjectTabViews.tsx rename to app/workbench/src/pages/projects/ProjectTabViews.tsx index ff2e7c43f..4a5244344 100644 --- a/app/shared/src/workbench/pages/projects/ProjectTabViews.tsx +++ b/app/workbench/src/pages/projects/ProjectTabViews.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignFileIcon, } from '../../designIcons'; diff --git a/app/shared/src/workbench/pages/projects/index.ts b/app/workbench/src/pages/projects/index.ts similarity index 100% rename from app/shared/src/workbench/pages/projects/index.ts rename to app/workbench/src/pages/projects/index.ts diff --git a/app/shared/src/workbench/pages/projects/shared.tsx b/app/workbench/src/pages/projects/shared.tsx similarity index 100% rename from app/shared/src/workbench/pages/projects/shared.tsx rename to app/workbench/src/pages/projects/shared.tsx diff --git a/app/shared/src/workbench/pages/projects/types.ts b/app/workbench/src/pages/projects/types.ts similarity index 99% rename from app/shared/src/workbench/pages/projects/types.ts rename to app/workbench/src/pages/projects/types.ts index 7991a0b82..c38033299 100644 --- a/app/shared/src/workbench/pages/projects/types.ts +++ b/app/workbench/src/pages/projects/types.ts @@ -4,7 +4,7 @@ import type { WorkbenchDocumentPreview } from '../../documentPreview'; import type { DesignNavIconName } from '../../designIcons'; -import type { FolderThemeColor } from '../../../folderThemeColors'; +import type { FolderThemeColor } from '@shared/folderThemeColors'; import type { WorkbenchProfileSource } from '../../profileRegistry'; export type ProjectRunStatus = diff --git a/app/shared/src/workbench/pages/projects/useProjectEditor.ts b/app/workbench/src/pages/projects/useProjectEditor.ts similarity index 100% rename from app/shared/src/workbench/pages/projects/useProjectEditor.ts rename to app/workbench/src/pages/projects/useProjectEditor.ts diff --git a/app/shared/src/workbench/pages/settings/SettingsMain.tsx b/app/workbench/src/pages/settings/SettingsMain.tsx similarity index 95% rename from app/shared/src/workbench/pages/settings/SettingsMain.tsx rename to app/workbench/src/pages/settings/SettingsMain.tsx index 463d60656..db90cedcd 100644 --- a/app/shared/src/workbench/pages/settings/SettingsMain.tsx +++ b/app/workbench/src/pages/settings/SettingsMain.tsx @@ -7,14 +7,14 @@ import React from 'react'; import { DesignNavIcon } from '../../designIcons'; -import { RecoveryPanel, StatusNotice } from '../../../ui'; +import { RecoveryPanel, StatusNotice } from '@shared/ui'; import styles from '../SettingsPage.module.css'; import { PANE_RENDERERS } from './SettingsPanes'; import { PANE_META } from './types'; import type { SettingsPageProps } from './types'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../../chatview/i18n/resources'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; export type SettingsMainProps = SettingsPageProps; diff --git a/app/shared/src/workbench/pages/settings/SettingsNav.tsx b/app/workbench/src/pages/settings/SettingsNav.tsx similarity index 100% rename from app/shared/src/workbench/pages/settings/SettingsNav.tsx rename to app/workbench/src/pages/settings/SettingsNav.tsx diff --git a/app/shared/src/workbench/pages/settings/SettingsPaneHelpers.test.ts b/app/workbench/src/pages/settings/SettingsPaneHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/pages/settings/SettingsPaneHelpers.test.ts rename to app/workbench/src/pages/settings/SettingsPaneHelpers.test.ts diff --git a/app/shared/src/workbench/pages/settings/SettingsPaneHelpers.ts b/app/workbench/src/pages/settings/SettingsPaneHelpers.ts similarity index 97% rename from app/shared/src/workbench/pages/settings/SettingsPaneHelpers.ts rename to app/workbench/src/pages/settings/SettingsPaneHelpers.ts index f7173146f..96655b338 100644 --- a/app/shared/src/workbench/pages/settings/SettingsPaneHelpers.ts +++ b/app/workbench/src/pages/settings/SettingsPaneHelpers.ts @@ -1,6 +1,6 @@ import type { DesignNavIconName } from '../../designIcons'; -import { getWorkbenchDataModeContract } from '../../../demo'; -import type { LocalCliDiscoveryItem } from '../../../platform'; +import { getWorkbenchDataModeContract } from '@shared/demo'; +import type { LocalCliDiscoveryItem } from '@shared/platform'; import type { StatePanelKind } from './types'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/pages/settings/SettingsPaneParts.tsx b/app/workbench/src/pages/settings/SettingsPaneParts.tsx similarity index 96% rename from app/shared/src/workbench/pages/settings/SettingsPaneParts.tsx rename to app/workbench/src/pages/settings/SettingsPaneParts.tsx index e59ad7549..2994385a8 100644 --- a/app/shared/src/workbench/pages/settings/SettingsPaneParts.tsx +++ b/app/workbench/src/pages/settings/SettingsPaneParts.tsx @@ -4,7 +4,7 @@ import { DESIGN_NAV_GLYPH_SIZE, DESIGN_NAV_GLYPH_STROKE_WIDTH, } from '../../designIcons'; -import type { LocalCliDiscoveryManifest } from '../../../platform'; +import type { LocalCliDiscoveryManifest } from '@shared/platform'; import styles from '../SettingsPage.module.css'; import { SettingPath, @@ -24,8 +24,8 @@ import { } from './SettingsPaneHelpers'; import type { StatePanelKind } from './types'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../../chatview/i18n/resources'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; /* ═══════════════════════════════════════════════════════════════════════ SettingsPaneParts — presentational residual slices from SettingsPanes diff --git a/app/shared/src/workbench/pages/settings/SettingsPanes.tsx b/app/workbench/src/pages/settings/SettingsPanes.tsx similarity index 98% rename from app/shared/src/workbench/pages/settings/SettingsPanes.tsx rename to app/workbench/src/pages/settings/SettingsPanes.tsx index 3a4fd0c97..066d1261e 100644 --- a/app/shared/src/workbench/pages/settings/SettingsPanes.tsx +++ b/app/workbench/src/pages/settings/SettingsPanes.tsx @@ -5,8 +5,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../../chatview/i18n/resources'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DataModeControl, SettingPath, @@ -30,8 +30,8 @@ import type { SettingsPageProps, SettingsPaneId } from './types'; import { getResolvedShortcutGroups, checkConflicts, -} from '../../../utils/keyboardShortcuts'; -import type { KeyboardShortcutGroup } from '../../../utils/keyboardShortcuts'; +} from '@shared/utils/keyboardShortcuts'; +import type { KeyboardShortcutGroup } from '@shared/utils/keyboardShortcuts'; export { DataModeStatus, diff --git a/app/shared/src/workbench/pages/settings/index.ts b/app/workbench/src/pages/settings/index.ts similarity index 100% rename from app/shared/src/workbench/pages/settings/index.ts rename to app/workbench/src/pages/settings/index.ts diff --git a/app/shared/src/workbench/pages/settings/shared.tsx b/app/workbench/src/pages/settings/shared.tsx similarity index 97% rename from app/shared/src/workbench/pages/settings/shared.tsx rename to app/workbench/src/pages/settings/shared.tsx index f6eb91c62..9da2ab6e7 100644 --- a/app/shared/src/workbench/pages/settings/shared.tsx +++ b/app/workbench/src/pages/settings/shared.tsx @@ -10,11 +10,11 @@ import { DESIGN_NAV_GLYPH_STROKE_WIDTH, type DesignNavIconName, } from '../../designIcons'; -import { getWorkbenchDataModeContract } from '../../../demo'; +import { getWorkbenchDataModeContract } from '@shared/demo'; import styles from '../SettingsPage.module.css'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../../chatview/i18n/resources'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; /* ── Design icons ── */ diff --git a/app/shared/src/workbench/pages/settings/types.ts b/app/workbench/src/pages/settings/types.ts similarity index 99% rename from app/shared/src/workbench/pages/settings/types.ts rename to app/workbench/src/pages/settings/types.ts index a1e326adc..20d05164a 100644 --- a/app/shared/src/workbench/pages/settings/types.ts +++ b/app/workbench/src/pages/settings/types.ts @@ -3,7 +3,7 @@ ═══════════════════════════════════════════════════════════════════════ */ import type { DesignNavIconName } from '../../designIcons'; -import type { LocalCliDiscoveryManifest, RuntimeSessionSummary } from '../../../platform'; +import type { LocalCliDiscoveryManifest, RuntimeSessionSummary } from '@shared/platform'; export type SettingsPaneId = 'appearance' | 'notify' | 'agent' | 'local' | 'states' | 'shortcuts'; diff --git a/app/shared/src/workbench/pages/tasks/TaskMainViews.tsx b/app/workbench/src/pages/tasks/TaskMainViews.tsx similarity index 98% rename from app/shared/src/workbench/pages/tasks/TaskMainViews.tsx rename to app/workbench/src/pages/tasks/TaskMainViews.tsx index a564b16ff..cada18a84 100644 --- a/app/shared/src/workbench/pages/tasks/TaskMainViews.tsx +++ b/app/workbench/src/pages/tasks/TaskMainViews.tsx @@ -8,8 +8,8 @@ import React, { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; -import { StatusNotice } from '../../../ui'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; +import { StatusNotice } from '@shared/ui'; import { DesignNavIcon } from '../../designIcons'; import styles from '../TasksPage.module.css'; import { TaskSelectionStrip, TaskTable } from './TaskTableViews'; diff --git a/app/shared/src/workbench/pages/tasks/TaskNav.tsx b/app/workbench/src/pages/tasks/TaskNav.tsx similarity index 98% rename from app/shared/src/workbench/pages/tasks/TaskNav.tsx rename to app/workbench/src/pages/tasks/TaskNav.tsx index 75a7c3875..2044123aa 100644 --- a/app/shared/src/workbench/pages/tasks/TaskNav.tsx +++ b/app/workbench/src/pages/tasks/TaskNav.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignNavIcon } from '../../designIcons'; import styles from '../TasksPage.module.css'; import { NAV_PRIMARY, NAV_QUICK, NavGlyph, TaskNavMenu } from './shared'; diff --git a/app/shared/src/workbench/pages/tasks/TaskTableHelpers.test.ts b/app/workbench/src/pages/tasks/TaskTableHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/pages/tasks/TaskTableHelpers.test.ts rename to app/workbench/src/pages/tasks/TaskTableHelpers.test.ts diff --git a/app/shared/src/workbench/pages/tasks/TaskTableHelpers.ts b/app/workbench/src/pages/tasks/TaskTableHelpers.ts similarity index 98% rename from app/shared/src/workbench/pages/tasks/TaskTableHelpers.ts rename to app/workbench/src/pages/tasks/TaskTableHelpers.ts index 92db81826..c931e4cb3 100644 --- a/app/shared/src/workbench/pages/tasks/TaskTableHelpers.ts +++ b/app/workbench/src/pages/tasks/TaskTableHelpers.ts @@ -1,4 +1,4 @@ -import type { EmptyStateAction, EmptyStateProps } from '../../../ui'; +import type { EmptyStateAction, EmptyStateProps } from '@shared/ui'; import styles from '../TasksPage.module.css'; import type { TaskEditDraft, TaskGroup, TaskStatus } from './types'; diff --git a/app/shared/src/workbench/pages/tasks/TaskTableParts.tsx b/app/workbench/src/pages/tasks/TaskTableParts.tsx similarity index 98% rename from app/shared/src/workbench/pages/tasks/TaskTableParts.tsx rename to app/workbench/src/pages/tasks/TaskTableParts.tsx index 5e99e0547..a573b58a7 100644 --- a/app/shared/src/workbench/pages/tasks/TaskTableParts.tsx +++ b/app/workbench/src/pages/tasks/TaskTableParts.tsx @@ -1,12 +1,12 @@ import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { DesignNavIcon } from '../../designIcons'; import { resolveWorkbenchProfile, type WorkbenchProfileSource, } from '../../profileRegistry'; -import { EmptyState } from '../../../ui'; +import { EmptyState } from '@shared/ui'; import styles from '../TasksPage.module.css'; import { TASK_EDIT_FIELD_CONFIGS, diff --git a/app/shared/src/workbench/pages/tasks/TaskTableViews.tsx b/app/workbench/src/pages/tasks/TaskTableViews.tsx similarity index 100% rename from app/shared/src/workbench/pages/tasks/TaskTableViews.tsx rename to app/workbench/src/pages/tasks/TaskTableViews.tsx diff --git a/app/shared/src/workbench/pages/tasks/index.ts b/app/workbench/src/pages/tasks/index.ts similarity index 100% rename from app/shared/src/workbench/pages/tasks/index.ts rename to app/workbench/src/pages/tasks/index.ts diff --git a/app/shared/src/workbench/pages/tasks/shared.tsx b/app/workbench/src/pages/tasks/shared.tsx similarity index 97% rename from app/shared/src/workbench/pages/tasks/shared.tsx rename to app/workbench/src/pages/tasks/shared.tsx index eed11b195..af2db4140 100644 --- a/app/shared/src/workbench/pages/tasks/shared.tsx +++ b/app/workbench/src/pages/tasks/shared.tsx @@ -13,7 +13,7 @@ import { import styles from '../TasksPage.module.css'; import type { TasksPane, ViewMode } from './types'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; // ── Nav definitions ── diff --git a/app/shared/src/workbench/pages/tasks/types.ts b/app/workbench/src/pages/tasks/types.ts similarity index 100% rename from app/shared/src/workbench/pages/tasks/types.ts rename to app/workbench/src/pages/tasks/types.ts diff --git a/app/shared/src/workbench/pages/workbench-page-shared.module.css b/app/workbench/src/pages/workbench-page-shared.module.css similarity index 100% rename from app/shared/src/workbench/pages/workbench-page-shared.module.css rename to app/workbench/src/pages/workbench-page-shared.module.css diff --git a/app/shared/src/workbench/profileRegistry.test.ts b/app/workbench/src/profileRegistry.test.ts similarity index 100% rename from app/shared/src/workbench/profileRegistry.test.ts rename to app/workbench/src/profileRegistry.test.ts diff --git a/app/shared/src/workbench/profileRegistry.ts b/app/workbench/src/profileRegistry.ts similarity index 100% rename from app/shared/src/workbench/profileRegistry.ts rename to app/workbench/src/profileRegistry.ts diff --git a/app/shared/src/workbench/rightInspectorHelpers.test.ts b/app/workbench/src/rightInspectorHelpers.test.ts similarity index 98% rename from app/shared/src/workbench/rightInspectorHelpers.test.ts rename to app/workbench/src/rightInspectorHelpers.test.ts index b58471817..65f382700 100644 --- a/app/shared/src/workbench/rightInspectorHelpers.test.ts +++ b/app/workbench/src/rightInspectorHelpers.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; -import type { EvidenceRef } from '../transcript'; -import type { RuntimeEvidenceSnapshot } from '../inspector'; +import type { EvidenceRef } from '@shared/transcript'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; import { createInspectorResizerKeyDownHandler, createInspectorResizerPointerDownHandler, diff --git a/app/shared/src/workbench/rightInspectorHelpers.ts b/app/workbench/src/rightInspectorHelpers.ts similarity index 97% rename from app/shared/src/workbench/rightInspectorHelpers.ts rename to app/workbench/src/rightInspectorHelpers.ts index b1c1db73d..7db65ae55 100644 --- a/app/shared/src/workbench/rightInspectorHelpers.ts +++ b/app/workbench/src/rightInspectorHelpers.ts @@ -1,9 +1,9 @@ import type { KeyboardEvent as ReactKeyboardEvent, PointerEvent as ReactPointerEvent } from 'react'; -import type { EvidenceRef, RouteDecisionTranscriptBlock, SubagentTranscriptBlock, SubtaskTranscriptBlock, ChildAgentTranscriptBlock } from '../transcript'; -import type { RuntimeEvidenceSnapshot } from '../inspector'; -import type { FileDiff } from '../types/chat'; -import type { DagNode } from '../ui/DagTree'; -import { buildDagNodesFromTranscript } from '../ui/DagTree'; +import type { EvidenceRef, RouteDecisionTranscriptBlock, SubagentTranscriptBlock, SubtaskTranscriptBlock, ChildAgentTranscriptBlock } from '@shared/transcript'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { FileDiff } from '@shared/types/chat'; +import type { DagNode } from '@shared/ui/DagTree'; +import { buildDagNodesFromTranscript } from '@shared/ui/DagTree'; import { isThemedBlankPreviewUrl } from './inspector/BrowserPreview'; import type { PreviewFile } from './inspector/FilePreviewRouter'; import { diff --git a/app/shared/src/workbench/rightInspectorTypes.ts b/app/workbench/src/rightInspectorTypes.ts similarity index 94% rename from app/shared/src/workbench/rightInspectorTypes.ts rename to app/workbench/src/rightInspectorTypes.ts index e315ac869..5f96b6b97 100644 --- a/app/shared/src/workbench/rightInspectorTypes.ts +++ b/app/workbench/src/rightInspectorTypes.ts @@ -1,5 +1,5 @@ -import type { RuntimeEvidenceSnapshot } from '../inspector'; -import type { PreviewPort } from '../platform'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; +import type { PreviewPort } from '@shared/platform'; import type { ChildAgentTranscriptBlock, ContextUsageTranscriptBlock, @@ -7,7 +7,7 @@ import type { RouteDecisionTranscriptBlock, SubagentTranscriptBlock, SubtaskTranscriptBlock, -} from '../transcript'; +} from '@shared/transcript'; import type { FileItem, RunResultInfo } from './inspector/OverviewPanel'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/sessionImport/SessionImportList.module.css b/app/workbench/src/sessionImport/SessionImportList.module.css similarity index 100% rename from app/shared/src/workbench/sessionImport/SessionImportList.module.css rename to app/workbench/src/sessionImport/SessionImportList.module.css diff --git a/app/shared/src/workbench/sessionImport/SessionImportList.test.tsx b/app/workbench/src/sessionImport/SessionImportList.test.tsx similarity index 100% rename from app/shared/src/workbench/sessionImport/SessionImportList.test.tsx rename to app/workbench/src/sessionImport/SessionImportList.test.tsx diff --git a/app/shared/src/workbench/sessionImport/SessionImportList.tsx b/app/workbench/src/sessionImport/SessionImportList.tsx similarity index 100% rename from app/shared/src/workbench/sessionImport/SessionImportList.tsx rename to app/workbench/src/sessionImport/SessionImportList.tsx diff --git a/app/shared/src/workbench/sessionImport/index.ts b/app/workbench/src/sessionImport/index.ts similarity index 100% rename from app/shared/src/workbench/sessionImport/index.ts rename to app/workbench/src/sessionImport/index.ts diff --git a/app/shared/src/workbench/sessionImport/types.ts b/app/workbench/src/sessionImport/types.ts similarity index 100% rename from app/shared/src/workbench/sessionImport/types.ts rename to app/workbench/src/sessionImport/types.ts diff --git a/app/shared/src/workbench/settingsService.test.ts b/app/workbench/src/settingsService.test.ts similarity index 97% rename from app/shared/src/workbench/settingsService.test.ts rename to app/workbench/src/settingsService.test.ts index d75bf64d0..861bbfb9e 100644 --- a/app/shared/src/workbench/settingsService.test.ts +++ b/app/workbench/src/settingsService.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; import { createSettingsService } from './settingsService'; -import type { SettingsPort } from '../platform/types'; +import type { SettingsPort } from '@shared/platform/types'; function createPort(overrides?: Partial): SettingsPort { return { diff --git a/app/shared/src/workbench/settingsService.ts b/app/workbench/src/settingsService.ts similarity index 98% rename from app/shared/src/workbench/settingsService.ts rename to app/workbench/src/settingsService.ts index abce36e8a..d821f92d3 100644 --- a/app/shared/src/workbench/settingsService.ts +++ b/app/workbench/src/settingsService.ts @@ -11,7 +11,7 @@ */ import { deserializeSettings, serializeSettings } from './settingsTypes'; -import type { SettingsPort } from '../platform/types'; +import type { SettingsPort } from '@shared/platform/types'; // Re-export for convenience export type { SettingsPort }; diff --git a/app/shared/src/workbench/settingsTypes.ts b/app/workbench/src/settingsTypes.ts similarity index 100% rename from app/shared/src/workbench/settingsTypes.ts rename to app/workbench/src/settingsTypes.ts diff --git a/app/shared/src/workbench/team/InlineDelegationCard.module.css b/app/workbench/src/team/InlineDelegationCard.module.css similarity index 100% rename from app/shared/src/workbench/team/InlineDelegationCard.module.css rename to app/workbench/src/team/InlineDelegationCard.module.css diff --git a/app/shared/src/workbench/team/InlineDelegationCard.test.tsx b/app/workbench/src/team/InlineDelegationCard.test.tsx similarity index 97% rename from app/shared/src/workbench/team/InlineDelegationCard.test.tsx rename to app/workbench/src/team/InlineDelegationCard.test.tsx index 0be18f4d9..03f0cba23 100644 --- a/app/shared/src/workbench/team/InlineDelegationCard.test.tsx +++ b/app/workbench/src/team/InlineDelegationCard.test.tsx @@ -6,15 +6,15 @@ import { render, screen, fireEvent, act } from '@testing-library/react'; import { beforeAll, describe, expect, it, beforeEach, vi } from 'vitest'; import type { ReactNode } from 'react'; -import { HUB_EVENTS } from '../../hubEvents'; +import { HUB_EVENTS } from '@shared/hubEvents'; import { getMessageDelegationStore } from './MessageDelegationStore'; import { getSubagentStreamStore } from './SubagentStreamStore'; import { InlineDelegationCard } from './InlineDelegationCard'; -import { Transcript } from '../../chatview/components/Transcript'; +import { Transcript } from '@shared/chatview/components/Transcript'; // The delegation card + transcript labels resolve through the shared test // i18next instance; opt into the zh bundle for this suite (Issue #1717). -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); @@ -33,7 +33,7 @@ vi.mock('virtua', () => ({ // InlineDelegationCard + SubagentTranscript resolve status labels via the // chatview i18n namespace; the registered test instance provides the real // zh copy. -import type { TranscriptUserItem } from '../../chatview/transcript-item'; +import type { TranscriptUserItem } from '@shared/chatview/transcript-item'; function feedDispatch(overrides: Record = {}): void { getMessageDelegationStore().handleEvent(HUB_EVENTS.AGENT_DISPATCH, { diff --git a/app/shared/src/workbench/team/InlineDelegationCard.tsx b/app/workbench/src/team/InlineDelegationCard.tsx similarity index 98% rename from app/shared/src/workbench/team/InlineDelegationCard.tsx rename to app/workbench/src/team/InlineDelegationCard.tsx index 1bb16e520..59a0df5ec 100644 --- a/app/shared/src/workbench/team/InlineDelegationCard.tsx +++ b/app/workbench/src/team/InlineDelegationCard.tsx @@ -13,7 +13,7 @@ import React, { useCallback, useMemo, useState, useSyncExternalStore } from 'react'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import { getMessageDelegationStore, type DelegationEntry, @@ -21,7 +21,7 @@ import { } from './MessageDelegationStore'; import { getSubagentStreamStore } from './SubagentStreamStore'; import { SubagentTranscript } from './SubagentTranscript'; -import { Icon } from '../../ui/Icon'; +import { Icon } from '@shared/ui/Icon'; import styles from './InlineDelegationCard.module.css'; // ── Status presentation ───────────────────────────────────────────────────── diff --git a/app/shared/src/workbench/team/MessageDelegationStore.test.ts b/app/workbench/src/team/MessageDelegationStore.test.ts similarity index 99% rename from app/shared/src/workbench/team/MessageDelegationStore.test.ts rename to app/workbench/src/team/MessageDelegationStore.test.ts index 20f563686..c9dc32874 100644 --- a/app/shared/src/workbench/team/MessageDelegationStore.test.ts +++ b/app/workbench/src/team/MessageDelegationStore.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, beforeEach, vi } from 'vitest'; -import { HUB_EVENTS } from '../../hubEvents'; +import { HUB_EVENTS } from '@shared/hubEvents'; import { createMessageDelegationStore, type MessageDelegationStore, diff --git a/app/shared/src/workbench/team/MessageDelegationStore.ts b/app/workbench/src/team/MessageDelegationStore.ts similarity index 99% rename from app/shared/src/workbench/team/MessageDelegationStore.ts rename to app/workbench/src/team/MessageDelegationStore.ts index 997652e2f..dee20aca1 100644 --- a/app/shared/src/workbench/team/MessageDelegationStore.ts +++ b/app/workbench/src/team/MessageDelegationStore.ts @@ -17,7 +17,7 @@ // 'streaming' once (dispatching → streaming) and no-op on subsequent // tokens, so the store does not notify per token. -import { HUB_EVENTS, type HubEventType } from '../../hubEvents'; +import { HUB_EVENTS, type HubEventType } from '@shared/hubEvents'; // ── Types ────────────────────────────────────────────────────────────────── diff --git a/app/shared/src/workbench/team/SubagentSessionDialog.tsx b/app/workbench/src/team/SubagentSessionDialog.tsx similarity index 94% rename from app/shared/src/workbench/team/SubagentSessionDialog.tsx rename to app/workbench/src/team/SubagentSessionDialog.tsx index 6bd09b8a4..05dd76229 100644 --- a/app/shared/src/workbench/team/SubagentSessionDialog.tsx +++ b/app/workbench/src/team/SubagentSessionDialog.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../i18n'; -import Modal from '../../ui/Modal'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; +import Modal from '@shared/ui/Modal'; import { SubagentTranscript } from './SubagentTranscript'; import type { TeamSubagentStreamEvent } from './SubagentStreamStore'; import styles from './SubagentStreamOverlay.module.css'; diff --git a/app/shared/src/workbench/team/SubagentStreamOverlay.module.css b/app/workbench/src/team/SubagentStreamOverlay.module.css similarity index 100% rename from app/shared/src/workbench/team/SubagentStreamOverlay.module.css rename to app/workbench/src/team/SubagentStreamOverlay.module.css diff --git a/app/shared/src/workbench/team/SubagentStreamOverlay.test.tsx b/app/workbench/src/team/SubagentStreamOverlay.test.tsx similarity index 99% rename from app/shared/src/workbench/team/SubagentStreamOverlay.test.tsx rename to app/workbench/src/team/SubagentStreamOverlay.test.tsx index e05601d9f..aff34e7f1 100644 --- a/app/shared/src/workbench/team/SubagentStreamOverlay.test.tsx +++ b/app/workbench/src/team/SubagentStreamOverlay.test.tsx @@ -12,7 +12,7 @@ import { SubagentStreamOverlay } from './SubagentStreamOverlay'; // inside the expanded overlay) uses the chatview namespace for its category // labels and empty hint. The registered test instance provides the real zh // copy for both namespaces once this suite opts into zh (Issue #1717). -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/team/SubagentStreamOverlay.tsx b/app/workbench/src/team/SubagentStreamOverlay.tsx similarity index 98% rename from app/shared/src/workbench/team/SubagentStreamOverlay.tsx rename to app/workbench/src/team/SubagentStreamOverlay.tsx index 85e4506b6..7bf07f73b 100644 --- a/app/shared/src/workbench/team/SubagentStreamOverlay.tsx +++ b/app/workbench/src/team/SubagentStreamOverlay.tsx @@ -11,15 +11,15 @@ import React, { useSyncExternalStore, } from 'react'; import { useTranslation } from 'react-i18next'; -import { SHARED_WORKBENCH_I18N_NAMESPACE } from '../../i18n'; +import { SHARED_WORKBENCH_I18N_NAMESPACE } from '@shared/i18n'; import { getSubagentStreamStore, type TeamSubagentStreamEvent, } from './SubagentStreamStore'; import { SubagentTranscript } from './SubagentTranscript'; import { SubagentSessionDialog } from './SubagentSessionDialog'; -import { Icon } from '../../ui/Icon'; -import { Tooltip } from '../../ui/Tooltip'; +import { Icon } from '@shared/ui/Icon'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from './SubagentStreamOverlay.module.css'; // ── Status derivation ─────────────────────────────────────────────────────── diff --git a/app/shared/src/workbench/team/SubagentStreamStore.test.ts b/app/workbench/src/team/SubagentStreamStore.test.ts similarity index 100% rename from app/shared/src/workbench/team/SubagentStreamStore.test.ts rename to app/workbench/src/team/SubagentStreamStore.test.ts diff --git a/app/shared/src/workbench/team/SubagentStreamStore.ts b/app/workbench/src/team/SubagentStreamStore.ts similarity index 100% rename from app/shared/src/workbench/team/SubagentStreamStore.ts rename to app/workbench/src/team/SubagentStreamStore.ts diff --git a/app/shared/src/workbench/team/SubagentTranscript.module.css b/app/workbench/src/team/SubagentTranscript.module.css similarity index 100% rename from app/shared/src/workbench/team/SubagentTranscript.module.css rename to app/workbench/src/team/SubagentTranscript.module.css diff --git a/app/shared/src/workbench/team/SubagentTranscript.test.tsx b/app/workbench/src/team/SubagentTranscript.test.tsx similarity index 98% rename from app/shared/src/workbench/team/SubagentTranscript.test.tsx rename to app/workbench/src/team/SubagentTranscript.test.tsx index c8d027f91..c5044ad00 100644 --- a/app/shared/src/workbench/team/SubagentTranscript.test.tsx +++ b/app/workbench/src/team/SubagentTranscript.test.tsx @@ -7,7 +7,7 @@ import { SubagentTranscript } from './SubagentTranscript'; // SubagentTranscript resolves category labels + empty hint via the chatview // i18n namespace; the registered test instance provides the real zh copy // once this suite opts into the zh bundle (Issue #1717). -import { useTestI18nLanguage } from '../../testing/i18n'; +import { useTestI18nLanguage } from '@shared/testing/i18n'; beforeAll(async () => { await useTestI18nLanguage('zh'); diff --git a/app/shared/src/workbench/team/SubagentTranscript.tsx b/app/workbench/src/team/SubagentTranscript.tsx similarity index 98% rename from app/shared/src/workbench/team/SubagentTranscript.tsx rename to app/workbench/src/team/SubagentTranscript.tsx index 7cb8beeb4..ed209dff4 100644 --- a/app/shared/src/workbench/team/SubagentTranscript.tsx +++ b/app/workbench/src/team/SubagentTranscript.tsx @@ -5,9 +5,9 @@ import React, { type ReactNode, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { CHATVIEW_I18N_NAMESPACE } from '../../chatview/i18n/resources'; +import { CHATVIEW_I18N_NAMESPACE } from '@shared/chatview/i18n/resources'; import type { TeamSubagentStreamEvent } from './SubagentStreamStore'; -import { Icon } from '../../ui/Icon'; +import { Icon } from '@shared/ui/Icon'; import styles from './SubagentTranscript.module.css'; // ── Event category ────────────────────────────────────────────────────────── diff --git a/app/shared/src/workbench/team/index.ts b/app/workbench/src/team/index.ts similarity index 100% rename from app/shared/src/workbench/team/index.ts rename to app/workbench/src/team/index.ts diff --git a/app/shared/src/workbench/terminal/TerminalPanel.module.css b/app/workbench/src/terminal/TerminalPanel.module.css similarity index 100% rename from app/shared/src/workbench/terminal/TerminalPanel.module.css rename to app/workbench/src/terminal/TerminalPanel.module.css diff --git a/app/shared/src/workbench/terminal/TerminalPanel.test.tsx b/app/workbench/src/terminal/TerminalPanel.test.tsx similarity index 94% rename from app/shared/src/workbench/terminal/TerminalPanel.test.tsx rename to app/workbench/src/terminal/TerminalPanel.test.tsx index c65e63d3b..ea7f39c79 100644 --- a/app/shared/src/workbench/terminal/TerminalPanel.test.tsx +++ b/app/workbench/src/terminal/TerminalPanel.test.tsx @@ -1,7 +1,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import { createMockTerminalPort } from '../../platform/createMockPlatform'; -import type { TerminalSession } from '../../platform/types'; +import { createMockTerminalPort } from '@shared/platform/createMockPlatform'; +import type { TerminalSession } from '@shared/platform/types'; import { isLocalTerminalEnabled, TerminalPanel } from './TerminalPanel'; const sampleSessions: TerminalSession[] = [ diff --git a/app/shared/src/workbench/terminal/TerminalPanel.tsx b/app/workbench/src/terminal/TerminalPanel.tsx similarity index 99% rename from app/shared/src/workbench/terminal/TerminalPanel.tsx rename to app/workbench/src/terminal/TerminalPanel.tsx index 8b9064373..82f21113e 100644 --- a/app/shared/src/workbench/terminal/TerminalPanel.tsx +++ b/app/workbench/src/terminal/TerminalPanel.tsx @@ -3,9 +3,9 @@ import type { TerminalPort, TerminalSession, TerminalSessionId, -} from '../../platform/types'; +} from '@shared/platform/types'; import { DESIGN_NAV_GLYPH_STROKE_WIDTH, DesignNavIcon } from '../designIcons'; -import { Tooltip } from '../../ui/Tooltip'; +import { Tooltip } from '@shared/ui/Tooltip'; import styles from './TerminalPanel.module.css'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/terminal/index.ts b/app/workbench/src/terminal/index.ts similarity index 100% rename from app/shared/src/workbench/terminal/index.ts rename to app/workbench/src/terminal/index.ts diff --git a/app/shared/src/workbench/transcriptEventTypes.ts b/app/workbench/src/transcriptEventTypes.ts similarity index 100% rename from app/shared/src/workbench/transcriptEventTypes.ts rename to app/workbench/src/transcriptEventTypes.ts diff --git a/app/shared/src/workbench/unifiedComposerHelpers.test.ts b/app/workbench/src/unifiedComposerHelpers.test.ts similarity index 99% rename from app/shared/src/workbench/unifiedComposerHelpers.test.ts rename to app/workbench/src/unifiedComposerHelpers.test.ts index e81b17b19..b6f9d1d0a 100644 --- a/app/shared/src/workbench/unifiedComposerHelpers.test.ts +++ b/app/workbench/src/unifiedComposerHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { ComposerState } from '../composer'; +import type { ComposerState } from '@shared/composer'; import { MAX_ATTACHMENT_BYTES, buildAttachmentOversizeToast, diff --git a/app/shared/src/workbench/unifiedComposerHelpers.ts b/app/workbench/src/unifiedComposerHelpers.ts similarity index 99% rename from app/shared/src/workbench/unifiedComposerHelpers.ts rename to app/workbench/src/unifiedComposerHelpers.ts index 1bb83b675..c7f2a3c8c 100644 --- a/app/shared/src/workbench/unifiedComposerHelpers.ts +++ b/app/workbench/src/unifiedComposerHelpers.ts @@ -1,5 +1,5 @@ -import type { ComposerAttachment, ComposerMention, ComposerState } from '../composer'; -import { canSubmitComposer, formatComposerAttachmentSize } from '../composer'; +import type { ComposerAttachment, ComposerMention, ComposerState } from '@shared/composer'; +import { canSubmitComposer, formatComposerAttachmentSize } from '@shared/composer'; import type { ComposerSubmitBehavior } from './workbenchPreferences'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/unifiedComposerHostActions.test.ts b/app/workbench/src/unifiedComposerHostActions.test.ts similarity index 100% rename from app/shared/src/workbench/unifiedComposerHostActions.test.ts rename to app/workbench/src/unifiedComposerHostActions.test.ts diff --git a/app/shared/src/workbench/unifiedComposerHostActions.ts b/app/workbench/src/unifiedComposerHostActions.ts similarity index 99% rename from app/shared/src/workbench/unifiedComposerHostActions.ts rename to app/workbench/src/unifiedComposerHostActions.ts index ca37d1936..d8987ef3b 100644 --- a/app/shared/src/workbench/unifiedComposerHostActions.ts +++ b/app/workbench/src/unifiedComposerHostActions.ts @@ -3,7 +3,7 @@ import type { ComposerAction, ComposerAttachment, ComposerMention, -} from '../composer'; +} from '@shared/composer'; import { findMentionById } from './unifiedComposerHelpers'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/unifiedComposerHostFilePick.test.ts b/app/workbench/src/unifiedComposerHostFilePick.test.ts similarity index 100% rename from app/shared/src/workbench/unifiedComposerHostFilePick.test.ts rename to app/workbench/src/unifiedComposerHostFilePick.test.ts diff --git a/app/shared/src/workbench/unifiedComposerHostFilePick.ts b/app/workbench/src/unifiedComposerHostFilePick.ts similarity index 98% rename from app/shared/src/workbench/unifiedComposerHostFilePick.ts rename to app/workbench/src/unifiedComposerHostFilePick.ts index ec5298409..a58ec8438 100644 --- a/app/shared/src/workbench/unifiedComposerHostFilePick.ts +++ b/app/workbench/src/unifiedComposerHostFilePick.ts @@ -1,4 +1,4 @@ -import type { ComposerAttachment } from '../composer'; +import type { ComposerAttachment } from '@shared/composer'; /* ═══════════════════════════════════════════════════════════════════════ unifiedComposerHostFilePick — pure file-picker residual slices from diff --git a/app/shared/src/workbench/unifiedComposerHostHelpers.test.ts b/app/workbench/src/unifiedComposerHostHelpers.test.ts similarity index 99% rename from app/shared/src/workbench/unifiedComposerHostHelpers.test.ts rename to app/workbench/src/unifiedComposerHostHelpers.test.ts index c4ccc705f..2f51fefef 100644 --- a/app/shared/src/workbench/unifiedComposerHostHelpers.test.ts +++ b/app/workbench/src/unifiedComposerHostHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import type { ComposerState } from '../composer'; +import type { ComposerState } from '@shared/composer'; import { buildComposerHostChromeModel, buildComposerHostViewModel, diff --git a/app/shared/src/workbench/unifiedComposerHostHelpers.ts b/app/workbench/src/unifiedComposerHostHelpers.ts similarity index 100% rename from app/shared/src/workbench/unifiedComposerHostHelpers.ts rename to app/workbench/src/unifiedComposerHostHelpers.ts diff --git a/app/shared/src/workbench/unifiedComposerHostKeyDown.test.ts b/app/workbench/src/unifiedComposerHostKeyDown.test.ts similarity index 100% rename from app/shared/src/workbench/unifiedComposerHostKeyDown.test.ts rename to app/workbench/src/unifiedComposerHostKeyDown.test.ts diff --git a/app/shared/src/workbench/unifiedComposerHostKeyDown.ts b/app/workbench/src/unifiedComposerHostKeyDown.ts similarity index 98% rename from app/shared/src/workbench/unifiedComposerHostKeyDown.ts rename to app/workbench/src/unifiedComposerHostKeyDown.ts index 770c8a48a..8e38e130d 100644 --- a/app/shared/src/workbench/unifiedComposerHostKeyDown.ts +++ b/app/workbench/src/unifiedComposerHostKeyDown.ts @@ -1,4 +1,4 @@ -import type { ComposerAction, ComposerAttachment } from '../composer'; +import type { ComposerAction, ComposerAttachment } from '@shared/composer'; import type { ComposerSubmitBehavior } from './workbenchPreferences'; import { buildTextWithNewline, diff --git a/app/shared/src/workbench/unifiedComposerHostViewModel.test.ts b/app/workbench/src/unifiedComposerHostViewModel.test.ts similarity index 99% rename from app/shared/src/workbench/unifiedComposerHostViewModel.test.ts rename to app/workbench/src/unifiedComposerHostViewModel.test.ts index 38a709249..8f8b1b17c 100644 --- a/app/shared/src/workbench/unifiedComposerHostViewModel.test.ts +++ b/app/workbench/src/unifiedComposerHostViewModel.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { ComposerState } from '../composer'; +import type { ComposerState } from '@shared/composer'; import { deriveUnifiedComposerState } from './unifiedComposerHelpers'; import { buildComposerHostChromeModel, diff --git a/app/shared/src/workbench/unifiedComposerHostViewModel.ts b/app/workbench/src/unifiedComposerHostViewModel.ts similarity index 99% rename from app/shared/src/workbench/unifiedComposerHostViewModel.ts rename to app/workbench/src/unifiedComposerHostViewModel.ts index af218d44c..d99c5795b 100644 --- a/app/shared/src/workbench/unifiedComposerHostViewModel.ts +++ b/app/workbench/src/unifiedComposerHostViewModel.ts @@ -10,7 +10,7 @@ import type { ComposerState, QuoteContext, ReplyToContext, -} from '../composer'; +} from '@shared/composer'; import type { ComposerSubmitBehavior } from './workbenchPreferences'; import { COMPOSER_FILE_ACCEPT, diff --git a/app/shared/src/workbench/useWorkbenchAgentsRoute.test.ts b/app/workbench/src/useWorkbenchAgentsRoute.test.ts similarity index 99% rename from app/shared/src/workbench/useWorkbenchAgentsRoute.test.ts rename to app/workbench/src/useWorkbenchAgentsRoute.test.ts index b59223c76..3ec7d7e6d 100644 --- a/app/shared/src/workbench/useWorkbenchAgentsRoute.test.ts +++ b/app/workbench/src/useWorkbenchAgentsRoute.test.ts @@ -1,7 +1,7 @@ // real_tested=true import { act, renderHook } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import type { WorkbenchAgent } from '../platform'; +import type { WorkbenchAgent } from '@shared/platform'; import { WORKBENCH_MOCK_AGENT_CONFIGS, WORKBENCH_MOCK_AGENT_MODELS, diff --git a/app/shared/src/workbench/useWorkbenchAgentsRoute.ts b/app/workbench/src/useWorkbenchAgentsRoute.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchAgentsRoute.ts rename to app/workbench/src/useWorkbenchAgentsRoute.ts diff --git a/app/shared/src/workbench/useWorkbenchContactsRoute.test.ts b/app/workbench/src/useWorkbenchContactsRoute.test.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchContactsRoute.test.ts rename to app/workbench/src/useWorkbenchContactsRoute.test.ts diff --git a/app/shared/src/workbench/useWorkbenchContactsRoute.ts b/app/workbench/src/useWorkbenchContactsRoute.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchContactsRoute.ts rename to app/workbench/src/useWorkbenchContactsRoute.ts diff --git a/app/shared/src/workbench/useWorkbenchDocsRoute.test.ts b/app/workbench/src/useWorkbenchDocsRoute.test.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchDocsRoute.test.ts rename to app/workbench/src/useWorkbenchDocsRoute.test.ts diff --git a/app/shared/src/workbench/useWorkbenchDocsRoute.ts b/app/workbench/src/useWorkbenchDocsRoute.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchDocsRoute.ts rename to app/workbench/src/useWorkbenchDocsRoute.ts diff --git a/app/shared/src/workbench/useWorkbenchPanelLayout.test.ts b/app/workbench/src/useWorkbenchPanelLayout.test.ts similarity index 99% rename from app/shared/src/workbench/useWorkbenchPanelLayout.test.ts rename to app/workbench/src/useWorkbenchPanelLayout.test.ts index 6f886bf4d..60a8abb89 100644 --- a/app/shared/src/workbench/useWorkbenchPanelLayout.test.ts +++ b/app/workbench/src/useWorkbenchPanelLayout.test.ts @@ -1,7 +1,7 @@ // real_tested=true import { act, renderHook } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import type { AgentHubPlatform } from '../platform'; +import type { AgentHubPlatform } from '@shared/platform'; import { DESKTOP_TOGGLE_SIDEBAR_EVENT } from './desktopChromeEvents'; import type { GlobalRailPage } from './GlobalRail'; import { useWorkbenchPanelLayout } from './useWorkbenchPanelLayout'; diff --git a/app/shared/src/workbench/useWorkbenchPanelLayout.ts b/app/workbench/src/useWorkbenchPanelLayout.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchPanelLayout.ts rename to app/workbench/src/useWorkbenchPanelLayout.ts diff --git a/app/shared/src/workbench/useWorkbenchProfileChrome.test.ts b/app/workbench/src/useWorkbenchProfileChrome.test.ts similarity index 99% rename from app/shared/src/workbench/useWorkbenchProfileChrome.test.ts rename to app/workbench/src/useWorkbenchProfileChrome.test.ts index dafc94f86..b556b42aa 100644 --- a/app/shared/src/workbench/useWorkbenchProfileChrome.test.ts +++ b/app/workbench/src/useWorkbenchProfileChrome.test.ts @@ -1,7 +1,7 @@ // real_tested=true import { act, renderHook } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import type { WorkbenchAgent, WorkbenchConversation } from '../platform'; +import type { WorkbenchAgent, WorkbenchConversation } from '@shared/platform'; import { useWorkbenchProfileChrome } from './useWorkbenchProfileChrome'; import type { UseWorkbenchProfileChromeOptions } from './workbenchProfileChromeHelpers'; diff --git a/app/shared/src/workbench/useWorkbenchProfileChrome.ts b/app/workbench/src/useWorkbenchProfileChrome.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchProfileChrome.ts rename to app/workbench/src/useWorkbenchProfileChrome.ts diff --git a/app/shared/src/workbench/useWorkbenchProjectsRoute.test.ts b/app/workbench/src/useWorkbenchProjectsRoute.test.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchProjectsRoute.test.ts rename to app/workbench/src/useWorkbenchProjectsRoute.test.ts diff --git a/app/shared/src/workbench/useWorkbenchProjectsRoute.ts b/app/workbench/src/useWorkbenchProjectsRoute.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchProjectsRoute.ts rename to app/workbench/src/useWorkbenchProjectsRoute.ts diff --git a/app/shared/src/workbench/useWorkbenchSessionChrome.test.ts b/app/workbench/src/useWorkbenchSessionChrome.test.ts similarity index 99% rename from app/shared/src/workbench/useWorkbenchSessionChrome.test.ts rename to app/workbench/src/useWorkbenchSessionChrome.test.ts index 10d8b80bc..27ee6ddb9 100644 --- a/app/shared/src/workbench/useWorkbenchSessionChrome.test.ts +++ b/app/workbench/src/useWorkbenchSessionChrome.test.ts @@ -7,8 +7,8 @@ import type { RuntimeSessionSummary, WorkbenchAgent, WorkbenchConversation, -} from '../platform'; -import type { TranscriptBlock } from '../transcript'; +} from '@shared/platform'; +import type { TranscriptBlock } from '@shared/transcript'; import { INSPECTOR_DEFAULT_COLLAPSE_EVENT } from './workbenchLayoutConstants'; import { WORKBENCH_MOCK_SETTINGS_DEFAULTS } from './mockData'; import { diff --git a/app/shared/src/workbench/useWorkbenchSessionChrome.ts b/app/workbench/src/useWorkbenchSessionChrome.ts similarity index 98% rename from app/shared/src/workbench/useWorkbenchSessionChrome.ts rename to app/workbench/src/useWorkbenchSessionChrome.ts index 9d8e305c0..7b1f1ed62 100644 --- a/app/shared/src/workbench/useWorkbenchSessionChrome.ts +++ b/app/workbench/src/useWorkbenchSessionChrome.ts @@ -10,9 +10,9 @@ import { composerReducer, createInitialComposerState, saveDraft, -} from '../composer'; -import { toggleAppliedAgentHubTheme } from '../theme'; -import { collectTranscriptEvidence } from '../transcript'; +} from '@shared/composer'; +import { toggleAppliedAgentHubTheme } from '@shared/theme'; +import { collectTranscriptEvidence } from '@shared/transcript'; import { buildMainchainSummary } from './mainchain'; import type { FileItem } from './inspector'; import { WORKBENCH_MOCK_SETTINGS_DEFAULTS } from './mockData'; diff --git a/app/shared/src/workbench/useWorkbenchSettingsRoute.test.ts b/app/workbench/src/useWorkbenchSettingsRoute.test.ts similarity index 99% rename from app/shared/src/workbench/useWorkbenchSettingsRoute.test.ts rename to app/workbench/src/useWorkbenchSettingsRoute.test.ts index 9ccbeca85..b808b33f7 100644 --- a/app/shared/src/workbench/useWorkbenchSettingsRoute.test.ts +++ b/app/workbench/src/useWorkbenchSettingsRoute.test.ts @@ -4,8 +4,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { WORKBENCH_DATA_MODE_STORAGE_KEY, readWorkbenchDataModeOverride, -} from '../demo/dataMode'; -import type { SettingsPort } from '../platform/types'; +} from '@shared/demo/dataMode'; +import type { SettingsPort } from '@shared/platform/types'; import { WORKBENCH_MOCK_SETTINGS_DEFAULTS } from './mockData'; import { createSettingsService } from './settingsService'; import type { SettingsService } from './settingsService'; diff --git a/app/shared/src/workbench/useWorkbenchSettingsRoute.ts b/app/workbench/src/useWorkbenchSettingsRoute.ts similarity index 99% rename from app/shared/src/workbench/useWorkbenchSettingsRoute.ts rename to app/workbench/src/useWorkbenchSettingsRoute.ts index f62b6e4f0..1bea2980d 100644 --- a/app/shared/src/workbench/useWorkbenchSettingsRoute.ts +++ b/app/workbench/src/useWorkbenchSettingsRoute.ts @@ -5,7 +5,7 @@ import { readWorkbenchDataModeOverride, workbenchDataModeDisplayLabel, writeWorkbenchDataModeOverride, -} from '../demo'; +} from '@shared/demo'; import { composerSubmitBehaviorFromLabel, composerSubmitBehaviorLabel, diff --git a/app/shared/src/workbench/useWorkbenchTasksRoute.test.ts b/app/workbench/src/useWorkbenchTasksRoute.test.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchTasksRoute.test.ts rename to app/workbench/src/useWorkbenchTasksRoute.test.ts diff --git a/app/shared/src/workbench/useWorkbenchTasksRoute.ts b/app/workbench/src/useWorkbenchTasksRoute.ts similarity index 100% rename from app/shared/src/workbench/useWorkbenchTasksRoute.ts rename to app/workbench/src/useWorkbenchTasksRoute.ts diff --git a/app/shared/src/workbench/useWorkbenchTranscriptChrome.test.ts b/app/workbench/src/useWorkbenchTranscriptChrome.test.ts similarity index 99% rename from app/shared/src/workbench/useWorkbenchTranscriptChrome.test.ts rename to app/workbench/src/useWorkbenchTranscriptChrome.test.ts index e71630324..f16a82af6 100644 --- a/app/shared/src/workbench/useWorkbenchTranscriptChrome.test.ts +++ b/app/workbench/src/useWorkbenchTranscriptChrome.test.ts @@ -2,8 +2,8 @@ import { act, renderHook } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import type { ReactElement } from 'react'; -import type { WorkbenchConversation } from '../platform'; -import type { TranscriptBlock } from '../transcript'; +import type { WorkbenchConversation } from '@shared/platform'; +import type { TranscriptBlock } from '@shared/transcript'; import type { TranscriptContextMenuEvent, TranscriptPointerEvent } from './transcriptEventTypes'; import { SELECTION_HOLD_CANCEL_DISTANCE, diff --git a/app/shared/src/workbench/useWorkbenchTranscriptChrome.ts b/app/workbench/src/useWorkbenchTranscriptChrome.ts similarity index 98% rename from app/shared/src/workbench/useWorkbenchTranscriptChrome.ts rename to app/workbench/src/useWorkbenchTranscriptChrome.ts index a697c13b4..78e919ac0 100644 --- a/app/shared/src/workbench/useWorkbenchTranscriptChrome.ts +++ b/app/workbench/src/useWorkbenchTranscriptChrome.ts @@ -7,8 +7,8 @@ import { type MutableRefObject, type RefObject, } from 'react'; -import type { ComposerAction } from '../composer'; -import type { ApprovalDecisionAction, TranscriptBlock } from '../transcript'; +import type { ComposerAction } from '@shared/composer'; +import type { ApprovalDecisionAction, TranscriptBlock } from '@shared/transcript'; import type { ContextMenuItem, MultiSelectBarAction } from './floating'; import type { TranscriptContextMenuEvent, TranscriptPointerEvent } from './transcriptEventTypes'; import { diff --git a/app/workbench/src/vite-env.d.ts b/app/workbench/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/app/workbench/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/app/shared/src/workbench/workbenchAgentMapping.test.ts b/app/workbench/src/workbenchAgentMapping.test.ts similarity index 97% rename from app/shared/src/workbench/workbenchAgentMapping.test.ts rename to app/workbench/src/workbenchAgentMapping.test.ts index 5104ac9ee..04c370b21 100644 --- a/app/shared/src/workbench/workbenchAgentMapping.test.ts +++ b/app/workbench/src/workbenchAgentMapping.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { WorkbenchAgent } from '../platform'; +import type { WorkbenchAgent } from '@shared/platform'; import { formatAgentTargetPreference, workbenchAgentStateToAgentState, diff --git a/app/shared/src/workbench/workbenchAgentMapping.ts b/app/workbench/src/workbenchAgentMapping.ts similarity index 98% rename from app/shared/src/workbench/workbenchAgentMapping.ts rename to app/workbench/src/workbenchAgentMapping.ts index 35456c15b..a1263c875 100644 --- a/app/shared/src/workbench/workbenchAgentMapping.ts +++ b/app/workbench/src/workbenchAgentMapping.ts @@ -1,4 +1,4 @@ -import type { WorkbenchAgent } from '../platform'; +import type { WorkbenchAgent } from '@shared/platform'; import type { AgentConfig, ToolPermission } from './pages/AgentsPage'; import { WORKBENCH_MOCK_AGENT_TOOL_OPTIONS } from './mockData'; diff --git a/app/shared/src/workbench/workbenchAgentsPageModel.test.ts b/app/workbench/src/workbenchAgentsPageModel.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchAgentsPageModel.test.ts rename to app/workbench/src/workbenchAgentsPageModel.test.ts diff --git a/app/shared/src/workbench/workbenchAgentsPageModel.ts b/app/workbench/src/workbenchAgentsPageModel.ts similarity index 100% rename from app/shared/src/workbench/workbenchAgentsPageModel.ts rename to app/workbench/src/workbenchAgentsPageModel.ts diff --git a/app/shared/src/workbench/workbenchAgentsRouteHelpers.test.ts b/app/workbench/src/workbenchAgentsRouteHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchAgentsRouteHelpers.test.ts rename to app/workbench/src/workbenchAgentsRouteHelpers.test.ts diff --git a/app/shared/src/workbench/workbenchAgentsRouteHelpers.ts b/app/workbench/src/workbenchAgentsRouteHelpers.ts similarity index 99% rename from app/shared/src/workbench/workbenchAgentsRouteHelpers.ts rename to app/workbench/src/workbenchAgentsRouteHelpers.ts index 638d89922..eb948005b 100644 --- a/app/shared/src/workbench/workbenchAgentsRouteHelpers.ts +++ b/app/workbench/src/workbenchAgentsRouteHelpers.ts @@ -1,4 +1,4 @@ -import type { WorkbenchAgent } from '../platform'; +import type { WorkbenchAgent } from '@shared/platform'; import type { AgentConfig, AgentsPaneId, ModelInfo, ModelState, ToolPermission } from './pages/AgentsPage'; import { WORKBENCH_MOCK_AGENT_CONFIGS, diff --git a/app/shared/src/workbench/workbenchDataMode.test.ts b/app/workbench/src/workbenchDataMode.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchDataMode.test.ts rename to app/workbench/src/workbenchDataMode.test.ts diff --git a/app/shared/src/workbench/workbenchDataMode.ts b/app/workbench/src/workbenchDataMode.ts similarity index 100% rename from app/shared/src/workbench/workbenchDataMode.ts rename to app/workbench/src/workbenchDataMode.ts diff --git a/app/shared/src/workbench/workbenchDocPreview.test.ts b/app/workbench/src/workbenchDocPreview.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchDocPreview.test.ts rename to app/workbench/src/workbenchDocPreview.test.ts diff --git a/app/shared/src/workbench/workbenchDocPreview.ts b/app/workbench/src/workbenchDocPreview.ts similarity index 100% rename from app/shared/src/workbench/workbenchDocPreview.ts rename to app/workbench/src/workbenchDocPreview.ts diff --git a/app/shared/src/workbench/workbenchFrameHelpers.test.ts b/app/workbench/src/workbenchFrameHelpers.test.ts similarity index 99% rename from app/shared/src/workbench/workbenchFrameHelpers.test.ts rename to app/workbench/src/workbenchFrameHelpers.test.ts index 34bba1cfe..4b6097591 100644 --- a/app/shared/src/workbench/workbenchFrameHelpers.test.ts +++ b/app/workbench/src/workbenchFrameHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import type { TranscriptBlock } from '../transcript'; +import type { TranscriptBlock } from '@shared/transcript'; import { DEFAULT_BROWSER_PREVIEW_URL, assignIfDefined, diff --git a/app/shared/src/workbench/workbenchFrameHelpers.ts b/app/workbench/src/workbenchFrameHelpers.ts similarity index 97% rename from app/shared/src/workbench/workbenchFrameHelpers.ts rename to app/workbench/src/workbenchFrameHelpers.ts index d2a80962e..6b23f5f30 100644 --- a/app/shared/src/workbench/workbenchFrameHelpers.ts +++ b/app/workbench/src/workbenchFrameHelpers.ts @@ -1,6 +1,6 @@ import type { PointerEvent as ReactPointerEvent, KeyboardEvent as ReactKeyboardEvent } from 'react'; -import type { AgentHubPlatform, TerminalPort } from '../platform'; -import type { TranscriptBlock } from '../transcript'; +import type { AgentHubPlatform, TerminalPort } from '@shared/platform'; +import type { TranscriptBlock } from '@shared/transcript'; import type { TranscriptContextMenuEvent } from './transcriptEventTypes'; /* ═══════════════════════════════════════════════════════════════════════ diff --git a/app/shared/src/workbench/workbenchFramePartsHelpers.test.ts b/app/workbench/src/workbenchFramePartsHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchFramePartsHelpers.test.ts rename to app/workbench/src/workbenchFramePartsHelpers.test.ts diff --git a/app/shared/src/workbench/workbenchFramePartsHelpers.ts b/app/workbench/src/workbenchFramePartsHelpers.ts similarity index 100% rename from app/shared/src/workbench/workbenchFramePartsHelpers.ts rename to app/workbench/src/workbenchFramePartsHelpers.ts diff --git a/app/shared/src/workbench/workbenchFrameTypes.ts b/app/workbench/src/workbenchFrameTypes.ts similarity index 97% rename from app/shared/src/workbench/workbenchFrameTypes.ts rename to app/workbench/src/workbenchFrameTypes.ts index 1b8779625..b1800197d 100644 --- a/app/shared/src/workbench/workbenchFrameTypes.ts +++ b/app/workbench/src/workbenchFrameTypes.ts @@ -3,8 +3,8 @@ import type { AgentHubPlatform, WorkbenchAgent, WorkbenchConversation, -} from '../platform'; -import type { TranscriptBlock } from '../transcript'; +} from '@shared/platform'; +import type { TranscriptBlock } from '@shared/transcript'; import type { ConnectionStatusKind, GlobalRailPage } from './GlobalRail'; import type { RuntimeEvidenceSnapshot } from './RightInspector'; import type { @@ -120,7 +120,7 @@ export interface ChatConversationHostFrameProps { transcriptChrome: WorkbenchTranscriptChrome; profile: WorkbenchProfileChrome; transcript: TranscriptBlock[]; - transcriptUnreadDivider?: import('../chatview').UnreadDividerDescriptor | undefined; + transcriptUnreadDivider?: import('@shared/chatview').UnreadDividerDescriptor | undefined; connectionStatus?: ConnectionStatusKind | undefined; inspectorCollapsed: boolean; toggleInspector: () => void; diff --git a/app/shared/src/workbench/workbenchLayoutConstants.ts b/app/workbench/src/workbenchLayoutConstants.ts similarity index 100% rename from app/shared/src/workbench/workbenchLayoutConstants.ts rename to app/workbench/src/workbenchLayoutConstants.ts diff --git a/app/shared/src/workbench/workbenchPanelLayoutHelpers.test.ts b/app/workbench/src/workbenchPanelLayoutHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchPanelLayoutHelpers.test.ts rename to app/workbench/src/workbenchPanelLayoutHelpers.test.ts diff --git a/app/shared/src/workbench/workbenchPanelLayoutHelpers.ts b/app/workbench/src/workbenchPanelLayoutHelpers.ts similarity index 99% rename from app/shared/src/workbench/workbenchPanelLayoutHelpers.ts rename to app/workbench/src/workbenchPanelLayoutHelpers.ts index f7a09bfec..b404cfc0b 100644 --- a/app/shared/src/workbench/workbenchPanelLayoutHelpers.ts +++ b/app/workbench/src/workbenchPanelLayoutHelpers.ts @@ -1,5 +1,5 @@ import type React from 'react'; -import type { AgentHubPlatform } from '../platform'; +import type { AgentHubPlatform } from '@shared/platform'; import type { GlobalRailPage } from './GlobalRail'; import { INSPECTOR_COLLAPSE_SNAP_WIDTH, diff --git a/app/shared/src/workbench/workbenchPreferences.test.ts b/app/workbench/src/workbenchPreferences.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchPreferences.test.ts rename to app/workbench/src/workbenchPreferences.test.ts diff --git a/app/shared/src/workbench/workbenchPreferences.ts b/app/workbench/src/workbenchPreferences.ts similarity index 100% rename from app/shared/src/workbench/workbenchPreferences.ts rename to app/workbench/src/workbenchPreferences.ts diff --git a/app/shared/src/workbench/workbenchProfileChromeHelpers.test.ts b/app/workbench/src/workbenchProfileChromeHelpers.test.ts similarity index 99% rename from app/shared/src/workbench/workbenchProfileChromeHelpers.test.ts rename to app/workbench/src/workbenchProfileChromeHelpers.test.ts index cabe9fe6f..f313eb1dc 100644 --- a/app/shared/src/workbench/workbenchProfileChromeHelpers.test.ts +++ b/app/workbench/src/workbenchProfileChromeHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { WorkbenchAgent, WorkbenchConversation } from '../platform'; +import type { WorkbenchAgent, WorkbenchConversation } from '@shared/platform'; import { agentStateLabel, attachAgentProfileAnchor, diff --git a/app/shared/src/workbench/workbenchProfileChromeHelpers.ts b/app/workbench/src/workbenchProfileChromeHelpers.ts similarity index 99% rename from app/shared/src/workbench/workbenchProfileChromeHelpers.ts rename to app/workbench/src/workbenchProfileChromeHelpers.ts index 42fa222b6..26664845b 100644 --- a/app/shared/src/workbench/workbenchProfileChromeHelpers.ts +++ b/app/workbench/src/workbenchProfileChromeHelpers.ts @@ -3,7 +3,7 @@ import type { RefObject, SetStateAction, } from 'react'; -import type { WorkbenchAgent, WorkbenchConversation } from '../platform'; +import type { WorkbenchAgent, WorkbenchConversation } from '@shared/platform'; import type { GlobalRailPage } from './GlobalRail'; import { WORKBENCH_MOCK_AGENT_CONFIGS, WORKBENCH_MOCK_CONTACT_MEMBERS } from './mockData'; diff --git a/app/shared/src/workbench/workbenchProfileSources.test.ts b/app/workbench/src/workbenchProfileSources.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchProfileSources.test.ts rename to app/workbench/src/workbenchProfileSources.test.ts diff --git a/app/shared/src/workbench/workbenchProfileSources.ts b/app/workbench/src/workbenchProfileSources.ts similarity index 100% rename from app/shared/src/workbench/workbenchProfileSources.ts rename to app/workbench/src/workbenchProfileSources.ts diff --git a/app/shared/src/workbench/workbenchProjectPreview.test.ts b/app/workbench/src/workbenchProjectPreview.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchProjectPreview.test.ts rename to app/workbench/src/workbenchProjectPreview.test.ts diff --git a/app/shared/src/workbench/workbenchProjectPreview.ts b/app/workbench/src/workbenchProjectPreview.ts similarity index 100% rename from app/shared/src/workbench/workbenchProjectPreview.ts rename to app/workbench/src/workbenchProjectPreview.ts diff --git a/app/shared/src/workbench/workbenchProjectsPort.ts b/app/workbench/src/workbenchProjectsPort.ts similarity index 100% rename from app/shared/src/workbench/workbenchProjectsPort.ts rename to app/workbench/src/workbenchProjectsPort.ts diff --git a/app/shared/src/workbench/workbenchRoutesHelpers.test.ts b/app/workbench/src/workbenchRoutesHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchRoutesHelpers.test.ts rename to app/workbench/src/workbenchRoutesHelpers.test.ts diff --git a/app/shared/src/workbench/workbenchRoutesHelpers.ts b/app/workbench/src/workbenchRoutesHelpers.ts similarity index 99% rename from app/shared/src/workbench/workbenchRoutesHelpers.ts rename to app/workbench/src/workbenchRoutesHelpers.ts index e1236a4bb..4e614bac8 100644 --- a/app/shared/src/workbench/workbenchRoutesHelpers.ts +++ b/app/workbench/src/workbenchRoutesHelpers.ts @@ -1,4 +1,4 @@ -import type { LocalCliDiscoveryManifest, RuntimeSessionSummary } from '../platform'; +import type { LocalCliDiscoveryManifest, RuntimeSessionSummary } from '@shared/platform'; import type { ContactsPageProps } from './pages/ContactsPage'; import type { DocsPageProps } from './pages/DocsPage'; import type { ProjectsPageProps } from './pages/ProjectsPage'; diff --git a/app/shared/src/workbench/workbenchRoutesTypes.ts b/app/workbench/src/workbenchRoutesTypes.ts similarity index 98% rename from app/shared/src/workbench/workbenchRoutesTypes.ts rename to app/workbench/src/workbenchRoutesTypes.ts index 00c4f1d34..a863b8185 100644 --- a/app/shared/src/workbench/workbenchRoutesTypes.ts +++ b/app/workbench/src/workbenchRoutesTypes.ts @@ -1,4 +1,4 @@ -import type { LocalCliDiscoveryManifest, WorkbenchAgent } from '../platform'; +import type { LocalCliDiscoveryManifest, WorkbenchAgent } from '@shared/platform'; import type { DocRow, ProjectDraft, @@ -61,7 +61,7 @@ export interface WorkbenchRoutesProps { documentsActions?: WorkbenchDocumentsActions | undefined; localCliDiscovery?: LocalCliDiscoveryManifest | null | undefined; /** Desktop settings: local runtime session import list (#1192). */ - sessionImportItems?: import('../platform').RuntimeSessionSummary[] | undefined; + sessionImportItems?: import('@shared/platform').RuntimeSessionSummary[] | undefined; sessionImportLoading?: boolean | undefined; sessionImportError?: string | null | undefined; sessionImportVisible?: boolean | undefined; diff --git a/app/shared/src/workbench/workbenchSessionChromeHelpers.test.ts b/app/workbench/src/workbenchSessionChromeHelpers.test.ts similarity index 98% rename from app/shared/src/workbench/workbenchSessionChromeHelpers.test.ts rename to app/workbench/src/workbenchSessionChromeHelpers.test.ts index 965311b27..b41e66fd5 100644 --- a/app/shared/src/workbench/workbenchSessionChromeHelpers.test.ts +++ b/app/workbench/src/workbenchSessionChromeHelpers.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import type { TranscriptBlock } from '../transcript'; -import type { WorkbenchAgent, WorkbenchConversation } from '../platform'; +import type { TranscriptBlock } from '@shared/transcript'; +import type { WorkbenchAgent, WorkbenchConversation } from '@shared/platform'; import { LOCAL_CLI_DISCOVERY_FALLBACK, buildInspectorTranscriptViews, diff --git a/app/shared/src/workbench/workbenchSessionChromeHelpers.ts b/app/workbench/src/workbenchSessionChromeHelpers.ts similarity index 97% rename from app/shared/src/workbench/workbenchSessionChromeHelpers.ts rename to app/workbench/src/workbenchSessionChromeHelpers.ts index 29c76946e..28696fbfd 100644 --- a/app/shared/src/workbench/workbenchSessionChromeHelpers.ts +++ b/app/workbench/src/workbenchSessionChromeHelpers.ts @@ -7,15 +7,15 @@ import type { ComposerMention, ComposerAction, ComposerState, -} from '../composer'; +} from '@shared/composer'; import type { AgentHubPlatform, LocalCliDiscoveryManifest, RuntimeSessionSummary, WorkbenchAgent, WorkbenchConversation, -} from '../platform'; -import type { RuntimeEvidenceSnapshot } from '../inspector'; +} from '@shared/platform'; +import type { RuntimeEvidenceSnapshot } from '@shared/inspector'; import type { TranscriptBlock, ContextUsageTranscriptBlock, @@ -24,13 +24,13 @@ import type { SubtaskTranscriptBlock, ChildAgentTranscriptBlock, EvidenceRef, -} from '../transcript'; +} from '@shared/transcript'; import type { MainchainSummary } from './mainchain'; import type { FileItem, RunResultInfo } from './inspector'; import type { SettingsService } from './settingsService'; import type { GlobalRailPage } from './GlobalRail'; -import { matchesShortcut } from '../utils/keyboardUtils'; -import type { KeyboardEventLike } from '../utils/keyboardUtils'; +import { matchesShortcut } from '@shared/utils/keyboardUtils'; +import type { KeyboardEventLike } from '@shared/utils/keyboardUtils'; /* ═══════════════════════════════════════════════════════════════════════ workbenchSessionChromeHelpers — pure residual slices from diff --git a/app/shared/src/workbench/workbenchState.test.ts b/app/workbench/src/workbenchState.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchState.test.ts rename to app/workbench/src/workbenchState.test.ts diff --git a/app/shared/src/workbench/workbenchState.ts b/app/workbench/src/workbenchState.ts similarity index 100% rename from app/shared/src/workbench/workbenchState.ts rename to app/workbench/src/workbenchState.ts diff --git a/app/shared/src/workbench/workbenchStateApplyEvent.test.ts b/app/workbench/src/workbenchStateApplyEvent.test.ts similarity index 99% rename from app/shared/src/workbench/workbenchStateApplyEvent.test.ts rename to app/workbench/src/workbenchStateApplyEvent.test.ts index aa297ebab..5775833cf 100644 --- a/app/shared/src/workbench/workbenchStateApplyEvent.test.ts +++ b/app/workbench/src/workbenchStateApplyEvent.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { AnyEvent } from '../events'; +import type { AnyEvent } from '@shared/events'; import { initialWorkbenchState } from './workbenchState'; import { applyEvent } from './workbenchStateApplyEvent'; import type { WorkbenchState } from './workbenchStateTypes'; diff --git a/app/shared/src/workbench/workbenchStateApplyEvent.ts b/app/workbench/src/workbenchStateApplyEvent.ts similarity index 99% rename from app/shared/src/workbench/workbenchStateApplyEvent.ts rename to app/workbench/src/workbenchStateApplyEvent.ts index e5be814d3..c175e97b0 100644 --- a/app/shared/src/workbench/workbenchStateApplyEvent.ts +++ b/app/workbench/src/workbenchStateApplyEvent.ts @@ -1,5 +1,5 @@ -import type { AnyEvent } from '../events'; -import type { RunLogs } from '../types'; +import type { AnyEvent } from '@shared/events'; +import type { RunLogs } from '@shared/types'; import { approvalKind, chunkText, diff --git a/app/shared/src/workbench/workbenchStateHelpers.test.ts b/app/workbench/src/workbenchStateHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchStateHelpers.test.ts rename to app/workbench/src/workbenchStateHelpers.test.ts diff --git a/app/shared/src/workbench/workbenchStateHelpers.ts b/app/workbench/src/workbenchStateHelpers.ts similarity index 99% rename from app/shared/src/workbench/workbenchStateHelpers.ts rename to app/workbench/src/workbenchStateHelpers.ts index 0a0650615..d96b1ca1c 100644 --- a/app/shared/src/workbench/workbenchStateHelpers.ts +++ b/app/workbench/src/workbenchStateHelpers.ts @@ -5,7 +5,7 @@ import type { RunLogs, Thread, ThreadItem, -} from '../types'; +} from '@shared/types'; import type { WorkbenchSnapshot, WorkbenchSnapshotData, diff --git a/app/shared/src/workbench/workbenchStateTypes.ts b/app/workbench/src/workbenchStateTypes.ts similarity index 96% rename from app/shared/src/workbench/workbenchStateTypes.ts rename to app/workbench/src/workbenchStateTypes.ts index da27009a7..0001f8792 100644 --- a/app/shared/src/workbench/workbenchStateTypes.ts +++ b/app/workbench/src/workbenchStateTypes.ts @@ -1,4 +1,4 @@ -import type { AnyEvent } from '../events'; +import type { AnyEvent } from '@shared/events'; import type { Approval, Artifact, @@ -10,7 +10,7 @@ import type { Runner, Thread, ThreadItem, -} from '../types'; +} from '@shared/types'; export type WorkbenchConnectionStatus = | 'idle' diff --git a/app/shared/src/workbench/workbenchTaskGroups.test.ts b/app/workbench/src/workbenchTaskGroups.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchTaskGroups.test.ts rename to app/workbench/src/workbenchTaskGroups.test.ts diff --git a/app/shared/src/workbench/workbenchTaskGroups.ts b/app/workbench/src/workbenchTaskGroups.ts similarity index 100% rename from app/shared/src/workbench/workbenchTaskGroups.ts rename to app/workbench/src/workbenchTaskGroups.ts diff --git a/app/shared/src/workbench/workbenchTasksPageModel.test.ts b/app/workbench/src/workbenchTasksPageModel.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksPageModel.test.ts rename to app/workbench/src/workbenchTasksPageModel.test.ts diff --git a/app/shared/src/workbench/workbenchTasksPageModel.ts b/app/workbench/src/workbenchTasksPageModel.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksPageModel.ts rename to app/workbench/src/workbenchTasksPageModel.ts diff --git a/app/shared/src/workbench/workbenchTasksRouteGroupHelpers.test.ts b/app/workbench/src/workbenchTasksRouteGroupHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksRouteGroupHelpers.test.ts rename to app/workbench/src/workbenchTasksRouteGroupHelpers.test.ts diff --git a/app/shared/src/workbench/workbenchTasksRouteGroupHelpers.ts b/app/workbench/src/workbenchTasksRouteGroupHelpers.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksRouteGroupHelpers.ts rename to app/workbench/src/workbenchTasksRouteGroupHelpers.ts diff --git a/app/shared/src/workbench/workbenchTasksRouteHelpers.test.ts b/app/workbench/src/workbenchTasksRouteHelpers.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksRouteHelpers.test.ts rename to app/workbench/src/workbenchTasksRouteHelpers.test.ts diff --git a/app/shared/src/workbench/workbenchTasksRouteHelpers.ts b/app/workbench/src/workbenchTasksRouteHelpers.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksRouteHelpers.ts rename to app/workbench/src/workbenchTasksRouteHelpers.ts diff --git a/app/shared/src/workbench/workbenchTasksRoutePlanners.test.ts b/app/workbench/src/workbenchTasksRoutePlanners.test.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksRoutePlanners.test.ts rename to app/workbench/src/workbenchTasksRoutePlanners.test.ts diff --git a/app/shared/src/workbench/workbenchTasksRoutePlanners.ts b/app/workbench/src/workbenchTasksRoutePlanners.ts similarity index 100% rename from app/shared/src/workbench/workbenchTasksRoutePlanners.ts rename to app/workbench/src/workbenchTasksRoutePlanners.ts diff --git a/app/shared/src/workbench/workbenchTestFixtures.ts b/app/workbench/src/workbenchTestFixtures.ts similarity index 97% rename from app/shared/src/workbench/workbenchTestFixtures.ts rename to app/workbench/src/workbenchTestFixtures.ts index 96a4ff00e..3f45aea6e 100644 --- a/app/shared/src/workbench/workbenchTestFixtures.ts +++ b/app/workbench/src/workbenchTestFixtures.ts @@ -4,8 +4,8 @@ * any workbench test can reuse them without importing the integration file. */ -import type { WorkbenchAgent } from '../platform/types'; -import type { TranscriptBlock } from '../transcript/types'; +import type { WorkbenchAgent } from '@shared/platform/types'; +import type { TranscriptBlock } from '@shared/transcript/types'; export const workbenchAgents: WorkbenchAgent[] = [ { diff --git a/app/shared/src/workbench/workbenchTranscriptChromeActionMappers.test.ts b/app/workbench/src/workbenchTranscriptChromeActionMappers.test.ts similarity index 99% rename from app/shared/src/workbench/workbenchTranscriptChromeActionMappers.test.ts rename to app/workbench/src/workbenchTranscriptChromeActionMappers.test.ts index 27dbda17a..feacceebe 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeActionMappers.test.ts +++ b/app/workbench/src/workbenchTranscriptChromeActionMappers.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, vi } from 'vitest'; import type { ReactNode } from 'react'; import { fireEvent, render } from '@testing-library/react'; -import type { TranscriptBlock } from '../transcript'; +import type { TranscriptBlock } from '@shared/transcript'; import { applyTranscriptChromeSideEffects, buildTranscriptContextMenuGroups, diff --git a/app/shared/src/workbench/workbenchTranscriptChromeActionMappers.ts b/app/workbench/src/workbenchTranscriptChromeActionMappers.ts similarity index 99% rename from app/shared/src/workbench/workbenchTranscriptChromeActionMappers.ts rename to app/workbench/src/workbenchTranscriptChromeActionMappers.ts index 75644cd1d..41b6837cb 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeActionMappers.ts +++ b/app/workbench/src/workbenchTranscriptChromeActionMappers.ts @@ -1,7 +1,7 @@ import { createElement } from 'react'; -import type { ComposerAction } from '../composer'; -import type { WorkbenchConversation } from '../platform'; -import type { ApprovalDecisionAction, TranscriptBlock } from '../transcript'; +import type { ComposerAction } from '@shared/composer'; +import type { WorkbenchConversation } from '@shared/platform'; +import type { ApprovalDecisionAction, TranscriptBlock } from '@shared/transcript'; import type { ContextMenuItem, MultiSelectBarAction } from './floating'; import { EmojiPicker } from './floating/EmojiPicker'; import { ForwardConversationPicker } from './floating/ForwardConversationPicker'; diff --git a/app/shared/src/workbench/workbenchTranscriptChromeHelpers.test.ts b/app/workbench/src/workbenchTranscriptChromeHelpers.test.ts similarity index 99% rename from app/shared/src/workbench/workbenchTranscriptChromeHelpers.test.ts rename to app/workbench/src/workbenchTranscriptChromeHelpers.test.ts index 44ad6cefe..318fd5189 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeHelpers.test.ts +++ b/app/workbench/src/workbenchTranscriptChromeHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it, vi } from 'vitest'; -import type { TranscriptBlock } from '../transcript'; +import type { TranscriptBlock } from '@shared/transcript'; import { SELECTION_HOLD_CANCEL_DISTANCE, SELECTION_HOLD_DELAY_MS, diff --git a/app/shared/src/workbench/workbenchTranscriptChromeHelpers.ts b/app/workbench/src/workbenchTranscriptChromeHelpers.ts similarity index 98% rename from app/shared/src/workbench/workbenchTranscriptChromeHelpers.ts rename to app/workbench/src/workbenchTranscriptChromeHelpers.ts index 8e5b4ae96..07f53d3f8 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeHelpers.ts +++ b/app/workbench/src/workbenchTranscriptChromeHelpers.ts @@ -1,6 +1,6 @@ -import type { ComposerAction } from '../composer'; -import type { WorkbenchConversation } from '../platform'; -import type { ApprovalDecisionAction, TranscriptBlock } from '../transcript'; +import type { ComposerAction } from '@shared/composer'; +import type { WorkbenchConversation } from '@shared/platform'; +import type { ApprovalDecisionAction, TranscriptBlock } from '@shared/transcript'; import type { ContextMenuItem, MultiSelectBarAction } from './floating'; import { applyTranscriptChromeSideEffects, diff --git a/app/shared/src/workbench/workbenchTranscriptChromeLabels.test.ts b/app/workbench/src/workbenchTranscriptChromeLabels.test.ts similarity index 98% rename from app/shared/src/workbench/workbenchTranscriptChromeLabels.test.ts rename to app/workbench/src/workbenchTranscriptChromeLabels.test.ts index 2e080c1ad..6381eb804 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeLabels.test.ts +++ b/app/workbench/src/workbenchTranscriptChromeLabels.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { TranscriptBlock } from '../transcript'; +import type { TranscriptBlock } from '@shared/transcript'; import { SELECTION_HOLD_CANCEL_DISTANCE, SELECTION_HOLD_DELAY_MS, diff --git a/app/shared/src/workbench/workbenchTranscriptChromeLabels.ts b/app/workbench/src/workbenchTranscriptChromeLabels.ts similarity index 98% rename from app/shared/src/workbench/workbenchTranscriptChromeLabels.ts rename to app/workbench/src/workbenchTranscriptChromeLabels.ts index 83ea937df..403403adb 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeLabels.ts +++ b/app/workbench/src/workbenchTranscriptChromeLabels.ts @@ -1,4 +1,4 @@ -import type { ApprovalDecisionAction, TranscriptBlock } from '../transcript'; +import type { ApprovalDecisionAction, TranscriptBlock } from '@shared/transcript'; /* ═══════════════════════════════════════════════════════════════════════ workbenchTranscriptChromeLabels — pure residual label / title / quote diff --git a/app/shared/src/workbench/workbenchTranscriptChromeStateHelpers.test.ts b/app/workbench/src/workbenchTranscriptChromeStateHelpers.test.ts similarity index 98% rename from app/shared/src/workbench/workbenchTranscriptChromeStateHelpers.test.ts rename to app/workbench/src/workbenchTranscriptChromeStateHelpers.test.ts index c0525a9d4..c8878efe8 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeStateHelpers.test.ts +++ b/app/workbench/src/workbenchTranscriptChromeStateHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import type { TranscriptBlock } from '../transcript'; +import type { TranscriptBlock } from '@shared/transcript'; import { addIdIfMissing, createEnterSelectionSnapshot, diff --git a/app/shared/src/workbench/workbenchTranscriptChromeStateHelpers.ts b/app/workbench/src/workbenchTranscriptChromeStateHelpers.ts similarity index 99% rename from app/shared/src/workbench/workbenchTranscriptChromeStateHelpers.ts rename to app/workbench/src/workbenchTranscriptChromeStateHelpers.ts index fc7275edc..d9e78c137 100644 --- a/app/shared/src/workbench/workbenchTranscriptChromeStateHelpers.ts +++ b/app/workbench/src/workbenchTranscriptChromeStateHelpers.ts @@ -1,4 +1,4 @@ -import type { TranscriptBlock } from '../transcript'; +import type { TranscriptBlock } from '@shared/transcript'; import { SELECTION_HOLD_CANCEL_DISTANCE, SELECTION_HOLD_DELAY_MS, diff --git a/app/workbench/tsconfig.json b/app/workbench/tsconfig.json new file mode 100644 index 000000000..36dfc729d --- /dev/null +++ b/app/workbench/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "ES2021", + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": false, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "noEmit": true, + "lib": ["ES2021", "DOM"], + "baseUrl": ".", + "paths": { + "@shared/*": ["../shared/src/*"], + "@shared": ["../shared/src/index.ts"] + } + }, + "include": ["src"], + "exclude": [ + "src/__tests__", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/**/*.stories.tsx", + "src/**/*.stories.ts" + ] +} diff --git a/app/workbench/vitest.config.ts b/app/workbench/vitest.config.ts new file mode 100644 index 000000000..56e8ca8ff --- /dev/null +++ b/app/workbench/vitest.config.ts @@ -0,0 +1,45 @@ +import { defineConfig } from 'vitest/config'; +import path from 'node:path'; +import { createCoverage } from '../test-config/coverage'; + +export default defineConfig({ + ssr: { + noExternal: true, + }, + resolve: { + // 与 app/shared、app/web、app/desktop 同源:dedupe react/react-dom, + // 避免测试图里出现两份 React(hooks + zustand v5 store 会失效)。 + dedupe: ['react', 'react-dom'], + alias: { + // workbench→shared 单向依赖(#1759):深导入走 @shared 别名,与 + // web/desktop 既有约定一致;包级依赖在 package.json 声明。 + '@shared': path.resolve(__dirname, '..', 'shared', 'src'), + // 测试套件横跨 shared 源码(@shared/testing/i18n 等)与 workbench + // 源码,两个包各自的 node_modules 会解析出两份 react-i18next/i18next, + // 导致测试 i18n 实例注册在另一份模块上、组件查不到翻译。与 + // desktop/web 的 vitest 配置相同,强制收敛到本包单一副本。 + '@testing-library/react': path.resolve(__dirname, 'node_modules', '@testing-library', 'react'), + 'i18next': path.resolve(__dirname, 'node_modules', 'i18next'), + 'react-i18next': path.resolve(__dirname, 'node_modules', 'react-i18next'), + 'react': path.resolve(__dirname, 'node_modules', 'react'), + 'react-dom': path.resolve(__dirname, 'node_modules', 'react-dom'), + }, + }, + test: { + globals: true, + environment: 'jsdom', + // 与 shared/desktop 配置一致的 worker 上限:全量测试时默认全核并发 + // 会导致 forks worker 启动/回收超时,限制并发后 coverage 全量运行稳定。 + maxWorkers: 4, + include: ['src/**/*.test.ts', 'src/**/*.test.tsx'], + exclude: ['**/node_modules/**', '**/dist/**', '**/.worktrees/**', '**/reference/**'], + setupFiles: ['./src/__tests__/setup.ts'], + coverage: createCoverage({ + // 绝对 floors:2026-08-21 独立成包(#1759 第二阶段)首次实测后设定, + // 规则同其他包:floor(实测)-1,低于 60 的维度是提升目标非永久豁免。 + thresholds: { lines: 59, branches: 52, functions: 52, statements: 59 }, + // workbenchProjectsPort.ts — 纯类型契约(#1546),零运行时语句 + exclude: ['src/workbenchProjectsPort.ts'], + }), + }, +}); diff --git a/docs/architecture.md b/docs/architecture.md index 9807b1729..0b26b1940 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -37,7 +37,8 @@ Mobile shared workbench (viewer / limited control) | 层 | 目录 | 职责 | |---|---|---| -| Shared UI | `app/shared/` | workbench、transcript、composer、inspector、platform contracts | +| Shared UI | `app/shared/` | transcript、composer、inspector、platform contracts、design token | +| Workbench | `app/workbench/` | 端级工作台 shell(`@agenthub/workbench`,依赖方向 workbench → shared 单向,#1759) | | Desktop | `app/desktop/` | Tauri shell、Desktop adapter、Local Edge、本机能力 | | Web | `app/web/` | Hub session、Web adapter、远程审批和查看 | | Mobile | `app/mobile-rn/` | RN shell、Mobile adapter、Hub viewer surface | diff --git a/docs/architecture/04-frontend-data-flow.md b/docs/architecture/04-frontend-data-flow.md index 659b1d507..db94982a1 100644 --- a/docs/architecture/04-frontend-data-flow.md +++ b/docs/architecture/04-frontend-data-flow.md @@ -13,13 +13,13 @@ ```text Desktop/Web/Mobile app shell -> platform adapter - -> app/shared workbench + chatview + -> @agenthub/workbench shell + app/shared chatview -> transcript normalizer + renderer ``` 规则: -- `app/shared/` 拥有 workbench、transcript、composer、inspector 和 chatview 组件合同。 +- `app/workbench/`(`@agenthub/workbench`)拥有端级 workbench shell;`app/shared/` 拥有 transcript、composer、inspector 和 chatview 组件合同。依赖方向固定为 workbench → shared 单向(#1759)。 - Desktop/Web/Mobile 只提供 platform adapter、认证、查询、runtime bridge 和壳层能力。 - Shared UI 不直接调用 Tauri invoke、Hub client 或 Edge client。 - Desktop renderer 不直接执行 CLI;本地执行走 Tauri host typed API、Local Edge 和 Edge adapter。 @@ -49,7 +49,7 @@ UI 可以根据 `capabilities` 隐藏或禁用动作,但不能 fork 另一套 | 责任 | Owner | |---|---| -| Workbench shell / rail / inspector / routes | `app/shared/src/workbench/` | +| Workbench shell / rail / inspector / routes | `app/workbench/src/` | | Chat transcript renderer | `app/shared/src/chatview/` | | Transcript block types, ordering, Hub/Edge normalization, evidence | `app/shared/src/transcript/` | | Data mode compatibility contract | `app/shared/src/demo/dataMode.ts`, `app/shared/src/testing/e2eDataModeContract.ts` | diff --git a/docs/architecture/07-design-system-ssot.md b/docs/architecture/07-design-system-ssot.md index 386214fa3..61350f741 100644 --- a/docs/architecture/07-design-system-ssot.md +++ b/docs/architecture/07-design-system-ssot.md @@ -18,7 +18,7 @@ Issue: #466 (P9.1 SSOT map) · residual hardcode closed via #879 / #910 / #1021 | **Theme preset runtime SSOT** | `app/shared/src/themePresets.ts` (re-exported from `theme.ts`) | preset list, meta, storage key, `data-theme-preset` apply | | **Cross-platform alias registry** | `app/shared/src/designTokens.ts` | `--td-*` ↔ web/mobile alias map + surface rules (**TS registry, not CSS values**) | | **UI primitives** | `app/shared/src/ui/**` | Shared components + module CSS | -| **Workbench shell** | `app/shared/src/workbench/**` | Product shell CSS/TS | +| **Workbench shell** | `app/workbench/src/**` | Product shell CSS/TS(#1759 独立为 `@agenthub/workbench`) | | **Chatview scoped fork** | `app/shared/src/chatview/design/tokens.css` | Re-declares surfaces/text/status under `.chatview` (intentional isolation; value-drift risk) | | **Desktop re-exports** | `app/desktop/src/styles/{tokens,themes,presets}.css` | Thin `@import` of shared (+ desktop border overrides in presets) | | **Web re-exports** | `app/web/src/styles/{tokens,themes,presets}.css` | Thin `@import` of shared (+ web glass border overrides) | diff --git a/docs/governance/verifier-map.md b/docs/governance/verifier-map.md index 238732f6f..165570975 100644 --- a/docs/governance/verifier-map.md +++ b/docs/governance/verifier-map.md @@ -26,6 +26,7 @@ | 演示诚实:stub/fixture 不得冒充真实登录/API | `scripts/verify/verify-real-e2e-contract.py` | checks.yml → validate | | OpenAPI↔hub router 合同一致 | `scripts/verify/verify-openapi-contract.py` | checks.yml → validate | | shared 内不出现 Edge 客户端实现 | `scripts/verify/verify-shared-boundary.py` | checks.yml → validate | +| 前端包依赖方向:shared 不得 import workbench,workbench 只依赖 shared(#1759) | `scripts/verify/verify-frontend-package-boundary.py`(负向自测 `scripts/verify/tests/verify-frontend-package-boundary.Tests.py`)+ app/eslint.config.mjs `no-restricted-imports` | checks.yml → validate | | shared barrel 不泄漏 Edge 导出 | `scripts/verify/verify-shared-barrel.py` | checks.yml → validate | | Hub handler 不直连 repository | `scripts/verify/verify-hub-layering.py` | checks.yml → validate | | router 方法必须在 conventions.md 文档化 | `scripts/verify/verify-conventions.py` | checks.yml → validate | diff --git a/scripts/verify/coverage-baseline.json b/scripts/verify/coverage-baseline.json index 64e50eeda..b37443f0e 100644 --- a/scripts/verify/coverage-baseline.json +++ b/scripts/verify/coverage-baseline.json @@ -26,6 +26,23 @@ "skipped": 0 } }, + "@agenthub/workbench": { + "dir": "app/workbench", + "coverage": { + "lines": 83.67, + "statements": 82.28, + "functions": 81.66, + "branches": 75.61 + }, + "uncoveredFiles": 7, + "coverageNote": "2026-08-21 首次入基线(#1759 第二阶段 workbench 独立成 @agenthub/workbench 第 6 包;拆包时 vitest.config floors 59/52/52/59 即按本次实测设定,低于 60 的维度是提升目标非永久豁免)。uncovered 7 文件:MessageSearchPanel.stories.tsx / WorkbenchDocsRouteView.tsx / index.ts / auxPanel/AuxPanel.tsx / floating/PersonPanel.tsx / pages/projects/ProjectMembers.tsx / pages/projects/ProjectTabViews.tsx。", + "tests": { + "total": 1020, + "passed": 1020, + "failed": 0, + "skipped": 0 + } + }, "agenthub-web": { "dir": "app/web", "coverage": { diff --git a/scripts/verify/i18n-callsite-baseline.json b/scripts/verify/i18n-callsite-baseline.json index 5337bb0c1..c1fb1a903 100644 --- a/scripts/verify/i18n-callsite-baseline.json +++ b/scripts/verify/i18n-callsite-baseline.json @@ -1,18 +1,18 @@ { "_comment": "Baseline of i18n callsite violations (source files with CJK string literals but no useTranslation import). The verify gate fails when the current count EXCEEDS this baseline; lower this number as more callsites are wired to useTranslation.", - "scanRoot": "app/shared/src", - "totalViolations": 86, - "totalCjkLiteralLines": 763, + "scanRoot": "app/shared/src + app/workbench/src", + "totalViolations": 88, + "totalCjkLiteralLines": 779, "files": [ { - "path": "chatview/adapterMapBlock.ts", + "path": "shared/chatview/adapterMapBlock.ts", "cjkLiteralLines": 1, "samples": [ 263 ] }, { - "path": "chatview/components/UnreadDivider.tsx", + "path": "shared/chatview/components/UnreadDivider.tsx", "cjkLiteralLines": 2, "samples": [ 3, @@ -20,7 +20,7 @@ ] }, { - "path": "chatview/design/labels.ts", + "path": "shared/chatview/design/labels.ts", "cjkLiteralLines": 10, "samples": [ 164, @@ -31,7 +31,7 @@ ] }, { - "path": "chatview/types.ts", + "path": "shared/chatview/types.ts", "cjkLiteralLines": 2, "samples": [ 72, @@ -39,7 +39,7 @@ ] }, { - "path": "composer/types.ts", + "path": "shared/composer/types.ts", "cjkLiteralLines": 2, "samples": [ 15, @@ -47,7 +47,7 @@ ] }, { - "path": "demo/dataMode.ts", + "path": "shared/demo/dataMode.ts", "cjkLiteralLines": 3, "samples": [ 149, @@ -56,7 +56,7 @@ ] }, { - "path": "demo/teamrunDemo.ts", + "path": "shared/demo/teamrunDemo.ts", "cjkLiteralLines": 4, "samples": [ 163, @@ -66,7 +66,7 @@ ] }, { - "path": "demo/workbenchDemoAgents.ts", + "path": "shared/demo/workbenchDemoAgents.ts", "cjkLiteralLines": 19, "samples": [ 19, @@ -77,7 +77,7 @@ ] }, { - "path": "demo/workbenchDemoMessages.ts", + "path": "shared/demo/workbenchDemoMessages.ts", "cjkLiteralLines": 8, "samples": [ 19, @@ -88,7 +88,7 @@ ] }, { - "path": "demo/workbenchDemoStore.ts", + "path": "shared/demo/workbenchDemoStore.ts", "cjkLiteralLines": 5, "samples": [ 137, @@ -99,14 +99,14 @@ ] }, { - "path": "demo/workbenchDemoTypes.ts", + "path": "shared/demo/workbenchDemoTypes.ts", "cjkLiteralLines": 1, "samples": [ 39 ] }, { - "path": "inspector/inspectorEvidence.ts", + "path": "shared/inspector/inspectorEvidence.ts", "cjkLiteralLines": 10, "samples": [ 270, @@ -117,7 +117,7 @@ ] }, { - "path": "transcript/edgeEventEvidence.ts", + "path": "shared/transcript/edgeEventEvidence.ts", "cjkLiteralLines": 4, "samples": [ 183, @@ -127,7 +127,7 @@ ] }, { - "path": "transcript/normalizeHubMessages.ts", + "path": "shared/transcript/normalizeHubMessages.ts", "cjkLiteralLines": 2, "samples": [ 64, @@ -135,14 +135,14 @@ ] }, { - "path": "transcript/normalizeThreadItems.ts", + "path": "shared/transcript/normalizeThreadItems.ts", "cjkLiteralLines": 1, "samples": [ 101 ] }, { - "path": "transcript/pinMap.ts", + "path": "shared/transcript/pinMap.ts", "cjkLiteralLines": 3, "samples": [ 17, @@ -151,7 +151,7 @@ ] }, { - "path": "ui/cjkRemarkPlugin.ts", + "path": "shared/ui/cjkRemarkPlugin.ts", "cjkLiteralLines": 12, "samples": [ 29, @@ -162,7 +162,7 @@ ] }, { - "path": "ui/useCopiedFlag.ts", + "path": "shared/ui/useCopiedFlag.ts", "cjkLiteralLines": 1, "samples": [ 17 @@ -198,6 +198,15 @@ 60 ] }, + { + "path": "workbench/__tests__/helpers.tsx", + "cjkLiteralLines": 3, + "samples": [ + 110, + 111, + 112 + ] + }, { "path": "workbench/agentCapabilities.ts", "cjkLiteralLines": 7, @@ -829,6 +838,17 @@ 32 ] }, + { + "path": "workbench/workbenchTestFixtures.ts", + "cjkLiteralLines": 13, + "samples": [ + 14, + 22, + 34, + 67, + 76 + ] + }, { "path": "workbench/workbenchTranscriptChromeLabels.ts", "cjkLiteralLines": 2, diff --git a/scripts/verify/tests/verify-frontend-package-boundary.Tests.py b/scripts/verify/tests/verify-frontend-package-boundary.Tests.py new file mode 100644 index 000000000..666ae2ed4 --- /dev/null +++ b/scripts/verify/tests/verify-frontend-package-boundary.Tests.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""Fixture mutation tests for verify-frontend-package-boundary.py. + +Builds a synthetic app/ tree (shared + workbench) in a temp dir and asserts: + +1. clean tree → exit 0 +2. shared imports `@agenthub/workbench` → exit 1 +3. shared reaches workbench via relative path → exit 1 +4. workbench imports an app package (agenthub-desktop) → exit 1 +5. workbench relative import escapes the workbench->shared boundary → exit 1 +6. workbench deep import into shared (relative) stays legal → exit 0 +""" + +import os +import subprocess +import sys +import tempfile +import unittest + +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +VERIFIER_PATH = os.path.join(REPO_ROOT, "scripts", "verify", "verify-frontend-package-boundary.py") + +CLEAN_SHARED = "export const sharedThing = 1;\n" +CLEAN_WORKBENCH = ( + "import { sharedThing } from '@shared/thing';\n" + "import type { HubClient } from '@agenthub/shared/hub/hubClient';\n" + "export const workbenchThing = sharedThing;\n" + "void workbenchThing; void 0 as HubClient | undefined;\n" +) + + +def build_tree(tmp_dir: str, shared_body: str, workbench_body: str) -> str: + app_root = os.path.join(tmp_dir, "app") + shared_src = os.path.join(app_root, "shared", "src") + workbench_src = os.path.join(app_root, "workbench", "src") + os.makedirs(shared_src) + os.makedirs(workbench_src) + with open(os.path.join(shared_src, "thing.ts"), "w", encoding="utf-8") as handle: + handle.write(shared_body) + with open(os.path.join(workbench_src, "index.ts"), "w", encoding="utf-8") as handle: + handle.write(workbench_body) + return app_root + + +def run_verifier(app_root: str) -> tuple: + result = subprocess.run( + [sys.executable, VERIFIER_PATH, "--AppRoot", app_root], + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + return result.returncode, (result.stdout or "") + (result.stderr or "") + + +class FrontendPackageBoundaryTests(unittest.TestCase): + def assert_gate(self, shared_body: str, workbench_body: str, expect_fail: bool, needle: str = ""): + with tempfile.TemporaryDirectory(prefix="agenthub-pkg-boundary-") as tmp_dir: + app_root = build_tree(tmp_dir, shared_body, workbench_body) + code, output = run_verifier(app_root) + if expect_fail: + self.assertNotEqual(code, 0, f"expected failure, got 0:\n{output}") + if needle: + self.assertIn(needle, output) + else: + self.assertEqual(code, 0, f"expected pass, got {code}:\n{output}") + + def test_clean_tree_passes(self): + self.assert_gate(CLEAN_SHARED, CLEAN_WORKBENCH, expect_fail=False) + + def test_shared_importing_workbench_package_fails(self): + shared_body = "import { workbenchThing } from '@agenthub/workbench';\nvoid workbenchThing;\n" + self.assert_gate(shared_body, CLEAN_WORKBENCH, expect_fail=True, needle="@agenthub/workbench") + + def test_shared_relative_escape_into_workbench_fails(self): + shared_body = "import { workbenchThing } from '../../workbench/src/index';\nvoid workbenchThing;\n" + self.assert_gate(shared_body, CLEAN_WORKBENCH, expect_fail=True, needle="escapes into workbench") + + def test_workbench_importing_app_package_fails(self): + workbench_body = "import { desktopHelper } from 'agenthub-desktop/src/helper';\nvoid desktopHelper;\n" + self.assert_gate(CLEAN_SHARED, workbench_body, expect_fail=True, needle="must not import app packages") + + def test_workbench_relative_escape_fails(self): + workbench_body = "import { leak } from '../../desktop/src/leak';\nvoid leak;\n" + self.assert_gate(CLEAN_SHARED, workbench_body, expect_fail=True, needle="escapes the workbench->shared") + + def test_workbench_relative_shared_import_is_legal(self): + workbench_body = "import { sharedThing } from '../shared/src/thing';\nvoid sharedThing;\n" + # fixture tree 里 workbench/src 与 shared/src 平级于 app/,故 + # ../../shared/src/thing 才是越界合法路径的模拟。 + with tempfile.TemporaryDirectory(prefix="agenthub-pkg-boundary-") as tmp_dir: + app_root = os.path.join(tmp_dir, "app") + shared_src = os.path.join(app_root, "shared", "src") + workbench_src = os.path.join(app_root, "workbench", "src", "nested") + os.makedirs(shared_src) + os.makedirs(workbench_src) + with open(os.path.join(shared_src, "thing.ts"), "w", encoding="utf-8") as handle: + handle.write(CLEAN_SHARED) + with open(os.path.join(workbench_src, "index.ts"), "w", encoding="utf-8") as handle: + handle.write("import { sharedThing } from '../../../shared/src/thing';\nvoid sharedThing;\n") + code, output = run_verifier(app_root) + self.assertEqual(code, 0, f"expected pass, got {code}:\n{output}") + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/verify/verify-ci-gates.py b/scripts/verify/verify-ci-gates.py index 2dce1d3fd..45385caee 100644 --- a/scripts/verify/verify-ci-gates.py +++ b/scripts/verify/verify-ci-gates.py @@ -357,6 +357,8 @@ def main() -> int: assert_step_continue_on_error(validate, "Verify OpenAPI↔hub router contract", False) assert_contains(validate, r"Verify Shared Edge-free boundary", "validate job must run the Shared Edge-free boundary verifier") assert_contains(validate, r"scripts/verify/verify-shared-boundary.py", "validate job must call scripts/verify/verify-shared-boundary.ps1") + assert_contains(validate, r"scripts/verify/verify-frontend-package-boundary.py", "validate job must call the frontend package boundary verifier (#1759)") + assert_step_continue_on_error(validate, "Verify frontend package boundary (workbench -> shared only, #1759)", False) assert_step_continue_on_error(validate, "Verify Shared Edge-free boundary", False) assert_contains(validate, r"Verify Shared barrel Edge-export ban", "validate job must run the Shared barrel Edge-export ban verifier") assert_contains(validate, r"scripts/verify/verify-shared-barrel.py", "validate job must call scripts/verify/verify-shared-barrel.ps1") @@ -399,7 +401,7 @@ def main() -> int: assert_not_contains(backend_perf, r"load-test", "backend-perf-leak-gates must not claim load/capacity smoke") assert_contains(changes, r"dorny/paths-filter@", "changes job must use dorny/paths-filter (major governed by #1580 runtime gate)") - assert_contains(changes, re.escape("app/shared/src/workbench/**"), "changes job must watch workbench paths") + assert_contains(changes, re.escape("app/workbench/**"), "changes job must watch workbench package paths (#1759)") assert_contains(changes, re.escape("app/shared/src/styles/**"), "changes job must watch shared styles") assert_contains(changes, re.escape("app/web/scripts/visual-qa*"), "changes job must watch web visual-qa scripts") assert_contains(changes, re.escape("app/desktop/scripts/visual-qa*"), "changes job must watch desktop visual-qa scripts") diff --git a/scripts/verify/verify-coverage-baseline.py b/scripts/verify/verify-coverage-baseline.py index 4343a2b14..80e763ad0 100644 --- a/scripts/verify/verify-coverage-baseline.py +++ b/scripts/verify/verify-coverage-baseline.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 """Frontend coverage baseline gate (baseline must not regress) — ps1 迁移。 -Runs vitest --coverage for the four frontend packages (@agenthub/shared, -agenthub-web, agenthub-desktop, agenthub-mobile-rn), parses the json-summary -coverage report and the json test-results report, then asserts: +Runs vitest --coverage for the five frontend packages (@agenthub/shared, +@agenthub/workbench, agenthub-web, agenthub-desktop, agenthub-mobile-rn), +parses the json-summary coverage report and the json test-results report, +then asserts: 1. Every coverage metric (lines/branches/functions/statements) is >= the value recorded in scripts/verify/coverage-baseline.json (no regression). 2. Every coverage.include file with statements but 0% lines is an @@ -171,7 +172,7 @@ def main() -> int: parser.add_argument("--BaselinePath", default=DEFAULT_BASELINE_PATH, help="path to coverage-baseline.json") parser.add_argument("--AppDir", default=DEFAULT_APP_DIR, help="workspace root (the app dir)") parser.add_argument("--KeepReports", action="store_true", help="do not delete generated coverage/ and test-results.json after parsing") - # --OnlyPackages: comma-separated baseline package keys to run (CI 4-package + # --OnlyPackages: comma-separated baseline package keys to run (CI 5-package # matrix splits the 10min gate into per-package parallel jobs). Default: all. parser.add_argument("--OnlyPackages", default="", help="comma-separated package keys to run; empty = all") # Re-baseline is deliberately stricter than the ordinary gate: it must run diff --git a/scripts/verify/verify-frontend-package-boundary.py b/scripts/verify/verify-frontend-package-boundary.py new file mode 100644 index 000000000..5565240ae --- /dev/null +++ b/scripts/verify/verify-frontend-package-boundary.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +"""verify-frontend-package-boundary — 前端包依赖方向门禁(#1759 第二阶段)。 + +workbench 从 shared 独立为 `@agenthub/workbench` 后,依赖方向固定为 +workbench → shared 单向: + +1. shared 永远不得 import workbench —— 扫 app/shared/src,禁止 + `@agenthub/workbench*` / `@workbench*` 包名与任何解析进 + app/workbench 的相对导入; +2. workbench 不得反向引用其它端包 —— 扫 app/workbench/src,相对导入 + 解析出 workbench 之外时必须落在 shared/src(深导入走 @shared 别名 + 或包名均可),且禁止 import desktop/web/mobile-rn 包。 + +Mobile 侧的 workbench 禁令由 app/mobile-rn/scripts/verify-boundaries.mjs +负责,不在本脚本重复。 + +Usage: + python scripts/verify/verify-frontend-package-boundary.py + python scripts/verify/verify-frontend-package-boundary.py --AppRoot +""" + +import argparse +import os +import re +import sys + +SOURCE_EXTENSIONS = {".ts", ".tsx", ".js", ".jsx", ".mjs"} + +# import/export ... from 'x' | import 'x' | import('x') | require('x') | vi.mock('x') +IMPORT_PATTERN = re.compile( + r"""(?: + (?:import|export)\s+(?:type\s+)?[^'"]*?\s+from\s* | + (?:import|export)\s+ | + import\s*\(\s* | + require\s*\(\s* | + vi\.mock\s*\(\s* + )['"]([^'"]+)['"]""", + re.VERBOSE, +) + +WORKBENCH_PACKAGE_PREFIXES = ("@agenthub/workbench", "@workbench") +FORBIDDEN_WORKBENCH_CONSUMER_PACKAGES = ( + "agenthub-desktop", + "agenthub-web", + "agenthub-mobile-rn", +) + + +def find_repo_root() -> str: + root = os.path.dirname(os.path.abspath(__file__)) + for _ in range(4): + if os.path.isfile(os.path.join(root, "AGENTS.md")): + return root + root = os.path.dirname(root) + raise RuntimeError("cannot locate AgentHub repository root") + + +def walk_source_files(root: str): + for dirpath, dirnames, filenames in os.walk(root): + dirnames.sort() + for name in sorted(filenames): + if os.path.splitext(name)[1] in SOURCE_EXTENSIONS: + yield os.path.join(dirpath, name) + + +def extract_specifiers(text: str): + return [match for match in IMPORT_PATTERN.findall(text) if match] + + +def check_shared_tree(app_root: str, failures: list) -> int: + """规则 1:shared 不得出现任何指向 workbench 的导入。""" + shared_src = os.path.join(app_root, "shared", "src") + workbench_root = os.path.join(app_root, "workbench") + scanned = 0 + + for file_path in walk_source_files(shared_src): + with open(file_path, encoding="utf-8", errors="replace") as handle: + text = handle.read() + relative_file = os.path.relpath(file_path, app_root) + + for specifier in extract_specifiers(text): + if specifier.startswith(WORKBENCH_PACKAGE_PREFIXES): + failures.append( + f"{relative_file}: shared must not import workbench ({specifier})" + ) + continue + if specifier.startswith("."): + resolved = os.path.normpath( + os.path.join(os.path.dirname(file_path), specifier) + ) + if resolved == workbench_root or resolved.startswith(workbench_root + os.sep): + failures.append( + f"{relative_file}: relative import escapes into workbench ({specifier})" + ) + scanned += 1 + + return scanned + + +def check_workbench_tree(app_root: str, failures: list) -> int: + """规则 2:workbench 只允许依赖 shared,不得引用其它端包或越界相对导入。""" + workbench_src = os.path.join(app_root, "workbench", "src") + shared_src = os.path.join(app_root, "shared", "src") + scanned = 0 + + for file_path in walk_source_files(workbench_src): + with open(file_path, encoding="utf-8", errors="replace") as handle: + text = handle.read() + relative_file = os.path.relpath(file_path, app_root) + + for specifier in extract_specifiers(text): + if specifier.startswith(FORBIDDEN_WORKBENCH_CONSUMER_PACKAGES): + failures.append( + f"{relative_file}: workbench must not import app packages ({specifier})" + ) + continue + if specifier.startswith("."): + resolved = os.path.normpath( + os.path.join(os.path.dirname(file_path), specifier) + ) + inside_workbench = resolved == workbench_src or resolved.startswith( + workbench_src + os.sep + ) + inside_shared = resolved == shared_src or resolved.startswith( + shared_src + os.sep + ) + if not inside_workbench and not inside_shared: + failures.append( + f"{relative_file}: relative import escapes the workbench->shared " + f"boundary ({specifier})" + ) + scanned += 1 + + return scanned + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--AppRoot", + default=None, + help="前端 monorepo 根(默认 /app;测试可注入 fixture 树)", + ) + args = parser.parse_args() + + app_root = os.path.abspath(args.AppRoot) if args.AppRoot else os.path.join(find_repo_root(), "app") + + failures: list = [] + print("=== Frontend package boundary (workbench -> shared only, #1759) ===") + + shared_scanned = check_shared_tree(app_root, failures) + workbench_scanned = check_workbench_tree(app_root, failures) + print(f"Scanned {shared_scanned} shared + {workbench_scanned} workbench source file(s).") + + if failures: + for failure in failures: + print(f" FAIL {failure}") + print(f"\n Passed: 0 | Failed: {len(failures)}") + return 1 + + print(" PASS no dependency-direction violations") + return 0 + + +if __name__ == "__main__": + try: + sys.exit(main()) + except Exception as exc: # noqa: BLE001 —— 顶层兜底,对齐其它 verifier + print(f"ERROR: {exc}", file=sys.stderr) + sys.exit(1) diff --git a/scripts/verify/verify-i18n-callsites.py b/scripts/verify/verify-i18n-callsites.py index 6294d6228..fba50a0cd 100644 --- a/scripts/verify/verify-i18n-callsites.py +++ b/scripts/verify/verify-i18n-callsites.py @@ -1,8 +1,9 @@ #!/usr/bin/env python3 """verify-i18n-callsites.py — guard against regressing hardcoded CJK literals. -Scans app/shared/src for source files that still embed CJK string literals -(Zh/Hans) but do NOT import `useTranslation`. Each such file is a "callsite +Scans app/shared/src and app/workbench/src (#1759) for source files that +still embed CJK string literals (Zh/Hans) but do NOT import `useTranslation`. +Each such file is a "callsite violation": a component/module that renders user-visible Chinese without going through react-i18next, so it cannot be localized to English. @@ -34,7 +35,12 @@ # Repository root is two levels up from this script (scripts/verify -> repo). REPO_ROOT = Path(__file__).resolve().parent.parent.parent -SCAN_ROOT = REPO_ROOT / "app" / "shared" / "src" +# #1759:workbench 独立成包后,CJK 字面量 ratchet 同时覆盖 shared 与 +# workbench 两棵源码树(相对路径带包名前缀,避免重名文件互相遮蔽)。 +SCAN_ROOTS = ( + ("shared", REPO_ROOT / "app" / "shared" / "src"), + ("workbench", REPO_ROOT / "app" / "workbench" / "src"), +) BASELINE_FILE = REPO_ROOT / "scripts" / "verify" / "i18n-callsite-baseline.json" # A CJK Unified Ideograph range covers Zh/Hans/Hant, Hiragana, Katakana, and @@ -73,15 +79,17 @@ # Files that are the i18n dictionaries themselves, plus test/setup files. # These are the legitimate homes for CJK literals and must never be flagged. +# Paths are prefixed with the scan-root package name (shared/workbench). DICTIONARY_PATHS = { - "chatview/i18n/resources.ts", - "i18n/workbench.ts", - "i18n/index.ts", + "shared/chatview/i18n/resources.ts", + "shared/i18n/workbench.ts", + "shared/i18n/index.ts", } TEST_SUFFIXES = (".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx") SETUP_FILES = { - "__tests__/setup.ts", - "__tests__/setup.tsx", + "shared/__tests__/setup.ts", + "shared/__tests__/setup.tsx", + "workbench/__tests__/setup.ts", # Test scaffolding: zh map fixtures + lobehub mock payloads. Not shipped # UI strings; safe to exclude from the i18n callsite gate. "workbench/workbenchTestMocks.ts", @@ -129,38 +137,41 @@ def scan_cjk_literal_lines(text: str) -> list[int]: def collect_violations() -> list[Violation]: - """Walk SCAN_ROOT and return all callsite violations, sorted by path.""" - if not SCAN_ROOT.is_dir(): - return [] - + """Walk all scan roots and return callsite violations, sorted by path.""" violations: list[Violation] = [] - for source_path in sorted(SCAN_ROOT.rglob("*")): - if not source_path.is_file(): - continue - if source_path.suffix not in {".ts", ".tsx"}: - continue - relative = source_path.relative_to(SCAN_ROOT).as_posix() - if is_dictionary_file(relative): - continue - text = source_path.read_text(encoding="utf-8", errors="replace") - cjk_lines = scan_cjk_literal_lines(text) - if not cjk_lines: - continue - if file_has_use_translation(text): - # The file imports useTranslation, so its CJK literals are presumed - # to be t() keys/values or comments — not hardcoded UI strings. - # This is a heuristic; the baseline absorbs edge cases. + for package_name, scan_root in SCAN_ROOTS: + if not scan_root.is_dir(): continue - sample = tuple(cjk_lines[:5]) - violations.append( - Violation( - relative_path=relative, - cjk_line_count=len(cjk_lines), - sample_lines=sample, + for source_path in sorted(scan_root.rglob("*")): + if not source_path.is_file(): + continue + if source_path.suffix not in {".ts", ".tsx"}: + continue + relative = f"{package_name}/{source_path.relative_to(scan_root).as_posix()}" + if is_dictionary_file(relative): + continue + + text = source_path.read_text(encoding="utf-8", errors="replace") + cjk_lines = scan_cjk_literal_lines(text) + if not cjk_lines: + continue + if file_has_use_translation(text): + # The file imports useTranslation, so its CJK literals are + # presumed to be t() keys/values or comments — not hardcoded + # UI strings. This is a heuristic; the baseline absorbs edge + # cases. + continue + + sample = tuple(cjk_lines[:5]) + violations.append( + Violation( + relative_path=relative, + cjk_line_count=len(cjk_lines), + sample_lines=sample, + ) ) - ) violations.sort(key=lambda violation: violation.relative_path) return violations @@ -184,7 +195,7 @@ def write_baseline(violations: list[Violation]) -> None: "fails when the current count EXCEEDS this baseline; lower this " "number as more callsites are wired to useTranslation." ), - "scanRoot": "app/shared/src", + "scanRoot": "app/shared/src + app/workbench/src", "totalViolations": len(violations), "totalCjkLiteralLines": sum(v.cjk_line_count for v in violations), "files": [v.to_dict() for v in violations], @@ -224,7 +235,8 @@ def main(argv: list[str] | None = None) -> int: baseline_count = int(baseline.get("totalViolations", -1)) baseline_lines = int(baseline.get("totalCjkLiteralLines", -1)) - print(f"[i18n-callsites] scan root : {SCAN_ROOT.relative_to(REPO_ROOT)}") + print("[i18n-callsites] scan roots: " + + ", ".join(str(scan_root.relative_to(REPO_ROOT)) for _, scan_root in SCAN_ROOTS)) print(f"[i18n-callsites] baseline : {baseline_count} files / " f"{baseline_lines} CJK literal lines") print(f"[i18n-callsites] current : {current_count} files / " diff --git a/scripts/verify/verify-shared-boundary.py b/scripts/verify/verify-shared-boundary.py index 677ae9064..1d296729a 100644 --- a/scripts/verify/verify-shared-boundary.py +++ b/scripts/verify/verify-shared-boundary.py @@ -21,9 +21,10 @@ else: raise RuntimeError("cannot locate AgentHub repository root") -# 三端共同消费的 shared 子树,必须保持 Edge-free +# 三端共同消费的 shared/workbench 子树,必须保持 Edge-free +# (#1759 第二阶段起 workbench 独立为 app/workbench 包) SCAN_DIRS = [ - "app/shared/src/workbench", + "app/workbench/src", "app/shared/src/chatview", "app/shared/src/ui", ] @@ -119,7 +120,7 @@ def fail_line(text: str) -> None: defect_notes += 1 print(f" KNOWN DEFECT {label} in {rel}:{index + 1} — {reason}") else: - tree = re.sub(r"^app/shared/src/", "", rel, flags=re.IGNORECASE) + tree = re.sub(r"^app/(?:shared|workbench)/src/", "", rel, flags=re.IGNORECASE) tree = re.sub(r"/.*$", "", tree, flags=re.IGNORECASE) fail_line(f"{label} found in {rel}:{index + 1} — Edge client must not leak into shared {tree}") diff --git a/scripts/verify/verify-shared-ui-hubclient.py b/scripts/verify/verify-shared-ui-hubclient.py index ccfd781d1..cf6c42556 100644 --- a/scripts/verify/verify-shared-ui-hubclient.py +++ b/scripts/verify/verify-shared-ui-hubclient.py @@ -1,20 +1,23 @@ #!/usr/bin/env python3 -"""verify-shared-ui-hubclient — shared UI 层 hubClient 依赖门禁(ps1 迁移,契约见 server docs/design/ps1-to-python-migration.md)。 +"""verify-shared-ui-hubclient — 前端表现层 hubClient 依赖门禁(ps1 迁移,契约见 server docs/design/ps1-to-python-migration.md)。 -shared 表现层(ui/components/workbench/chatview)不得 value-import Hub 客户端(hubClient), -也不得引用具体 HubClient 类型。 +shared 表现层(ui/components/chatview)与 workbench 包(#1759 第二阶段起 +独立为 app/workbench 的 @agenthub/workbench)不得 value-import Hub 客户端 +(hubClient),也不得引用具体 HubClient 类型。 -策略(audit-A A-V2 / A7#4 "shared UI no hubClient",#1546 硬化):shared 表现模块 +策略(audit-A A-V2 / A7#4 "shared UI no hubClient",#1546 硬化):表现模块 只消费窄领域端口(如 WorkbenchProjectsPort),由平台 composition root 注入。 -把 hubClient value-import 进 shared UI 层是分层违规;type-only import 同样是违规—— +把 hubClient value-import 进表现层是分层违规;type-only import 同样是违规—— 它把表现契约耦合到具体客户端的方法面(正是 #1546 移除的耦合)。 经验基线:#1546 之前 workbench/* 有 4 处 `import type { HubClient }`;#1546 之后 -shared workbench 只引用领域端口。本门禁的价值是防止未来 runtime 耦合与 type 耦合回归。 +shared workbench 只引用领域端口。#1759 拆包后,workbench 源码树整体纳入 +本门禁扫描(WORKBENCH_SRC_DIR),shared 侧默认 scope 不再含 workbench。 +本门禁的价值是防止未来 runtime 耦合与 type 耦合回归。 用法: python scripts/verify/verify-shared-ui-hubclient.py - python scripts/verify/verify-shared-ui-hubclient.py --shared-src-dir app/shared/src --scope-dirs ui components workbench chatview + python scripts/verify/verify-shared-ui-hubclient.py --shared-src-dir app/shared/src --scope-dirs ui components chatview """ import argparse @@ -34,6 +37,10 @@ # #1546 之后 shared 表现层已完全解耦。 KNOWN_DEFECTS = set() +# #1759 第二阶段:workbench 独立为 @agenthub/workbench 包后,门禁额外 +# 扫描该包整棵源码树(与 shared 表现层同一条分层红线)。 +WORKBENCH_SRC_DIR = "app/workbench/src" + def pass_check(text: str) -> None: global PASSED @@ -54,11 +61,19 @@ def strip_comments(line: str) -> str: def collect_scope_files(shared_src_dir: str, scope_dirs) -> list: + """Collect .ts/.tsx files under shared scopes plus the workbench package. + + scan roots are (src_dir, scope) pairs relative to the repo root; scope "" + means the src_dir itself is the scan root (workbench package tree). + """ + scan_roots = [(shared_src_dir, scope) for scope in scope_dirs] + scan_roots.append((WORKBENCH_SRC_DIR, "")) files = [] - for scope in scope_dirs: - full = os.path.join(ROOT, shared_src_dir.replace("/", os.sep), scope) + for src_dir, scope in scan_roots: + full = os.path.join(ROOT, src_dir.replace("/", os.sep), scope) + label = f"{src_dir}/{scope}" if scope else src_dir if not os.path.isdir(full): - fail_check(f"scope directory missing: {shared_src_dir}/{scope}") + fail_check(f"scope directory missing: {label}") continue for dirpath, dirnames, filenames in os.walk(full): dirnames.sort() @@ -76,13 +91,13 @@ def is_allowlisted(rel: str) -> bool: def main() -> int: parser = argparse.ArgumentParser(description="shared UI hubClient gate verifier (#1468)") parser.add_argument("--shared-src-dir", default="app/shared/src", help="shared source directory (default: app/shared/src)") - parser.add_argument("--scope-dirs", nargs="+", default=["ui", "components", "workbench", "chatview"], help="scope subdirectories (default: ui components workbench chatview)") + parser.add_argument("--scope-dirs", nargs="+", default=["ui", "components", "chatview"], help="scope subdirectories (default: ui components chatview; workbench package is always scanned)") args = parser.parse_args() files = collect_scope_files(args.shared_src_dir, args.scope_dirs) print("\n=== Shared UI hubClient gate (no value/type reference to hubClient) ===") - print(f"Scanning {len(files)} .ts/.tsx file(s) in shared/{', '.join(args.scope_dirs)}.") + print(f"Scanning {len(files)} .ts/.tsx file(s) in shared/{', '.join(args.scope_dirs)} + workbench package ({WORKBENCH_SRC_DIR}).") import_from_re = re.compile(r"""from\s*['"]([^'"]*)""" + FORBIDDEN_SPEC_PATTERN + r"""([^'"]*)['"]""") side_effect_re = re.compile(r"""^\s*import\s+['"]([^'"]*)""" + FORBIDDEN_SPEC_PATTERN + r"""([^'"]*)['"]""")