Files
workutils/docs/superpowers/plans/2026-08-01-workutils-unified-ui-p1-api-doc.md
T
2026-08-01 12:42:14 +08:00

260 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<ApiDocExportRequest.Mode>`.
- 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<Control>`, `setRequestControlsDisabled(boolean)`.
- Produces testable setters `setPathInput(String)`, `setFqnInput(String)`, `setBatchProjectPaths(List<String>)`, 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 文档导出"
```