重构插件异步状态与业务用例
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
# API Doc Refactor Phase 3 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:** Give API document export one immutable request, one async operation owner, an explicit debug-proxy option, and the shared history UI.
|
||||
|
||||
**Architecture:** Capture all JavaFX form values into `ApiDocExportRequest` on the UI thread, then execute from `AsyncOperationController`. Inject the debug endpoint into `ApiDocGenerator`; only the export use case may decide whether to start the local proxy. Adapt existing history rows to `HistoryItem` without changing persistence.
|
||||
|
||||
**Tech Stack:** Java 17, JavaFX, JUnit 5, Maven.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Work only on `codex/plugin-ui-refactor` in the existing isolated worktree.
|
||||
- Preserve generated HTML structure and history-file format.
|
||||
- Do not introduce `new Thread`.
|
||||
- File and directory choosers must not enter loading state.
|
||||
- Local debug proxy is a visible option and defaults to disabled.
|
||||
- Write and observe a failing test before production behavior.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Immutable API export request
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/apidoc/application/ApiDocExportRequest.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/apidoc/application/ApiDocExportRequestTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `Mode { PATH, FQN, BATCH }`.
|
||||
- Produces validated factories `path`, `fqn`, and `batch`.
|
||||
- Carries debug-proxy opt-in and batch filters.
|
||||
|
||||
- [ ] Test mode-specific validation and immutable project-path copying.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Implement the minimal request record.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 2: Explicit debug endpoint injection
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/apidoc/generator/ApiDocGenerator.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/apidoc/generator/ApiDocGeneratorProxyTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces constructor `ApiDocGenerator(WorkUtilsProperties, Supplier<String>)`.
|
||||
- Existing constructor supplies an empty endpoint and never starts a proxy.
|
||||
|
||||
- [ ] Test that generated HTML receives the supplied endpoint and the default constructor does not instantiate the proxy.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Inject endpoint supplier and remove `DubboDebugProxy.getInstance()` from HTML rendering.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 3: Single async export operation
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/apidoc/application/RunApiDocExportUseCase.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/apidoc/application/RunApiDocExportUseCaseTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes `ApiDocExportRequest`.
|
||||
- Produces `Result(Path output, int moduleCount, int classCount, int methodCount)`.
|
||||
- Reports progress through `OperationContext`.
|
||||
|
||||
- [ ] Test proxy-disabled execution does not resolve a local endpoint and proxy-enabled execution resolves it once.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Move export orchestration behind the use case and connect it to `AsyncOperationController`.
|
||||
- [ ] Bind the button through `LoadingButtonBinder` and status through `OperationStatusPane`.
|
||||
- [ ] Confirm focused and existing tests pass.
|
||||
|
||||
### Task 4: Shared history UI
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/apidoc/ApiDocHistoryMappingTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces package-visible `toHistoryItem(HistoryRow)`.
|
||||
- Consumes `ExportHistoryPane` and `HistoryFileCommands`.
|
||||
|
||||
- [ ] Test history mapping.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Replace duplicated table and action cell.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 5: Verification
|
||||
|
||||
- [ ] Run all tests.
|
||||
- [ ] Compile with JDK 17.
|
||||
- [ ] Run `git diff --check`.
|
||||
- [ ] Confirm `ApiDocWindow` contains no `new Thread`, `buildActionCell`, or direct export-button state mutations.
|
||||
- [ ] Inspect PATH, FQN, and BATCH panels without invoking export or file choosers.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Diff Engine Phase 5 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:** Make CLI and JavaFX text comparison share one UI-independent line and character diff engine, then run expensive comparisons off the JavaFX thread.
|
||||
|
||||
**Architecture:** `DiffEngine` returns immutable line and character segment records without HTML or JavaFX dependencies. `DiffPlugin` delegates CLI comparison to it. `DiffWindow` renders those records and owns only presentation/navigation, with `AsyncOperationController` owning background execution.
|
||||
|
||||
**Tech Stack:** Java 17, JavaFX, JUnit 5, Maven.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Preserve current unified text output and HTML colors/navigation.
|
||||
- `DiffEngine` must not import JavaFX, HTML generators, filesystem, or plugin classes.
|
||||
- CLI and UI must use the same line-diff implementation.
|
||||
- Capture text on the UI thread before background execution.
|
||||
- Test first.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Immutable line diff engine
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/diff/domain/DiffEngine.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/diff/domain/DiffEngineLineTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces `DiffResult compareLines(List<String> left, List<String> right)`.
|
||||
- Produces immutable `LineDiff`, `LineType`, and counts.
|
||||
|
||||
- [ ] Test unchanged, added, removed, replacement, and input-list mutation.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Implement LCS line comparison.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 2: Structured character diff
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/diff/domain/DiffEngine.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/diff/domain/DiffEngineCharacterTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces `CharacterDiff compareCharacters(String left, String right)`.
|
||||
- Produces immutable `CharacterSegment(Type, text)` lists for both sides.
|
||||
- Produces `similarity(String left, String right)`.
|
||||
|
||||
- [ ] Test insertion, deletion, replacement, equality, HTML-like text, and similarity.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Implement character LCS and merged segments.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 3: Migrate CLI and renderer
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/diff/DiffPlugin.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/diff/DiffWindow.java`
|
||||
|
||||
- [ ] Replace `DiffPlugin` nested algorithm types with `DiffEngine`.
|
||||
- [ ] Replace Window line/character computation with engine records.
|
||||
- [ ] Delete duplicate LCS methods from Plugin and Window.
|
||||
- [ ] Run focused engine and existing tests.
|
||||
|
||||
### Task 4: Background comparison state
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/diff/DiffWindow.java`
|
||||
|
||||
- [ ] Add `AsyncOperationController<DiffRender>`.
|
||||
- [ ] Bind compare button through `LoadingButtonBinder`.
|
||||
- [ ] Add `OperationStatusPane`.
|
||||
- [ ] Capture both text values before background execution.
|
||||
- [ ] Apply HTML and counters only on success.
|
||||
|
||||
### Task 5: Verification
|
||||
|
||||
- [ ] Run all tests and compile with JDK 17.
|
||||
- [ ] Run `git diff --check`.
|
||||
- [ ] Confirm no LCS matrix implementation remains in `DiffPlugin` or `DiffWindow`.
|
||||
- [ ] Inspect empty, equal, and different short-text UI states without saving files.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Git Sync Refactor Phase 4 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:** Stop exposing credential-bearing Git URLs, make large project lists searchable, and give repository synchronization one structured async state owner.
|
||||
|
||||
**Architecture:** Convert raw remote URLs to safe display values before they enter table rows. Add a filtered view over immutable project presentation rows. Wrap the existing blocking synchronization API behind a use case that translates callbacks into immutable progress and result records consumed by `AsyncOperationController`.
|
||||
|
||||
**Tech Stack:** Java 17, JavaFX, JUnit 5, Maven.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Preserve Git command behavior and damaged-repository confirmation flow.
|
||||
- Never display username, password, or token-bearing URL text in the project table.
|
||||
- Do not add raw `new Thread` to `GitSyncWindow`.
|
||||
- Do not execute synchronization during UI verification.
|
||||
- Test first for every new production behavior.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Remote URL safe display
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/git/presentation/RemoteUrlDisplay.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/git/presentation/RemoteUrlDisplayTest.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/git/GitSyncWindow.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces `RemoteUrlDisplay.safe(String rawUrl)`.
|
||||
- HTTPS credentials and SSH usernames are removed; host and repository path remain.
|
||||
|
||||
- [ ] Test HTTPS credentials, SSH SCP syntax, plain host/path, and blank values.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Implement safe display.
|
||||
- [ ] Store only safe text in `ProjectRow`.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 2: Project search and status filter
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/git/presentation/ProjectFilter.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/git/presentation/ProjectFilterTest.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/git/GitSyncWindow.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces pure `matches(name, path, status, query, requiredStatus)`.
|
||||
|
||||
- [ ] Test case-insensitive name/path matching and status filtering.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Add search field, status combo, filtered list, and visible count.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 3: Structured synchronization use case
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/git/application/GitSyncRequest.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/git/application/GitSyncProgress.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/git/application/RunGitSyncUseCase.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/git/application/RunGitSyncUseCaseTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes `Gateway.sync(branch, Listener)`.
|
||||
- Produces immutable `Result(total, success, damaged, failed, elapsedSeconds, damagedRepos)`.
|
||||
- Reports progress through `OperationContext`.
|
||||
|
||||
- [ ] Test request validation, progress translation, result construction, and missing completion failure.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Implement request, progress, gateway, listener, and result.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 4: Migrate main sync action
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/git/GitSyncWindow.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes `AsyncOperationController<RunGitSyncUseCase.Result>`.
|
||||
- Uses `LoadingButtonBinder` and `OperationStatusPane`.
|
||||
|
||||
- [ ] Replace manual button/progress state and window-level worker thread.
|
||||
- [ ] Keep cancellation wired to both operation state and `plugin.cancelSync()`.
|
||||
- [ ] Render structured completion summary and damaged rows.
|
||||
- [ ] Confirm `GitSyncWindow` has no `new Thread` in synchronization flow.
|
||||
|
||||
### Task 5: Verification
|
||||
|
||||
- [ ] Run all tests and JDK 17 compilation.
|
||||
- [ ] Run `git diff --check`.
|
||||
- [ ] Inspect project list search/filter, masked remote values, and idle sync state.
|
||||
- [ ] Do not start synchronization or damaged-repository actions.
|
||||
@@ -0,0 +1,78 @@
|
||||
# MyBatis Generator Phase 6 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:** Remove sensitive database details from the page, prevent database query/generation from freezing JavaFX, and replace the embedded-page “cancel” action with reset.
|
||||
|
||||
**Architecture:** Convert raw JDBC configuration into a safe `DatasourceSummary` before rendering. Capture query and generation inputs into immutable requests and execute two independent use cases through separate `AsyncOperationController` instances. Keep existing service callbacks as infrastructure adapters.
|
||||
|
||||
**Tech Stack:** Java 17, JavaFX, JUnit 5, Maven.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Never expose database password, username, JDBC query parameters, or raw URL in page labels.
|
||||
- Query and generate actions must have independent state.
|
||||
- Preserve database access and generated artifact behavior.
|
||||
- Directory choosers remain synchronous and do not show loading.
|
||||
- UI verification must not query a database or generate files.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Safe datasource summary
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisgenerator/presentation/DatasourceSummary.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/mybatisgenerator/presentation/DatasourceSummaryTest.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces `DatasourceSummary.from(alias, jdbcUrl)`.
|
||||
- Exposes alias, database type, masked host, and database name only.
|
||||
|
||||
- [ ] Test MySQL, OceanBase, query parameters, malformed URL, and blank alias.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Implement safe parsing and display text.
|
||||
- [ ] Replace raw URL/user labels.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 2: Immutable operation requests
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisgenerator/application/QueryTablesRequest.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisgenerator/application/GenerateArtifactsRequest.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/mybatisgenerator/application/GeneratorRequestTest.java`
|
||||
|
||||
- [ ] Test trimming, required pattern/table selection, directory defaults, and immutable table copying.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Implement both records.
|
||||
- [ ] Confirm focused test passes.
|
||||
|
||||
### Task 3: Independent use cases and loading
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisgenerator/application/QueryTablesUseCase.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisgenerator/application/GenerateArtifactsUseCase.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/mybatisgenerator/application/GeneratorUseCaseTest.java`
|
||||
|
||||
- [ ] Test gateway delegation and result immutability.
|
||||
- [ ] Observe focused test failure.
|
||||
- [ ] Add two use cases and two async controllers.
|
||||
- [ ] Bind query/generate buttons independently and add operation status panes.
|
||||
- [ ] Confirm focused tests pass.
|
||||
|
||||
### Task 4: Reset semantics
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java`
|
||||
|
||||
- [ ] Rename bottom “取消” to “重置配置”.
|
||||
- [ ] Reset pattern, table selection, and directory defaults without closing the page.
|
||||
- [ ] Keep window close behavior only on actual window close.
|
||||
|
||||
### Task 5: Verification
|
||||
|
||||
- [ ] Run all tests and compile with JDK 17.
|
||||
- [ ] Run `git diff --check`.
|
||||
- [ ] Confirm Window no longer directly calls query/generate on the UI thread.
|
||||
- [ ] Inspect datasource summary, idle query/generate states, and reset label without database or filesystem side effects.
|
||||
@@ -0,0 +1,245 @@
|
||||
# Plugin UI Refactor Phase 1 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:** Introduce one testable asynchronous operation model and migrate Scanner and MyBatis SQL Export to consistent loading, cancellation, progress, error recovery, and history UI behavior.
|
||||
|
||||
**Architecture:** Add JavaFX-neutral operation state in `system/ui/async`, with a JavaFX adapter that binds buttons and status controls. Plugin windows submit immutable requests to operation controllers; services publish structured progress. Existing business services remain intact in this phase.
|
||||
|
||||
**Tech Stack:** Java 17, JavaFX 17, Spring Boot 2.7, JUnit 5, Maven.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Preserve all existing command-line plugin behavior.
|
||||
- Do not add raw `new Thread` calls.
|
||||
- Do not put Git, SQL, HTTP, process, or file traversal logic in Window classes.
|
||||
- Use enum state instead of boolean combinations.
|
||||
- Give unrelated user actions independent operation state.
|
||||
- Keep production files below 350 lines where practical; do not increase existing Window line counts.
|
||||
- Do not modify the user's existing checkout; work only in the isolated worktree.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Operation State Machine
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/OperationState.java`
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/OperationSnapshot.java`
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/OperationStateMachine.java`
|
||||
- Test: `src/test/java/com/example/workutils/system/ui/async/OperationStateMachineTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `OperationState`, immutable `OperationSnapshot`, and `OperationStateMachine`.
|
||||
- `OperationStateMachine.start(String)`, `progress(String,double)`, `requestCancel()`, `succeed(String)`, `fail(String)`, and `reset()`.
|
||||
- Invalid transitions throw `IllegalStateException`.
|
||||
|
||||
- [ ] **Step 1: Write failing state-transition tests**
|
||||
|
||||
```java
|
||||
@Test
|
||||
void runningOperationCanProgressAndSucceed() {
|
||||
OperationStateMachine machine = new OperationStateMachine();
|
||||
machine.start("扫描中");
|
||||
machine.progress("项目 1/3", 1.0 / 3.0);
|
||||
machine.succeed("扫描完成");
|
||||
|
||||
assertEquals(OperationState.SUCCEEDED, machine.snapshot().state());
|
||||
assertEquals("扫描完成", machine.snapshot().message());
|
||||
}
|
||||
|
||||
@Test
|
||||
void idleOperationCannotBeCancelled() {
|
||||
OperationStateMachine machine = new OperationStateMachine();
|
||||
assertThrows(IllegalStateException.class, machine::requestCancel);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests and verify RED**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=OperationStateMachineTest test`
|
||||
|
||||
Expected: compilation failure because the production types do not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the minimal state machine**
|
||||
|
||||
Implement explicit legal transitions:
|
||||
|
||||
```text
|
||||
IDLE -> RUNNING
|
||||
RUNNING -> CANCELLING | SUCCEEDED | FAILED
|
||||
CANCELLING -> SUCCEEDED | FAILED
|
||||
SUCCEEDED | FAILED -> IDLE
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run tests and verify GREEN**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=OperationStateMachineTest test`
|
||||
|
||||
Expected: all tests pass.
|
||||
|
||||
### Task 2: Asynchronous Operation Controller
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/OperationTask.java`
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/OperationContext.java`
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/AsyncOperationController.java`
|
||||
- Create: `src/main/java/com/example/workutils/system/support/AppExecutors.java`
|
||||
- Test: `src/test/java/com/example/workutils/system/ui/async/AsyncOperationControllerTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `OperationStateMachine`.
|
||||
- Produces: one controller with `start(OperationTask<T>, Consumer<T>)`, `cancel()`, `snapshotProperty()`, and `runningProperty()`.
|
||||
- `OperationContext` supplies `report(message, progress)` and `isCancellationRequested()`.
|
||||
|
||||
- [ ] **Step 1: Write failing success, failure, and cancellation tests**
|
||||
|
||||
Use a single-thread test executor and `CountDownLatch`. Assert observable controller state, not mock calls.
|
||||
|
||||
- [ ] **Step 2: Run tests and verify RED**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=AsyncOperationControllerTest test`
|
||||
|
||||
Expected: compilation failure because the controller does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the minimal controller**
|
||||
|
||||
Use the injected `ExecutorService`; marshal observable property changes through an injected `Consumer<Runnable>` so tests can use `Runnable::run` and production can use `Platform::runLater`.
|
||||
|
||||
- [ ] **Step 4: Run tests and verify GREEN**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=AsyncOperationControllerTest test`
|
||||
|
||||
Expected: success, failure, and cancellation tests pass.
|
||||
|
||||
### Task 3: Loading and Status Components
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/LoadingButtonBinder.java`
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/async/OperationStatusPane.java`
|
||||
- Modify: `src/main/resources/css/light-theme.css`
|
||||
- Test: `src/test/java/com/example/workutils/system/ui/async/LoadingButtonTextTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `AsyncOperationController`.
|
||||
- Produces: consistent primary-button loading text, conditional cancel button, progress indicator, status message, and error/success style classes.
|
||||
|
||||
- [ ] **Step 1: Write a failing pure text-mapping test**
|
||||
|
||||
Test `LoadingButtonBinder.textFor(snapshot, idleText)` with literal expectations:
|
||||
|
||||
```java
|
||||
assertEquals("扫描中…", textFor(running("扫描中"), "开始扫描"));
|
||||
assertEquals("正在停止…", textFor(cancelling(), "开始扫描"));
|
||||
assertEquals("开始扫描", textFor(idle(), "开始扫描"));
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests and verify RED**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=LoadingButtonTextTest test`
|
||||
|
||||
- [ ] **Step 3: Implement mapper, binder, and status pane**
|
||||
|
||||
Use a real JavaFX `ProgressIndicator` only in the binder. Keep text selection pure and independently testable.
|
||||
|
||||
- [ ] **Step 4: Run tests and compile**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=LoadingButtonTextTest test`
|
||||
|
||||
Run: `.\mvnw.cmd -q -DskipTests compile`
|
||||
|
||||
### Task 4: Scanner Migration
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/scanner/application/ScanRequest.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/scanner/application/ScanProgress.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/scanner/application/RunScanUseCase.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/scanner/application/ScanRequestTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `ScannerService`, `AsyncOperationController`.
|
||||
- Produces: immutable validated `ScanRequest` and structured `ScanProgress`.
|
||||
|
||||
- [ ] **Step 1: Write failing ScanRequest validation tests**
|
||||
|
||||
Test blank keyword rejection, invalid regular expression rejection, and trimmed keyword preservation.
|
||||
|
||||
- [ ] **Step 2: Run tests and verify RED**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=ScanRequestTest test`
|
||||
|
||||
- [ ] **Step 3: Implement request and use case**
|
||||
|
||||
The use case adapts existing `ScannerService` callbacks/results without changing scan behavior.
|
||||
|
||||
- [ ] **Step 4: Migrate ScannerWindow**
|
||||
|
||||
Replace raw thread creation and manual `scanBtn.setDisable` with `AsyncOperationController`. Add `OperationStatusPane`; display the cancel button only while running. Keep history behavior unchanged.
|
||||
|
||||
- [ ] **Step 5: Run focused and full tests**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=ScanRequestTest test`
|
||||
|
||||
Run: `.\mvnw.cmd -q test`
|
||||
|
||||
### Task 5: MyBatis SQL Export Migration
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisexport/application/SqlExportRequest.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgress.java`
|
||||
- Create: `src/main/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportUseCase.java`
|
||||
- Modify: `src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/mybatisexport/application/SqlExportRequestTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `MyBatisExportService`, `AsyncOperationController`.
|
||||
- Produces: immutable validated request and structured progress.
|
||||
|
||||
- [ ] **Step 1: Write failing filter validation tests**
|
||||
|
||||
Test that ALL requires no value, TABLE requires a nonblank table, and STATEMENT requires a nonblank statement id.
|
||||
|
||||
- [ ] **Step 2: Run tests and verify RED**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=SqlExportRequestTest test`
|
||||
|
||||
- [ ] **Step 3: Implement request and use case**
|
||||
|
||||
Adapt current export service without changing CSV format or history persistence.
|
||||
|
||||
- [ ] **Step 4: Migrate MyBatisExportWindow**
|
||||
|
||||
Replace raw thread and manual button toggles with the shared controller. Show progress, current project, and conditional cancel action.
|
||||
|
||||
- [ ] **Step 5: Run focused and full tests**
|
||||
|
||||
Run: `.\mvnw.cmd -q -Dtest=SqlExportRequestTest test`
|
||||
|
||||
Run: `.\mvnw.cmd -q test`
|
||||
|
||||
### Task 6: Phase Verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/audit/plugin-ui-code-audit.md` only if implementation differs from the approved design.
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: all previous tasks.
|
||||
- Produces: a compiling, tested first phase ready for UI inspection.
|
||||
|
||||
- [ ] **Step 1: Run all tests**
|
||||
|
||||
Run: `.\mvnw.cmd -q test`
|
||||
|
||||
- [ ] **Step 2: Compile production code**
|
||||
|
||||
Run: `.\mvnw.cmd -q -DskipTests compile`
|
||||
|
||||
- [ ] **Step 3: Inspect changes**
|
||||
|
||||
Run: `git diff --check`
|
||||
|
||||
Run: `git status --short`
|
||||
|
||||
- [ ] **Step 4: Launch and visually verify**
|
||||
|
||||
Verify Scanner and MyBatis SQL Export in IDLE, RUNNING, CANCELLING, FAILED, and SUCCEEDED states without triggering destructive operations.
|
||||
@@ -0,0 +1,108 @@
|
||||
# Shared Result UI Phase 2 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:** Remove duplicated export-history and empty-result UI code from Scanner and MyBatis SQL Export while preserving their behavior.
|
||||
|
||||
**Architecture:** Add a small immutable adapter model between plugin-specific history rows and a reusable JavaFX history pane. Keep file-opening side effects behind injected callbacks so the shared pane only renders data and emits commands. Add a reusable empty-state component for later plugin migrations.
|
||||
|
||||
**Tech Stack:** Java 17, JavaFX, JUnit 5, Maven.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Work only in `C:\tmp\workutils-plugin-ui-refactor` on branch `codex/plugin-ui-refactor`.
|
||||
- Do not add raw `new Thread`.
|
||||
- Shared UI components must not read files or launch external programs.
|
||||
- Plugin-specific persistence formats must remain unchanged.
|
||||
- Write and observe a failing test before each production behavior.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Immutable history presentation model
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/history/HistoryItem.java`
|
||||
- Test: `src/test/java/com/example/workutils/system/ui/history/HistoryItemTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `HistoryItem(String index, String query, String filePath, String count, String timestamp)`.
|
||||
- Produces: `fileDisplay(int maxLength)` for bounded single-line display.
|
||||
|
||||
- [ ] Write tests proving short paths remain unchanged, long paths are ellipsized while retaining the filename, and constructor values cannot be null.
|
||||
- [ ] Run `.\mvnw.cmd -q -Dtest=HistoryItemTest test` and observe compilation failure because `HistoryItem` is absent.
|
||||
- [ ] Implement the immutable record and its display method.
|
||||
- [ ] Run the focused test and confirm it passes.
|
||||
|
||||
### Task 2: Reusable empty state
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/EmptyStatePane.java`
|
||||
- Modify: `src/main/resources/css/light-theme.css`
|
||||
- Test: `src/test/java/com/example/workutils/system/ui/EmptyStateContentTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `EmptyStatePane.Content(String title, String description, String actionLabel)`.
|
||||
- Produces: `EmptyStatePane(Content content, Runnable action, int fontSize)`.
|
||||
|
||||
- [ ] Write pure content-validation tests proving title is required and a blank action label is allowed only when no action is rendered.
|
||||
- [ ] Run the focused test and observe failure because the component is absent.
|
||||
- [ ] Implement the content model, pane, optional action button, and theme classes.
|
||||
- [ ] Run the focused test and confirm it passes.
|
||||
|
||||
### Task 3: Reusable export history pane
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/history/HistoryFileCommands.java`
|
||||
- Create: `src/main/java/com/example/workutils/system/ui/history/ExportHistoryPane.java`
|
||||
- Test: `src/test/java/com/example/workutils/system/ui/history/HistoryFileCommandsTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `ObservableList<HistoryItem>`.
|
||||
- Consumes: `HistoryFileCommands` callbacks for open file, open editor, open folder, and copy path.
|
||||
- Produces: `ExportHistoryPane(String countColumnTitle, ObservableList<HistoryItem> items, HistoryFileCommands commands, int fontSize)`.
|
||||
|
||||
- [ ] Write tests proving `HistoryFileCommands` rejects missing callbacks and routes each command to the supplied consumer.
|
||||
- [ ] Run the focused test and observe failure because the type is absent.
|
||||
- [ ] Implement callback validation and the shared table, action cell, tooltip, context menu, and empty-state placeholder.
|
||||
- [ ] Run the focused test and confirm it passes.
|
||||
|
||||
### Task 4: Migrate Scanner
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/scanner/ScannerHistoryMappingTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `HistoryItem`, `ExportHistoryPane`, `HistoryFileCommands`.
|
||||
- Produces: package-visible `toHistoryItem(HistoryRow row)` for deterministic presentation mapping.
|
||||
|
||||
- [ ] Write a mapping test for index, query, path, match count, and timestamp.
|
||||
- [ ] Run the focused test and observe failure because the mapping method is absent.
|
||||
- [ ] Replace duplicated table/action-cell construction with `ExportHistoryPane`.
|
||||
- [ ] Run scanner tests and confirm they pass.
|
||||
|
||||
### Task 5: Migrate MyBatis SQL Export
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java`
|
||||
- Test: `src/test/java/com/example/workutils/biz/mybatisexport/MyBatisExportHistoryMappingTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `HistoryItem`, `ExportHistoryPane`, `HistoryFileCommands`.
|
||||
- Produces: package-visible `toHistoryItem(HistoryRow row)`.
|
||||
|
||||
- [ ] Write a mapping test for index, query, path, export count, and timestamp.
|
||||
- [ ] Run the focused test and observe failure because the mapping method is absent.
|
||||
- [ ] Replace duplicated table/action-cell construction with `ExportHistoryPane`.
|
||||
- [ ] Run export tests and confirm they pass.
|
||||
|
||||
### Task 6: Verification
|
||||
|
||||
**Files:**
|
||||
- Modify only files required by failures found during verification.
|
||||
|
||||
- [ ] Run `.\mvnw.cmd -q test`.
|
||||
- [ ] Run `.\mvnw.cmd -q -DskipTests compile`.
|
||||
- [ ] Run `git diff --check`.
|
||||
- [ ] Confirm Scanner and MyBatis SQL Export no longer declare `buildActionCell`.
|
||||
- [ ] Inspect both pages without triggering scan, export, file-open, or delete side effects.
|
||||
Reference in New Issue
Block a user