# Monorepo Refactoring Plan: Decoupling Designer, Engines, and Materials This document outlines the step-by-step plan to refactor the current `form-designer` monolithic structure into a clean, decoupled Monorepo architecture. **Core Philosophy:** Separation of Concerns. The Designer acts as a shell, Engines enforce logic, and Materials provide UI implementation. --- ## 🏗 Architecture Overview | Layer | Package | Responsibility | | :---------------- | :----------------------- | :--------------------------------------------------------------------------------------------------- | | **Foundation** | `@lingshu/types` | Shared interfaces, DSL definitions, and constants. | | | `@lingshu/core-utils` | Pure JS utilities (framework agnostic). | | **Logic Engines** | `@lingshu/user-script` | Runtime for standard User Scripts (Hooks, Actions) and Rule Engine (Low-code logical linkage). | | **Materials** | `@lingshu/widget-pc` | PC Vue components, widget configurations, and property panels. | | | `@lingshu/widget-mobile` | Mobile Vue components (Future). | | **Host/Editor** | `@lingshu/form-designer` | The visual editor shell. Implements adapters for engines and exposes extension points for materials. | | **Application** | `apps/lcdp` | The assembler entry point that wires everything together. | --- ## 📅 Phased Execution Plan The refactoring is divided into 3 autonomous phases. **At the end of each phase, the project MUST be buildable and runnable.** ### Phase 1: Foundation Construction (Infrastructure) **Goal**: Establish a shared language (`types`) to break circular dependencies and prepare for code migration. - **Boundary**: No functional logic changes. Only moving definitions and constants. - **Deliverable**: Codebase uses `@lingshu/types` for shared entities instead of relative imports. - **Verification**: 1. `pnpm build` passes for all packages. 2. `npm run dev` in `form-designer` works exactly as before. **Tasks:** 1. **Define Shared Types**: Extract `WidgetSchema`, `UserScriptContext`, `RuntimeEventApiBO` to `@lingshu/types`. 2. **Define Injection Keys**: Extract `USER_SCRIPT_EVENT_BUS_KEY` to `@lingshu/types`. 3. **Refactor Imports**: Update `form-designer` and `widget-pc` to import from `@lingshu/types`. --- ### Phase 2: Logic Engine Extraction (The Brain) **Goal**: Isolate the "User Script" and "Rule Engine" logic into a standalone package that doesn't depend on UI stores directly. - **Boundary**: `packages/user-script` contains pure logic. Access to Store/API is done via **Dependency Injection**. - **Deliverable**: A new `@lingshu/user-script` package. `form-designer` initializes this engine by injecting its internal state adapters. - **Verification**: 1. User Scripts (e.g., `onClick` logs) work in the Designer preview. 2. Rule Linkages (e.g., input A changes -> input B hides) work in the Designer preview. **Tasks:** 1. **Create Package**: Set up `packages/user-script` workspace. 2. **Migrate Logic**: Move `src/config/user-script` (Definitions) and `src/utils/user-script` (Runtime) to the new package. 3. **Refactor for DI**: Transform functions that directly import `PageStore` to accept `context` or `adapter` arguments. 4. **Wire Up**: In `form-designer/main.ts` (or boot sequence), call `initScriptEngine(adapters)` from the new package. --- ### Phase 3: Material Decoupling (The Body) **Goal**: Move all PC-specific widget configurations and implementations out of the Designer. - **Boundary**: `form-designer` becomes unaware of specific widgets. It loads whatever is passed to its `.use()` method. - **Deliverable**: `packages/widget-pc` contains all material definitions (`config/*`) and components components. - **Verification**: 1. Designer starts empty initially (theoretically). 2. After injecting `WidgetPC`, the left palette shows PC components. 3. Dragging components to canvas works correctly. **Tasks:** 1. **Move Configs**: Relocate `form-designer/src/config` (Materials, Settings) to `packages/widget-pc/src/config`. 2. **Refactor Hooks**: Move specific widget hooks (like `components/anchor/useCompEvent`) to `widget-pc`. 3. **Plugin Architecture**: create a `registerWidgets` export in `widget-pc`. 4. **Injection**: In the App Entry/Main, import `registerWidgets` and pass it to the Designer instance. --- ## 🔄 Dependency Injection Strategy (Crucial) To achieve decoupling, we use standard **Dependency Injection**. **1. Service Injection (Logic Layer)** Instead of importing `axios` or `pinia` in `user-script`: ```typescript // packages/user-script/src/index.ts export function initEngine(context: IScriptContext) { // context.network.request(...) // context.store.getFieldValue(...) } ``` **2. Event Injection (Material Layer)** Widgets use `inject` to communicate with the engine, without importing it. ```typescript // packages/widget-pc/src/hooks/useCompEvent.ts const bus = inject(USER_SCRIPT_EVENT_BUS_KEY); bus.emit("CLIENT_EVENT", payload); ```