Files
workutils/docs/superpowers/plans/2026-07-31-public-ui-foundation.md
2026-07-31 19:08:42 +08:00

17 KiB

Public UI Foundation 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: Consolidate notifications, history-file actions, progress semantics, and accessibility behavior without changing plugin business results.

Architecture: Windows depend on narrow UI services instead of JavaFX dialogs, clipboard APIs, or operating-system helpers. Domain-specific progress records remain independent; shared panes own reusable accessibility behavior. Each migration is protected by unit or source-architecture tests before window code changes.

Tech Stack: Java 17, JavaFX, JUnit 5, Maven Wrapper, PowerShell, existing AsyncOperationController and SystemOpenHelper.

Global Constraints

  • Do not modify Git Review, Mobile UI Automation, Settings, or Plugin Management.
  • Do not change generated output formats or persisted history formats.
  • Do not expose datasource passwords, tokens, or credential-bearing URLs in notifications.
  • Keep MyBatis SQL Export cancellable; it cooperatively checks cancellation between projects.
  • Scanner remains non-cancellable.
  • Use $env:JAVA_HOME='C:\Program Files\Java\jdk-17' for Maven commands.
  • Preserve unrelated untracked files under docs/audit, docs/audit/screenshots, and src/main/resources.

Task 1: Notification model and JavaFX presenter

Files:

  • Create: src/main/java/com/example/workutils/system/ui/notification/NotificationLevel.java
  • Create: src/main/java/com/example/workutils/system/ui/notification/NotificationMessage.java
  • Create: src/main/java/com/example/workutils/system/ui/notification/NotificationCenter.java
  • Create: src/main/java/com/example/workutils/system/ui/notification/FxNotificationCenter.java
  • Test: src/test/java/com/example/workutils/system/ui/notification/NotificationMessageTest.java
  • Test: src/test/java/com/example/workutils/system/ui/notification/FxNotificationCenterArchitectureTest.java

Interfaces:

  • Produces: NotificationMessage(NotificationLevel level, String title, String message).

  • Produces: NotificationCenter.info(String,String), warning(String,String), error(String,String), and boolean confirm(String,String,String).

  • Produces: new FxNotificationCenter(Window owner).

  • Step 1: Write failing notification value tests

Test trimming/default title, rejection of null or blank message, and preservation of multiline content:

assertEquals("提示", new NotificationMessage(INFO, " ", " 完成 ").title());
assertEquals("完成", new NotificationMessage(INFO, " ", " 完成 ").message());
assertThrows(IllegalArgumentException.class,
        () -> new NotificationMessage(ERROR, "失败", "  "));
  • Step 2: Run the focused test and verify RED

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=NotificationMessageTest test

Expected: compilation failure because notification types do not exist.

  • Step 3: Implement the model and narrow interface

Use this contract:

public interface NotificationCenter {
    void show(NotificationMessage message);
    boolean confirm(String title, String header, String message);
    default void info(String title, String message) { show(new NotificationMessage(INFO, title, message)); }
    default void warning(String title, String message) { show(new NotificationMessage(WARNING, title, message)); }
    default void error(String title, String message) { show(new NotificationMessage(ERROR, title, message)); }
}

FxNotificationCenter.show must call immediately on the FX thread and otherwise use Platform.runLater. confirm must throw IllegalStateException("confirm must be called on the JavaFX Application Thread") off the FX thread. Map levels to JavaFX AlertType; use an expandable, read-only TextArea for long or multiline messages.

  • Step 4: Add a source-architecture test for the threading contract

Read FxNotificationCenter.java and assert it contains Platform.isFxApplicationThread(), Platform.runLater, initOwner(owner), and the exact confirmation-thread error. This test avoids displaying a modal dialog in CI.

  • Step 5: Run focused tests and commit

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=NotificationMessageTest,FxNotificationCenterArchitectureTest test

Expected: PASS.

Commit: git commit -m "新增统一通知中心"


Task 2: Migrate MyBatis Generator and Git Sync notifications

Files:

  • Modify: src/main/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindow.java
  • Modify: src/main/java/com/example/workutils/biz/git/GitSyncWindow.java
  • Modify: src/test/java/com/example/workutils/biz/mybatisgenerator/MyBatisGeneratorWindowArchitectureTest.java
  • Create: src/test/java/com/example/workutils/biz/git/GitSyncWindowArchitectureTest.java

Interfaces:

  • Consumes: FxNotificationCenter(Window) and all NotificationCenter methods from Task 1.

  • Produces: both windows contain one NotificationCenter field and no direct new Alert.

  • Step 1: Tighten architecture tests before migration

Assert each target source contains NotificationCenter and FxNotificationCenter, and does not contain new Alert, showAlert(, or MyBatis's private showInfo(. Also assert Git's destructive confirmation text still contains 不可撤销.

  • Step 2: Run tests and verify RED

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=MyBatisGeneratorWindowArchitectureTest,GitSyncWindowArchitectureTest test

Expected: FAIL because both windows still construct alerts.

  • Step 3: Migrate MyBatis Generator

Initialize notificationCenter = new FxNotificationCenter(stage) after the stage is available. Replace query/generation warnings with warning, generation results with info, and confirmGeneration with:

return notificationCenter.confirm(
        "确认生成",
        "即将为 " + plan.tables().size() + " 个表生成代码",
        buildGenerationSummary(plan));

Delete the private dialog methods and their now-unused JavaFX imports.

  • Step 4: Migrate Git Sync without changing destructive behavior

Replace the missing-URL warning and bulk-reclone confirmation. Preserve the existing snapshot, row removal, thread start, delete/clone order, and confirmation wording. Only call recloneAll continuation when confirm(...) returns true.

  • Step 5: Run focused tests and commit

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=NotificationMessageTest,MyBatisGeneratorWindowArchitectureTest,GitSyncWindowArchitectureTest test

Expected: PASS.

Commit: git commit -m "统一生成器与 Git 同步通知"


Task 3: Shared result-file actions and three-window migration

Files:

  • Create: src/main/java/com/example/workutils/system/ui/history/FileActionGateway.java
  • Create: src/main/java/com/example/workutils/system/ui/history/SystemFileActionGateway.java
  • Create: src/main/java/com/example/workutils/system/ui/history/ResultFileActions.java
  • Test: src/test/java/com/example/workutils/system/ui/history/ResultFileActionsTest.java
  • Modify: src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java
  • Modify: src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java
  • Modify: src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java
  • Modify: src/test/java/com/example/workutils/biz/apidoc/ApiDocWindowArchitectureTest.java
  • Create: src/test/java/com/example/workutils/biz/scanner/ScannerWindowArchitectureTest.java
  • Create: src/test/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindowArchitectureTest.java

Interfaces:

  • Produces: FileActionGateway.open(Path), edit(Path), reveal(Path), copyPath(String).

  • Produces: ResultFileActions(FileActionGateway, NotificationCenter, Consumer<String>) and HistoryFileCommands commands().

  • Consumes: NotificationCenter from Task 1 and existing HistoryFileCommands.

  • Step 1: Write failing behavior tests using @TempDir

Use a recording gateway and notification fake. Cover open/edit/reveal delegation for an existing file; normalized absolute copy path; copying a missing path with a warning success message; rejection of blank paths; no gateway call for missing open targets; and gateway exception translated to notificationCenter.error("文件操作失败", ...).

  • Step 2: Run the focused test and verify RED

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=ResultFileActionsTest test

Expected: compilation failure because the action classes do not exist.

  • Step 3: Implement gateway and action service

Production mapping must be exactly:

open    -> SystemOpenHelper.openFile(path)
edit    -> SystemOpenHelper.editFile(path)
reveal  -> SystemOpenHelper.revealInFileManager(path.toString())
copyPath -> Clipboard.getSystemClipboard().setContent(content)

ResultFileActions.commands() returns method references for the four existing HistoryFileCommands slots. Catch platform RuntimeException around gateway calls, but let constructor null errors and invalid program state fail immediately.

  • Step 4: Add RED architecture constraints for the three windows

Assert all three sources contain ResultFileActions and do not contain SystemOpenHelper, ClipboardContent, private void openFile, private void openWithNotepad, or private void openFolder.

  • Step 5: Migrate one shared command instance per window

Create the command bundle once during window construction, using new SystemFileActionGateway() and new FxNotificationCenter(stage). Scanner keeps its [INFO] 已复制路径 log consumer; API Doc and SQL Export use their existing feedback areas where available, otherwise message -> {}. Pass the shared commands to every ExportHistoryPane.

  • Step 6: Run focused tests and commit

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=HistoryFileCommandsTest,ResultFileActionsTest,ApiDocWindowArchitectureTest,ScannerWindowArchitectureTest,MyBatisExportWindowArchitectureTest test

Expected: PASS.

Commit: git commit -m "统一导出历史文件操作"


Task 4: Progress contracts and cancellation proof

Files:

  • Modify: src/main/java/com/example/workutils/biz/scanner/application/ScanProgress.java
  • Modify: src/main/java/com/example/workutils/biz/scanner/application/RunScanUseCase.java
  • Modify: src/main/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgress.java
  • Modify: src/main/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportUseCase.java
  • Create: src/test/java/com/example/workutils/biz/scanner/application/ScanProgressTest.java
  • Create: src/test/java/com/example/workutils/biz/mybatisexport/application/SqlExportProgressTest.java
  • Create: src/test/java/com/example/workutils/biz/mybatisexport/application/RunSqlExportCancellationTest.java
  • Modify: src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java
  • Modify: src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java

Interfaces:

  • Produces: validated existing record constructors and stable progress messages; no shared base interface.

  • Preserves: SqlExportProgress.fraction() and SQL export's cooperative cancellation callback.

  • Step 1: Write RED boundary tests

For ScanProgress, accept -1, 0, and 1; reject blank message, NaN, values below -1, and values above 1. For SQL progress, accept (0,0,"准备",0) and (2,2,"project",5); reject negative fields, blank project, and current > total. Assert message() returns current + "/" + total + " " + projectName, and fraction() returns the expected boundary value.

  • Step 2: Prove SQL export cancellation propagation with a service fake

Capture the callback passed to exportWithCallback, request cancellation on the OperationContext, and assert callback.isCancelled() becomes true. The test must not connect to a database or write output files.

  • Step 3: Run tests and verify RED

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=ScanProgressTest,SqlExportProgressTest,RunSqlExportCancellationTest test

Expected: DTO validation tests fail before compact constructors are added.

  • Step 4: Add compact-constructor validation and explicit UI flags

Normalize record text with trim(). Make RunScanUseCase create ScanProgress values before reporting them, and make RunSqlExportUseCase report SqlExportProgress.message() and fraction() instead of rebuilding those values inline. Keep Scanner's new OperationStatusPane(..., false). Make SQL Export's cancellability explicit with new OperationStatusPane(..., true) so future readers do not depend on the convenience constructor.

  • Step 5: Run focused tests and commit

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=ScanProgressTest,SqlExportProgressTest,RunSqlExportCancellationTest test

Expected: PASS.

Commit: git commit -m "明确进度与取消契约"


Task 5: Accessibility behavior in shared panes

Files:

  • Modify: src/main/java/com/example/workutils/system/ui/history/ExportHistoryPane.java
  • Modify: src/main/java/com/example/workutils/system/ui/EmptyStatePane.java
  • Modify: src/main/java/com/example/workutils/system/ui/async/OperationStatusPane.java
  • Create: src/test/java/com/example/workutils/system/ui/SharedPaneAccessibilityArchitectureTest.java
  • Modify: src/test/java/com/example/workutils/system/ui/EmptyStateContentTest.java

Interfaces:

  • Consumes: existing constructors; no call-site signature changes.

  • Produces: consistent accessible text, focus traversal, keyboard open behavior, and no focusable cancel button for non-cancellable panes.

  • Step 1: Write RED source and value tests

Assert the shared pane sources use setAccessibleText, setFocusTraversable, and key handling for KeyCode.ENTER. Assert OperationStatusPane conditionally adds the cancel button rather than always adding a hidden focusable control. Extend empty-state content tests to verify trimmed text used by the combined accessible label.

  • Step 2: Run tests and verify RED

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=SharedPaneAccessibilityArchitectureTest,EmptyStateContentTest test

Expected: FAIL because accessibility calls and keyboard behavior are absent.

  • Step 3: Implement minimal shared accessibility behavior

Set descriptive accessible text on table action buttons, history file cells, empty-state container/action, progress bar, status label, and cancel button. Clear file-cell text, tooltip, accessible text, and row reference when empty. On Enter, open the selected history row only when the table itself owns the key event; do not intercept Space or context-menu keys. Add cancel button to children only when cancellable is true.

  • Step 4: Check component colors without broad theme changes

Search the three component files for inline -fx-text-fill, hexadecimal colors, and Color.web. If none exist, record no code change. If found, replace only those values with existing Theme.TEXT_PRIMARY, Theme.TEXT_SECONDARY, or established semantic style classes.

  • Step 5: Run focused tests and commit

Run: $env:JAVA_HOME='C:\Program Files\Java\jdk-17'; .\mvnw.cmd -q -Dtest=SharedPaneAccessibilityArchitectureTest,EmptyStateContentTest,HistoryFileCommandsTest test

Expected: PASS.

Commit: git commit -m "完善公共组件无障碍交互"


Task 6: Full verification and audit closeout

Files:

  • Modify: docs/audit/plugin-refactor-next.md
  • Test: all src/test/java tests.

Interfaces:

  • Consumes: all deliverables from Tasks 1-5.

  • Produces: verified P1 public-foundation completion record.

  • Step 1: Run all automated verification

Run:

$env:JAVA_HOME='C:\Program Files\Java\jdk-17'
.\mvnw.cmd -q test
.\mvnw.cmd -q -DskipTests compile
git diff --check

Expected: all commands exit 0. Record the exact test count from Maven reports.

  • Step 2: Perform read-only UI smoke verification

Launch the existing application without stopping unrelated Java processes. Verify API Doc, Scanner, and MyBatis SQL Export history panes render and expose their actions; verify MyBatis Generator confirmation/result and Git Sync confirmation can be opened and cancelled. Do not execute file opens, database exports, code generation, repository deletion, or clone operations.

  • Step 3: Update the audit todo

Move completed P1 items to the completed section. Record that SQL Export cancellation was retained after source verification, and leave Git Review, Mobile UI Automation, and Settings explicitly deferred/excluded.

  • Step 4: Re-run documentation diff checks and commit

Run: git diff --check; git status --short

Confirm only intended files are staged.

Commit: git commit -m "完成公共基础能力收尾"