# WorkUtils Unified UI P1 API Document Export Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Recompose the API document export plugin to match the approved segmented-mode form, honest progress/action row, and resizable execution log while preserving requests, proxy behavior, history, and base-class configuration. **Architecture:** Keep `ApiDocExportRequest`, `RunApiDocExportUseCase`, `AsyncOperationController`, and `ExportHistoryPane` unchanged. Extract presentation-only controls from the 900-line `ApiDocWindow`: `ApiDocModeSelector` renders modes, `ApiDocExportForm` owns mode-specific inputs, and `ResizableLogPane` owns log layout; `ApiDocWindow` remains the composition and event-wiring boundary. **Tech Stack:** Java 17, JavaFX 17.0.15, AtlantaFX 2.0.1, P0 `DesignTokens`/`IconFactory`, JUnit 5, Maven Wrapper. ## Global Constraints - Preserve tabs exactly: “导出配置 / 导出历史 / 基类配置”. - Preserve all three request modes and the explicit local-proxy checkbox; do not change request defaults or generated output. - Use one segmented mode row instead of three oversized cards. - Running state shows determinate progress when available, “导出中…”, and “取消” only when the operation has real cancellation support. - Navigation and history viewing remain usable during export; only request-mutating inputs are disabled. - The log pane is collapsible and vertically resizable, defaults to 10–14 visible lines, and uses the P0 mono token. - Do not expose credentials, tokens, or credential-bearing URLs in logs. - Follow TDD and preserve `LoadingButtonBinder` as the only loading-button renderer. --- ## File Structure ### Create - `src/main/java/com/example/workutils/biz/apidoc/presentation/ApiDocModeSelector.java` - `src/main/java/com/example/workutils/biz/apidoc/presentation/ApiDocExportForm.java` - `src/main/java/com/example/workutils/system/ui/ResizableLogPane.java` - `src/test/java/com/example/workutils/biz/apidoc/presentation/ApiDocModeSelectorTest.java` - `src/test/java/com/example/workutils/biz/apidoc/presentation/ApiDocExportFormTest.java` - `src/test/java/com/example/workutils/system/ui/ResizableLogPaneTest.java` ### Modify - `src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java` - `src/main/resources/css/light-theme.css` - `src/test/java/com/example/workutils/biz/apidoc/ApiDocWindowArchitectureTest.java` - `src/test/java/com/example/workutils/biz/apidoc/ApiDocHistoryMappingTest.java` ## Task 1: Extract the segmented mode selector **Files:** - Create: `src/main/java/com/example/workutils/biz/apidoc/presentation/ApiDocModeSelector.java` - Create: `src/test/java/com/example/workutils/biz/apidoc/presentation/ApiDocModeSelectorTest.java` - Modify: `src/main/resources/css/light-theme.css` **Interfaces:** - Produces: `selectedModeProperty(): ObjectProperty`. - Produces: `setMode(Mode)`, `getMode()`, and exactly three accessible toggle segments. - [ ] **Step 1: Write the failing selector test** ```java @Test void exposesThreeModesAndUpdatesOneSelectedProperty() { ApiDocModeSelector selector = fx(ApiDocModeSelector::new); assertEquals(List.of(Mode.PATH, Mode.FQN, Mode.BATCH), selector.modes()); fx(() -> selector.setMode(Mode.FQN)); assertEquals(Mode.FQN, fx(selector::getMode)); assertEquals(1, fx(selector::selectedCount)); } ``` - [ ] **Step 2: Run and verify failure** Run: `.\mvnw.cmd -q -Dtest=ApiDocModeSelectorTest test` Expected: FAIL because the component is missing. - [ ] **Step 3: Implement the selector** Use a `ToggleGroup` and three `ToggleButton`s. Each segment uses P0 `AppIcon` values, visible title/helper labels, CSS class `segmented-mode`, and an accessible label containing both title and helper text. Prevent clearing the final selection. - [ ] **Step 4: Run test and commit** Run: `.\mvnw.cmd -q -Dtest=ApiDocModeSelectorTest test` Expected: PASS. ```powershell git add src/main/java/com/example/workutils/biz/apidoc/presentation/ApiDocModeSelector.java src/test/java/com/example/workutils/biz/apidoc/presentation/ApiDocModeSelectorTest.java src/main/resources/css/light-theme.css git commit -m "重构 API 导出模式选择器" ``` ## Task 2: Extract mode-specific form state without changing requests **Files:** - Create: `src/main/java/com/example/workutils/biz/apidoc/presentation/ApiDocExportForm.java` - Create: `src/test/java/com/example/workutils/biz/apidoc/presentation/ApiDocExportFormTest.java` - Modify: `src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java` **Interfaces:** - Consumes: `ApiDocModeSelector`, existing project/module data, and proxy default. - Produces: `buildRequest(): ApiDocExportRequest`, `requestControls(): List`, `setRequestControlsDisabled(boolean)`. - Produces testable setters `setPathInput(String)`, `setFqnInput(String)`, `setBatchProjectPaths(List)`, and `setDebugProxyEnabled(boolean)`; production event handlers use the same properties. - [ ] **Step 1: Write failing request-equivalence tests** ```java private static ApiDocExportForm fixtureForm(ApiDocExportRequest.Mode mode) { ApiDocModeSelector selector = new ApiDocModeSelector(); selector.setMode(mode); ApiDocExportForm form = new ApiDocExportForm(selector, List.of("D:/demo"), false); form.setPathInput("D:/demo/OrderFacade.java"); form.setFqnInput("com.example.OrderFacade"); form.setBatchProjectPaths(List.of("D:/demo")); form.setDebugProxyEnabled(true); return form; } @ParameterizedTest @EnumSource(ApiDocExportRequest.Mode.class) void buildsTheSameRequestFieldsForEveryMode(ApiDocExportRequest.Mode mode) { ApiDocExportForm form = fx(() -> fixtureForm(mode)); ApiDocExportRequest request = fx(form::buildRequest); assertEquals(mode, request.mode()); switch (mode) { case PATH -> assertEquals("D:/demo/OrderFacade.java", request.input()); case FQN -> assertEquals("com.example.OrderFacade", request.input()); case BATCH -> assertEquals(List.of("D:/demo"), request.projectPaths()); } assertTrue(request.debugProxyEnabled()); } ``` - [ ] **Step 2: Run and verify failure** Run: `.\mvnw.cmd -q -Dtest=ApiDocExportFormTest test` Expected: FAIL because `ApiDocExportForm` is missing. - [ ] **Step 3: Move presentation and request extraction** Move the current custom path, FQN, batch project/module inputs, and validation messages into the form. Preserve the existing `ApiDocExportRequest` constructor/factory calls verbatim. Render label above input and helper below input; keep proxy as an explicit checkbox with helper text. - [ ] **Step 4: Delegate `ApiDocWindow.buildRequest()` to the form** `ApiDocWindow` may catch/display `IOException`, but it must no longer inspect individual mode controls. Bind selector mode to the form’s visible managed section. - [ ] **Step 5: Run request/use-case tests** Run: `.\mvnw.cmd -q -Dtest=ApiDocExportFormTest,ApiDocExportRequestTest,RunApiDocExportUseCaseTest,ApiDocGeneratorProxyTest test` Expected: PASS with unchanged proxy and generation semantics. - [ ] **Step 6: Commit** ```powershell git add src/main/java/com/example/workutils/biz/apidoc/presentation/ApiDocExportForm.java src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java src/test/java/com/example/workutils/biz/apidoc/presentation/ApiDocExportFormTest.java git commit -m "拆分 API 导出表单状态" ``` ## Task 3: Add the approved resizable log pane **Files:** - Create: `src/main/java/com/example/workutils/system/ui/ResizableLogPane.java` - Create: `src/test/java/com/example/workutils/system/ui/ResizableLogPaneTest.java` - Modify: `src/main/resources/css/light-theme.css` - Modify: `src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java` **Interfaces:** - Produces: `append(String)`, `clear()`, `expandedProperty()`, `setExpanded(boolean)`, `lineCountProperty()`, and package-visible `isBodyManaged()` for component verification. - Exposes a read-only `TextArea`; default preferred height must fit 12 mono lines. - [ ] **Step 1: Write failing component tests** ```java @Test void countsLinesAndCollapseRemovesBodyFromLayout() { ResizableLogPane pane = fx(() -> new ResizableLogPane("执行日志", 12)); fx(() -> pane.append("one\ntwo")); assertEquals(2, fx(() -> pane.lineCountProperty().get())); fx(() -> pane.setExpanded(false)); assertFalse(fx(pane::isBodyManaged)); } ``` - [ ] **Step 2: Run and verify failure** Run: `.\mvnw.cmd -q -Dtest=ResizableLogPaneTest test` Expected: FAIL because the component is missing. - [ ] **Step 3: Implement and integrate** Use a header, read-only text area, footer line count, collapse control, and resize grip/split divider that never overlays log text. Replace direct `logArea.appendText` calls with `logPane.append`; keep redaction at the existing message-producing boundary. - [ ] **Step 4: Run tests and commit** Run: `.\mvnw.cmd -q -Dtest=ResizableLogPaneTest,ApiDocWindowArchitectureTest test` Expected: PASS. ```powershell git add src/main/java/com/example/workutils/system/ui/ResizableLogPane.java src/test/java/com/example/workutils/system/ui/ResizableLogPaneTest.java src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java src/main/resources/css/light-theme.css git commit -m "增加可调整执行日志面板" ``` ## Task 4: Recompose the API page and synchronize operation state **Files:** - Modify: `src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java` - Modify: `src/test/java/com/example/workutils/biz/apidoc/ApiDocWindowArchitectureTest.java` - Modify: `src/test/java/com/example/workutils/biz/apidoc/ApiDocHistoryMappingTest.java` **Interfaces:** - Consumes: `ApiDocModeSelector`, `ApiDocExportForm`, `OperationStatusPane`, `ResizableLogPane`. - Preserves: `getContentPane()`, `toHistoryItem(HistoryRow)`, all three tabs, and history persistence. - [ ] **Step 1: Strengthen the failing architecture test** ```java assertTrue(source.contains("new ApiDocModeSelector()")); assertTrue(source.contains("new ApiDocExportForm(")); assertTrue(source.contains("new ResizableLogPane(\"执行日志\", 12)")); assertFalse(source.contains("buildModeCard(")); assertFalse(source.contains("setGraphic(btnBox)")); ``` - [ ] **Step 2: Run and verify failure** Run: `.\mvnw.cmd -q -Dtest=ApiDocWindowArchitectureTest test` Expected: FAIL on the old card/action-cell implementation. - [ ] **Step 3: Build the final layout** Compose tabs, selector, form, a progress/action row, and log pane in that order. Use `ExportHistoryPane` for history and keep base-class configuration content unchanged. Remove the duplicated history action cell code only after mapping tests prove shared history equivalence. - [ ] **Step 4: Bind disablement narrowly** Bind `form.setRequestControlsDisabled(snapshot.isBusy())`; leave tabs, navigation, and history controls enabled. Bind the export button through `LoadingButtonBinder` and show cancel only if `RunApiDocExportUseCase` cancellation reaches its operation context; otherwise omit cancel rather than simulate it. - [ ] **Step 5: Run all API tests** Run: `.\mvnw.cmd -q -Dtest=ApiDocModeSelectorTest,ApiDocExportFormTest,ApiDocWindowArchitectureTest,ApiDocHistoryMappingTest,ApiDocExportRequestTest,RunApiDocExportUseCaseTest,ApiDocGeneratorProxyTest test` Expected: PASS. - [ ] **Step 6: Perform read-only visual comparison** Capture “导出配置” idle and synthetic/controlled running state at the same viewport as the approved reference. Compare reference and implementation side by side for typography, segmented selector, input alignment, progress/action row, and log dimensions. Do not perform a real export or start the local proxy during visual verification. - [ ] **Step 7: Run P1 completion gate and commit** Run: `.\mvnw.cmd -q test` Run: `.\mvnw.cmd -q -DskipTests compile` Run: `git diff --check` Expected: all exit 0. ```powershell git add src/main/java/com/example/workutils/biz/apidoc src/main/java/com/example/workutils/system/ui/ResizableLogPane.java src/main/resources/css/light-theme.css src/test/java/com/example/workutils/biz/apidoc src/test/java/com/example/workutils/system/ui/ResizableLogPaneTest.java git commit -m "按统一规范重排 API 文档导出" ```