From 314511ed4a9b0835db8d24c119633d073a8dfc82 Mon Sep 17 00:00:00 2001 From: ExplodingUFO <1034917216@qq.com> Date: Wed, 13 May 2026 02:56:33 +0800 Subject: [PATCH] Add lasso screenshot proof route --- docs/en/demo-cookbook.md | 16 ++- docs/en/phase-0-reactflow-parity-audit.md | 11 +- docs/zh-CN/demo-cookbook.md | 16 ++- docs/zh-CN/phase-0-reactflow-parity-audit.md | 11 +- .../Cookbook/DemoCookbookCatalog.Recipes.cs | 85 +++++++++++++ .../CookbookScreenshotGateRoutes.json | 21 +++ .../CookbookShellVisualGateStates.json | 18 +++ .../DemoCookbookLassoProofDocsTests.cs | 47 +++++++ .../DemoCookbookScreenshotGateTests.cs | 120 +++++++++++++++++- .../PostPhase506VisualQueueDocsTests.cs | 3 +- .../ReactFlowParityRoadmapDocsTests.cs | 36 +++++- .../ShellVisualCoveragePlanningDocsTests.cs | 38 +++++- 12 files changed, 398 insertions(+), 24 deletions(-) create mode 100644 tests/AsterGraph.Demo.Tests/DemoCookbookLassoProofDocsTests.cs diff --git a/docs/en/demo-cookbook.md b/docs/en/demo-cookbook.md index 8230008e..b5dd770f 100644 --- a/docs/en/demo-cookbook.md +++ b/docs/en/demo-cookbook.md @@ -35,6 +35,7 @@ Use it when you want to move from "I can see the Demo" to "I know which code and | `builtin-background-grid-route` / Built-in Background/Grid route | PerformanceViewport | `src/AsterGraph.Avalonia/Controls/GridBackground.cs` (`CalculateVisibleLineMetrics`); `src/AsterGraph.Abstractions/Styling/CanvasStyleOptions.cs` (`GridBackgroundHex`); `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`TrySnapSelectedNodesToGrid`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`background-grid-density`); `tests/AsterGraph.Editor.Tests/GridBackgroundTests.cs` (`CalculateVisibleLineMetrics_WithExtremeZoomSpacing_KeepsLineDensityBounded`); `tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `GRID_BACKGROUND_DENSITY_OK` | `GRID_BACKGROUND_DENSITY_OK`, `LAYOUT_PROVIDER_SEAM_OK` | | `builtin-hosted-controls-route` / Built-in hosted Controls/Panel route | Authoring | `src/AsterGraph.Avalonia/Hosting/AsterGraphHostBuilder.cs` (`UseDefaultWorkbench`, `BuildAvaloniaView`); `src/AsterGraph.Avalonia/Hosting/AsterGraphWorkbenchOptions.cs` (`AsterGraphWorkbenchPanelState`); `src/AsterGraph.Editor/Runtime/GraphEditorCommandDescriptorSnapshot.cs` (`RecoveryHint`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`hosted-controls-panel`); `tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`); `tests/AsterGraph.Demo.Tests/DemoCookbookNavigationTests.cs` (`PART_CookbookWorkspaceNavigationPanel`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `HOSTED_CONTROLS_PANEL_COMPOSITION_OK` | `HOSTED_CONTROLS_PANEL_COMPOSITION_OK`, `DESIGNER_WORKBENCH_AUTHORING_OK` | | `interaction-selection-marquee-route` / Interaction selection marquee route | Authoring | `src/AsterGraph.Editor/Runtime/IGraphEditorQueries.cs` (`GetSelectionRectangleSnapshot`); `src/AsterGraph.Avalonia/Controls/Internal/Overlay/NodeCanvasOverlayCoordinator.cs` (`UpdateMarqueeSelection`); `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SelectAll`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`); `tests/AsterGraph.Editor.Tests/GraphEditorSelectionTransformContractsTests.cs` (`Queries_GetSelectionRectangleSnapshot_ReturnsNodesAndConnectionsInRectangle`); `tests/AsterGraph.Editor.Tests/NodeCanvasOverlayCoordinatorTests.cs` (`UpdateMarqueeSelection_WithFinalizeTrue_UsesBackendSelectionRectangleQuery`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` | `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`, `SELECTION_RECTANGLE_QUERY_OK`, `SELECTION_RECTANGLE_MARQUEE_OK` | +| `interaction-lasso-screenshot-proof-route` / Interaction lasso screenshot proof route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvasSelectionMode.cs` (`Lasso`); `src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`UpdateLassoFeedback`, `ClearLassoFeedback`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`); `tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`); `tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` (`shell-cookbook-lasso-screenshot-proof`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK` | `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`, `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` | | `interaction-keyboard-navigation-route` / Interaction keyboard navigation route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`HandleCanvasKeyDown`, `TryHandleCanvasArrowKey`); `src/AsterGraph.Avalonia/Controls/Internal/Automation/NodeCanvasAutomationPeer.cs` (`NodeCanvasAutomationPeer`); `src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`viewport.zoom-in`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`keyboard-navigation-lab`); `tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`, `ArrowKey_Navigate_SelectsNearestNodeWhenNoSelection`); `tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK` | `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK`, `CANVAS_KEYBOARD_NAVIGATION_OK`, `ARROW_KEY_NEAREST_NODE_OK` | | `interaction-host-event-inspector-route` / Interaction host event inspector route | ReviewHelp | `src/AsterGraph.Editor/Runtime/IGraphEditorEvents.cs` (`IGraphEditorEvents`); `src/AsterGraph.Editor/Runtime/Mutation/GraphEditorSessionMutation.cs` (`BeginMutation`, `FlushPendingEvents`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`host-event-inspector`); `tests/AsterGraph.Editor.Tests/GraphEditorSessionTests.cs` (`SessionEvents_SubscribeAndUnsubscribe_DoNotLeakMemory`, `SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` | `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK`, `HOST_EVENT_SUBSCRIPTION_OK`, `EVENT_BATCHING_CADENCE_OK` | | `lifecycle-workspace-save-restore-route` / Lifecycle workspace save/restore route | DiagnosticsSupport | `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SaveWorkspace`, `LoadWorkspace`); `src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`workspace.save`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`workspace-save-restore`); `tests/AsterGraph.Editor.Tests/GraphEditorDiagnosticsInspectionTests.cs` (`workspace.save.succeeded`); `tests/AsterGraph.Editor.Tests/GraphEditorServiceSeamsTests.cs` (`workspace.load.succeeded`) | [Demo Cookbook](./demo-cookbook.md); catalog path: `docs/en/demo-cookbook.md`; evidence: `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK` | `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`, `WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK` | @@ -52,7 +53,7 @@ Use it when you want to move from "I can see the Demo" to "I know which code and `DEMO_COOKBOOK_ROUTE_COVERAGE_OK` proves the cookbook keeps route posture explicit: -- `Supported SDK route`: `starter-host-route`, `interaction-selection-marquee-route`, `interaction-keyboard-navigation-route`, `lifecycle-clipboard-fragment-route`, and authoring contract routes. +- `Supported SDK route`: `starter-host-route`, `interaction-selection-marquee-route`, `interaction-lasso-screenshot-proof-route`, `interaction-keyboard-navigation-route`, `lifecycle-clipboard-fragment-route`, and authoring contract routes. - `Proof/demo route`: `builtin-minimap-workbench-route`, `builtin-background-grid-route`, `interaction-host-event-inspector-route`, `lifecycle-workspace-save-restore-route`, `lifecycle-validation-helper-route`, `groups-subgraphs-route`, `plugin-trust-route`, `diagnostics-support-route`, and `review-help-route`. - `Hosted UI route`: copy `AsterGraphEditorFactory.Create(...)` + `AsterGraphAvaloniaViewFactory.Create(...)` or thin `AsterGraphHostBuilder` composition. - `Runtime-only route`: use `AsterGraphEditorFactory.CreateSession(...)` + `IGraphEditorSession` when the host owns its UI. @@ -75,6 +76,7 @@ Category-derived route posture comes from `DemoCookbookRecipeCategory` and `Reci - `builtin-edge-toolbar-route` / Built-in EdgeToolbar route / Authoring: `src/AsterGraph.Avalonia/Controls/EdgeToolbar.cs` (`EdgeToolbar`); `src/AsterGraph.Avalonia/Hosting/AsterGraphAuthoringToolActionFactory.cs` (`CreateConnectionActions`); `src/AsterGraph.Avalonia/Hosting/AsterGraphBuiltInComponentCatalog.cs` (`builtin-edge-toolbar-route`); `src/AsterGraph.Demo/DemoGraphFactory.cs` (`clipboard-fragment-roundtrip`); `tests/AsterGraph.Editor.Tests/AsterGraphBuiltInToolbarTests.cs` (`EdgeToolbar_RendersCanonicalEdgeActionsAndExecutesDisconnectCommand`); `docs/en/demo-cookbook.md` (`BUILTIN_EDGE_TOOLBAR_OK`). Built-in EdgeToolbar route: compose `EdgeToolbar` with an `IGraphEditorSession` and connection id, then inspect CreateConnectionActions evidence. Supported seams live in `AsterGraph.Avalonia` EdgeToolbar and AsterGraphAuthoringToolActionFactory over `AsterGraph.Editor` command descriptors. Demo cookbook provides a screenshot fixture and proof anchors only; Demo does not add connection-local command logic. EdgeToolbar coverage is limited to the public EdgeToolbar control, action projection, and screenshot fixture; it does not add connection-local command logic, telemetry, or workflow automation. - `builtin-node-resizer-route` / Built-in NodeResizer route / Authoring: `src/AsterGraph.Avalonia/Controls/NodeResizer.cs` (`NodeResizer`); `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`TrySetNodeSize`); `src/AsterGraph.Avalonia/Hosting/AsterGraphBuiltInComponentCatalog.cs` (`builtin-node-resizer-route`); `src/AsterGraph.Demo/DemoGraphFactory.cs` (`validation-prevent-cycle`); `tests/AsterGraph.Editor.Tests/AsterGraphBuiltInNodeResizerTests.cs` (`NodeResizer_RendersStableHandlesAndCommitsResizeThroughSessionCommand`); `docs/en/demo-cookbook.md` (`BUILTIN_NODE_RESIZER_OK`). Built-in NodeResizer route: compose `NodeResizer` with an `IGraphEditorSession` and node id, then inspect TrySetNodeSize evidence. Supported seams live in `AsterGraph.Avalonia` NodeResizer and `AsterGraph.Editor` node surface size commands. Demo cookbook provides a screenshot fixture and proof anchors only; Demo does not add an alternate resize engine. NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Interaction selection marquee route: launch `selection-marquee-workbench` and inspect GetSelectionRectangleSnapshot plus marquee-selection proof evidence. Supported seams live in `AsterGraph.Editor` selection query/command contracts and `AsterGraph.Avalonia` overlay coordinator controls. Demo cookbook provides a rectangular fixture and screenshot-gate coverage only; Demo does not add another selection model or hit-test path. +- `interaction-lasso-screenshot-proof-route`: Interaction lasso screenshot proof route: launch `selection-marquee-workbench`, set `NodeCanvasSelectionMode.Lasso`, and capture the active transient lasso overlay. Supported seams live in `AsterGraph.Avalonia` NodeCanvas lasso feedback and the existing `AsterGraph.Editor` selection contracts. Demo cookbook provides a screenshot proof fixture and manifest state only; Demo does not add toolbar UX, eraser behavior, drawing primitives, persistence, or a renderer rewrite. - `interaction-keyboard-navigation-route`: Interaction keyboard navigation route: launch `keyboard-navigation-lab` and inspect NodeCanvas arrow-key, viewport shortcut, and automation peer evidence. Supported seams live in `AsterGraph.Avalonia` canvas controls and `AsterGraph.Editor` command descriptor/shortcut contracts. Demo cookbook provides a spatial fixture and screenshot-gate coverage only; Demo does not add a separate input model or accessibility framework. - `interaction-host-event-inspector-route`: Interaction host event inspector route: launch `host-event-inspector` and inspect IGraphEditorEvents plus mutation batching evidence. Supported seams live in `AsterGraph.Editor` session event contracts and mutation batching internals. Demo cookbook provides a host-observable fixture and screenshot-gate coverage only; Demo does not add telemetry, remote sync, or a new event broker. - `lifecycle-workspace-save-restore-route`: Lifecycle workspace save/restore route: launch `workspace-save-restore` and inspect SaveWorkspace, LoadWorkspace, and workspace diagnostics evidence. Supported seams live in `AsterGraph.Editor` workspace command contracts, command descriptors, and diagnostics surfaces. Demo cookbook provides a save/restore fixture and screenshot-gate coverage only; Demo does not add a persistence engine or storage policy. @@ -99,6 +101,7 @@ Category-derived route posture comes from `DemoCookbookRecipeCategory` and `Reci - `builtin-edge-toolbar-route`: EdgeToolbar coverage is limited to the public EdgeToolbar control, action projection, and screenshot fixture; it does not add connection-local command logic, telemetry, or workflow automation. - `builtin-node-resizer-route`: NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Selection marquee fixture coverage is limited to existing selection query, command, and overlay contracts; it does not add a spatial index, alternate selection model, or custom hit-test runtime. +- `interaction-lasso-screenshot-proof-route`: Lasso screenshot proof coverage is limited to existing lasso selection activation, transient overlay feedback, Cookbook scene capture, and shell visual metadata; the explicit exclusions remain no toolbar UX, no eraser behavior, no drawing primitives, no persistence, no strict pixel baselines, no retained API removal, and no full whiteboard parity. - `interaction-keyboard-navigation-route`: Keyboard navigation fixture coverage is limited to existing Avalonia canvas controls, automation peers, and command shortcut contracts; it does not add a custom input framework or full accessibility provider suite. - `interaction-host-event-inspector-route`: Host event fixture coverage is bounded to existing session event contracts and mutation batching; it does not add telemetry, remote sync, time-based throttling, or a separate event broker. - `lifecycle-workspace-save-restore-route`: Workspace lifecycle fixture coverage is limited to existing save/load commands, command descriptors, and diagnostics; it does not add a persistence engine, cloud sync, storage policy, or migration runtime. @@ -139,6 +142,7 @@ Route boundary strings for the v0.79 catalog: - `GraphOperations`: graph creation, command, overlay, or review paths are tied back to existing code/demo anchors. - `GraphOperations`: built-in screenshot fixtures use `minimap-workbench`, `background-grid-density`, `hosted-controls-panel`, `keyboard-navigation-lab`, `host-event-inspector`, `selection-marquee-workbench`, `clipboard-fragment-roundtrip`, and `validation-prevent-cycle` so Cookbook routes capture distinct graph documents instead of reusing the same scene. - `GraphOperations`: interaction screenshot fixtures use `selection-marquee-workbench`, `keyboard-navigation-lab`, and `host-event-inspector` so selection, keyboard, and event routes capture distinct graph documents. +- `GraphOperations`: Phase 536 / GitHub #195 / `avalonia-node-map-uvd` adds `cookbook-interaction-lasso-screenshot-proof` on `selection-marquee-workbench` as a bounded lasso screenshot proof route, not a new graph fixture family. - `GraphOperations`: lifecycle screenshot fixtures use `workspace-save-restore`, `clipboard-fragment-roundtrip`, and `validation-prevent-cycle` so save/load, clipboard, and validation helper routes capture distinct graph documents. - `GraphOperations`: `LAYOUT_PROVIDER_EVIDENCE_EXPANSION` keeps layout evidence synchronous and host-owned through `UseLayoutProvider(...)` / `IGraphLayoutProvider.CreateLayoutPlan(GraphLayoutRequest)`. `GraphLayoutRequest`, `GraphLayoutPlan`, `PreviewLayoutPlan`, `TryApplyLayoutPlan`, `TryApplyLayoutRequest`, `TrySnapSelectedNodesToGrid`, and `TrySnapAllNodesToGrid` stay on `IGraphEditorSession` commands and are verified through `GraphEditorLayoutProviderSeamTests`, `LAYOUT_PROVIDER_SEAM_OK`, `LAYOUT_PREVIEW_APPLY_CANCEL_OK`, and `LAYOUT_UNDO_TRANSACTION_OK`. This proof does not make layout execution asynchronous or cancellable. - `NodeMetadata`: metadata and trust evidence stay anchored to recipe code or proof markers. @@ -147,7 +151,7 @@ Route boundary strings for the v0.79 catalog: - `ValidationRuntimeOverlay`: validation, runtime overlay, support bundle, repair/help, and designer outline evidence is represented without enabling a workflow engine; outline state stays query-owned through `GetNavigatorOutlineSnapshot` and proof marker `DESIGNER_WORKBENCH_AUTHORING_OK`. Built-in recipes add `ToMiniMapBudgetMarker`, `CalculateVisibleLineMetrics_WithExtremeZoomSpacing_KeepsLineDensityBounded`, and `RecoveryHint` as route-specific validation cues. - `SupportEvidence`: each route keeps support claims tied to local proof or docs evidence. - `SupportEvidence`: built-in proof markers `BUILTIN_MINIMAP_WORKBENCH_OK`, `GRID_BACKGROUND_DENSITY_OK`, `HOSTED_CONTROLS_PANEL_COMPOSITION_OK`, `BUILTIN_STANDALONE_CONTROLS_OK`, `BUILTIN_STANDALONE_PANEL_OK`, `BUILTIN_NODE_TOOLBAR_OK`, `BUILTIN_EDGE_TOOLBAR_OK`, and `BUILTIN_NODE_RESIZER_OK` bind the batch to existing workbench and standalone surfaces. -- `SupportEvidence`: interaction proof markers `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`, `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK`, and `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` bind the batch to existing interaction and event surfaces. +- `SupportEvidence`: interaction proof markers `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`, `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`, `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK`, and `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` bind the batch to existing interaction and event surfaces. - `SupportEvidence`: lifecycle proof markers `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`, `LIFECYCLE_CLIPBOARD_FRAGMENT_FIXTURE_OK`, `LIFECYCLE_VALIDATION_HELPER_FIXTURE_OK`, `WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK`, `CLIPBOARD_FRAGMENT_ROUNDTRIP_OK`, and `VALIDATION_HELPER_ROUTE_OK` bind the batch to existing lifecycle surfaces. - `HostCodeExample`: host-copy examples remain anchored to starter or consumer sample code. @@ -164,6 +168,7 @@ Route boundary strings for the v0.79 catalog: - `Selection`: built-in Background/Grid and hosted Controls/Panel recipes reuse `TrySnapSelectedNodesToGrid` and `AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`. - `Selection`: marquee selection supports union and toggle modes through modifier keys (`UpdateMarqueeSelection`); arrow-key nudge moves selected nodes with shift for larger steps (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`). - `Selection`: the runnable interaction fixtures keep rectangle selection (`selection-marquee-workbench`), arrow-key movement (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`), and event cadence (`SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`) source-backed. +- `Selection`: Phase 536 keeps lasso screenshot proof source-backed through `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`, `shell-cookbook-lasso-screenshot-proof`, and `full-window-shell-lasso-state`; it adds no toolbar UX, no eraser, no drawing primitives, no persistence, and no full whiteboard parity. - `Connection`: the interaction fixtures keep selected-route and mutation effects inspectable through `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` and `IGraphEditorEvents`. - `Connection`: lifecycle fixtures keep fragment connection remapping and invalid route repair inspectable through `SessionCommands_RoundTripFragmentWorkspaceAndTemplateLibrary` and `validation.connection.route.reset`. - `Inspection`: hosted entry points, minimap cadence, trust decisions, support logs, and proof panels remain inspectable (`BuildAvaloniaApp`, `ToMiniMapBudgetMarker`, `PluginTrustDecision`, `RuntimeLogs`, `DemoHostMenuGroups.Proof`). @@ -171,6 +176,7 @@ Route boundary strings for the v0.79 catalog: - `Inspection`: standalone built-in recipes remain inspectable through `AsterGraphControls`, `AsterGraphPanel`, `NodeToolbar`, `EdgeToolbar`, `NodeResizer`, `AsterGraphPanelPosition`, `NodeToolbar_RendersCanonicalNodeActionsAndExecutesDuplicateCommand`, `EdgeToolbar_RendersCanonicalEdgeActionsAndExecutesDisconnectCommand`, and `NodeResizer_RendersStableHandlesAndCommitsResizeThroughSessionCommand`. - `Inspection`: automation peers expose node titles and canvas group structure (`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`). - `Inspection`: interaction fixtures expose `GetSelectionRectangleSnapshot`, `keyboard-navigation-lab`, `host-event-inspector`, and `DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers` as screenshot-backed cues. +- `Inspection`: lasso proof artifacts expose `PART_NodeCanvas`, `shell-cookbook-lasso-screenshot-proof`, and `full-window-shell-lasso-state` as screenshot-backed cues for the active transient lasso path. - `Inspection`: lifecycle fixtures expose `workspace.save`, `workspace.save.succeeded`, `workspace.load.succeeded`, `astergraph.clipboard/v1`, `TryFocusValidationIssue`, and `validation-prevent-cycle` as screenshot-backed cues. - `ValidationRuntimeFeedback`: extension, runtime, review, and projection feedback stay local and source-backed (`PluginTrust`, `RuntimeDiagnosticEntry`, `ValidationFeedback`, `MINIMAP_LIGHTWEIGHT_PROJECTION_OK`). - `ValidationRuntimeFeedback`: built-in routes keep feedback source-backed through `ToMiniMapBudgetMarker`, `AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`, `RecoveryHint`, `CreateCommandActions`, `CreateNodeActions`, `CreateConnectionActions`, and `TrySetNodeSize`. @@ -198,13 +204,13 @@ dotnet test tests/AsterGraph.Demo.Tests/AsterGraph.Demo.Tests.csproj --configura `DemoCookbookScreenshotGateTests` reads `tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json`, captures each route through the canonical scene PNG exporter, and writes PNG plus metadata under `artifacts/test-results/cookbook-screenshot-gate`. This scene gate proves graph document, viewport, node/edge rendering, and route metadata only; it does not capture the Demo window chrome, host drawer, left Cookbook navigation, overlays, or shell panels. Its metadata records `PngSha256` plus a Phase 512 `DriftMeasurement` object with `Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture` so generated artifacts can be compared across hosts without enforcing strict pixel hash pass/fail. Add future Cookbook routes by appending manifest rows; do not edit the test internals for routine route coverage. -The same test class also includes a manifest-driven full-window shell visual gate. `tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` currently captures `shell-cookbook-default-open`, `shell-cookbook-default-open-zh-cn`, `shell-cookbook-default-closed`, `shell-cookbook-default-closed-zh-cn`, `shell-runtime-diagnostics-open`, `shell-runtime-diagnostics-open-zh-cn`, `shell-runtime-diagnostics-closed`, the bounded Phase 508 flyout state `shell-cookbook-default-view-menu-flyout`, the bounded Phase 509 tooltip popup state `shell-cookbook-default-host-command-tooltip-popup`, and the bounded Phase 510 context-menu state `shell-cookbook-default-canvas-context-menu` from the default `starter-host-route` / `ai-pipeline` route. Each shell state declares its own language/theme metadata, so the gate now covers the English Cookbook drawer, the Chinese Cookbook drawer, the English and Chinese Cookbook closed-drawer shells, the English and Chinese runtime diagnostics drawers, the selected English runtime diagnostics closed-shell state, one English View menu flyout, one English disabled host-command tooltip popup, and one English canvas context menu without changing the route scene fixture. It opens the Demo in Avalonia headless Skia rendering, selects each shell state, captures the full window, and writes PNG plus metadata under `artifacts/test-results/cookbook-shell-visual-gate`. The metadata records `CaptureScope=full-window-shell-state` for base shell states, `CaptureScope=full-window-shell-flyout-state` for the flyout row, `CaptureScope=full-window-shell-popup-state` for the tooltip popup row, or `CaptureScope=full-window-shell-context-menu-state` for the context-menu row, requested and actual pixel dimensions, selected route and recipe, selected language/theme, covered shell parts, optional flyout menu part, covered flyout headers, optional popup target part, covered popup text, optional context-menu target part, covered context-menu headers, non-transparent pixel count, distinct color count, `PngSha256`, and the same record-only `DriftMeasurement` host metadata used by the scene gate. +The same test class also includes a manifest-driven full-window shell visual gate. `tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` currently captures `shell-cookbook-default-open`, `shell-cookbook-default-open-zh-cn`, `shell-cookbook-default-closed`, `shell-cookbook-default-closed-zh-cn`, `shell-runtime-diagnostics-open`, `shell-runtime-diagnostics-open-zh-cn`, `shell-runtime-diagnostics-closed`, the bounded Phase 508 flyout state `shell-cookbook-default-view-menu-flyout`, the bounded Phase 509 tooltip popup state `shell-cookbook-default-host-command-tooltip-popup`, the bounded Phase 510 context-menu state `shell-cookbook-default-canvas-context-menu`, and the bounded Phase 536 lasso state `shell-cookbook-lasso-screenshot-proof`. Each shell state declares its own language/theme metadata, so the gate now covers the English Cookbook drawer, the Chinese Cookbook drawer, the English and Chinese Cookbook closed-drawer shells, the English and Chinese runtime diagnostics drawers, the selected English runtime diagnostics closed-shell state, one English View menu flyout, one English disabled host-command tooltip popup, one English canvas context menu, and one English active lasso overlay capture. It opens the Demo in Avalonia headless Skia rendering, selects each shell state, captures the full window, and writes PNG plus metadata under `artifacts/test-results/cookbook-shell-visual-gate`. The metadata records `CaptureScope=full-window-shell-state` for base shell states, `CaptureScope=full-window-shell-flyout-state` for the flyout row, `CaptureScope=full-window-shell-popup-state` for the tooltip popup row, `CaptureScope=full-window-shell-context-menu-state` for the context-menu row, or `CaptureScope=full-window-shell-lasso-state` for the lasso row, requested and actual pixel dimensions, selected route and recipe, selected language/theme, covered shell parts, optional flyout menu part, covered flyout headers, optional popup target part, covered popup text, optional context-menu target part, covered context-menu headers, optional lasso target part, `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`, non-transparent pixel count, distinct color count, `PngSha256`, and the same record-only `DriftMeasurement` host metadata used by the scene gate. The `shell-cookbook-default-open` and `shell-cookbook-default-open-zh-cn` states cover `PART_HostMenu`, `PART_HostShellSplitView`, `PART_CookbookWorkspaceNavigationPanel`, `PART_MainGraphEditorHost`, and `PART_CookbookWorkspaceRecipeContentPanel`. The `shell-cookbook-default-view-menu-flyout` state additionally opens `PART_ViewMenu`, writes `shell-cookbook-default-view-menu-flyout.png`, and requires the flyout headers `Show header`, `Show library`, `Show inspector`, `Show status bar`, `Show mini map`, and `Open view controls`. The `shell-cookbook-default-host-command-tooltip-popup` state opens the disabled undo command tooltip on `PART_HostCommand_history.undo`, writes `shell-cookbook-default-host-command-tooltip-popup.png`, records `full-window-shell-popup-state`, and requires the popup text `Nothing to undo yet.`. The `shell-cookbook-default-canvas-context-menu` state opens the default canvas context menu on `PART_NodeCanvas`, writes `shell-cookbook-default-canvas-context-menu.png`, records `full-window-shell-context-menu-state`, and requires the context-menu headers `Add Node`, `Fit View`, and `Reset View`. The `shell-cookbook-default-closed`, `shell-cookbook-default-closed-zh-cn`, and `shell-runtime-diagnostics-closed` states keep the required parts to closed-shell chrome only: `PART_HostMenu`, `PART_HostShellSplitView`, and `PART_MainGraphEditorHost`. The `shell-runtime-diagnostics-open` and `shell-runtime-diagnostics-open-zh-cn` states cover the runtime drawer with `PART_RuntimeSummarySection`, `PART_RuntimeInspectionSection`, `PART_RuntimeDiagnosticsSection`, and `PART_MainGraphEditorHost`. Phase 511 / GitHub #142 / `avalonia-node-map-9rq` adds only the two bounded `zh-CN` + `canonical-dark` shell-state rows: `shell-cookbook-default-closed-zh-cn` and `shell-runtime-diagnostics-open-zh-cn`. All states assert the expected drawer-open or drawer-closed state, minimum rendered size, nonblank pixels, distinct colors, output path, metadata source, declared language/theme, and required named shell parts. -The first gate route is `starter-host-route` with the `ai-pipeline` scenario, `1480x900` viewport metadata, English UI text, and the `canonical-dark` theme. The built-in batch adds `builtin-minimap-workbench-route` / `minimap-workbench`, `builtin-background-grid-route` / `background-grid-density`, `builtin-hosted-controls-route` / `hosted-controls-panel`, `builtin-standalone-controls-route` / `keyboard-navigation-lab`, `builtin-standalone-panel-route` / `host-event-inspector`, `builtin-node-toolbar-route` / `selection-marquee-workbench`, `builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip`, and `builtin-node-resizer-route` / `validation-prevent-cycle`. The interaction batch adds `interaction-selection-marquee-route` / `selection-marquee-workbench`, `interaction-keyboard-navigation-route` / `keyboard-navigation-lab`, and `interaction-host-event-inspector-route` / `host-event-inspector`. The lifecycle batch adds `lifecycle-workspace-save-restore-route` / `workspace-save-restore`, `lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip`, and `lifecycle-validation-helper-route` / `validation-prevent-cycle`; each manifest row records `expectedDocumentTitle`, `minimumNodeCount`, `minimumConnectionCount`, and `requiredNodeIds` so the gate proves distinct scene fixtures. Attach the generated before/after PNG and `metadata.json` when a PR changes visuals. +The first gate route is `starter-host-route` with the `ai-pipeline` scenario, `1480x900` viewport metadata, English UI text, and the `canonical-dark` theme. The built-in batch adds `builtin-minimap-workbench-route` / `minimap-workbench`, `builtin-background-grid-route` / `background-grid-density`, `builtin-hosted-controls-route` / `hosted-controls-panel`, `builtin-standalone-controls-route` / `keyboard-navigation-lab`, `builtin-standalone-panel-route` / `host-event-inspector`, `builtin-node-toolbar-route` / `selection-marquee-workbench`, `builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip`, and `builtin-node-resizer-route` / `validation-prevent-cycle`. The interaction batch adds `interaction-selection-marquee-route` / `selection-marquee-workbench`, `interaction-lasso-screenshot-proof-route` / `selection-marquee-workbench`, `interaction-keyboard-navigation-route` / `keyboard-navigation-lab`, and `interaction-host-event-inspector-route` / `host-event-inspector`. The lifecycle batch adds `lifecycle-workspace-save-restore-route` / `workspace-save-restore`, `lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip`, and `lifecycle-validation-helper-route` / `validation-prevent-cycle`; each manifest row records `expectedDocumentTitle`, `minimumNodeCount`, `minimumConnectionCount`, and `requiredNodeIds` so the gate proves distinct scene fixtures. Phase 536 / GitHub #195 / `avalonia-node-map-uvd` adds only the lasso screenshot proof rows `cookbook-interaction-lasso-screenshot-proof` and `shell-cookbook-lasso-screenshot-proof`; no strict pixel baseline, toolbar UX, eraser, drawing primitives, persistence, renderer rewrite, retained API removal, or full whiteboard parity is claimed. Attach the generated before/after PNG and `metadata.json` when a PR changes visuals. -CI posture: both the scene PNG gate and the full-window shell visual gate are wired into `AsterGraph.Demo.Tests`, so the normal net9 validation lane runs them. The scene gate checks deterministic artifact generation, PNG validity, route metadata, and minimum image invariants. The shell gate additionally fails when a full-window artifact is missing, blank, undersized, has the wrong drawer state, misses the expected language/theme metadata, no longer covers the expected shell parts, when the Phase 508 flyout row cannot open `PART_ViewMenu` and record its required headers, when the Phase 509 popup row cannot open `PART_HostCommand_history.undo` and record `Nothing to undo yet.`, when the Phase 510 context-menu row cannot open `PART_NodeCanvas` and record `Add Node`, `Fit View`, and `Reset View`, or when the Phase 511 rows cannot record `zh-CN` + `canonical-dark` metadata for the closed Cookbook shell and runtime diagnostics drawer. Phase 512 / GitHub #143 / `avalonia-node-map-1j4` keeps both gates as record-only pixel-baseline drift measurement: generated metadata records `PngSha256`, `DriftMeasurement.Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture`, with no strict pixel baseline enforcement. Broad context-menu coverage beyond the listed canvas row, broad popup coverage beyond the listed tooltip popup, broad language/theme variants beyond the listed shell states, visual redesign, runtime behavior changes, public API changes, retained API removal, and strict pixel baselines remain outside this gate; Phase 508 / GitHub #139 / `avalonia-node-map-2nu` only adds one bounded View menu flyout manifest state, Phase 509 / GitHub #140 / `avalonia-node-map-0ff` only adds one bounded host-command tooltip popup manifest state, Phase 510 / GitHub #141 / `avalonia-node-map-8lu` only adds one bounded canvas context-menu manifest state, and Phase 511 / GitHub #142 / `avalonia-node-map-9rq` only adds two bounded `zh-CN` + `canonical-dark` shell-state rows. +CI posture: both the scene PNG gate and the full-window shell visual gate are wired into `AsterGraph.Demo.Tests`, so the normal net9 validation lane runs them. The scene gate checks deterministic artifact generation, PNG validity, route metadata, and minimum image invariants. The shell gate additionally fails when a full-window artifact is missing, blank, undersized, has the wrong drawer state, misses the expected language/theme metadata, no longer covers the expected shell parts, when the Phase 508 flyout row cannot open `PART_ViewMenu` and record its required headers, when the Phase 509 popup row cannot open `PART_HostCommand_history.undo` and record `Nothing to undo yet.`, when the Phase 510 context-menu row cannot open `PART_NodeCanvas` and record `Add Node`, `Fit View`, and `Reset View`, when the Phase 511 rows cannot record `zh-CN` + `canonical-dark` metadata for the closed Cookbook shell and runtime diagnostics drawer, or when the Phase 536 lasso row cannot open `PART_NodeCanvas`, set `NodeCanvasSelectionMode.Lasso`, record `full-window-shell-lasso-state`, and keep the `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` transient path visible for capture. Phase 512 / GitHub #143 / `avalonia-node-map-1j4` keeps both gates as record-only pixel-baseline drift measurement: generated metadata records `PngSha256`, `DriftMeasurement.Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture`, with no strict pixel baseline enforcement. Broad context-menu coverage beyond the listed canvas row, broad popup coverage beyond the listed tooltip popup, broad language/theme variants beyond the listed shell states, visual redesign, runtime behavior changes, public API changes, retained API removal, and strict pixel baselines remain outside this gate; Phase 508 / GitHub #139 / `avalonia-node-map-2nu` only adds one bounded View menu flyout manifest state, Phase 509 / GitHub #140 / `avalonia-node-map-0ff` only adds one bounded host-command tooltip popup manifest state, Phase 510 / GitHub #141 / `avalonia-node-map-8lu` only adds one bounded canvas context-menu manifest state, Phase 511 / GitHub #142 / `avalonia-node-map-9rq` only adds two bounded `zh-CN` + `canonical-dark` shell-state rows, and Phase 536 / GitHub #195 / `avalonia-node-map-uvd` only adds one bounded lasso screenshot state. Phase 515 / GitHub #151 / `avalonia-node-map-t44` decides the strict pixel-baseline policy from that Phase 512 evidence: strict single-hash enforcement remains deferred. The reviewed evidence is two successful Phase 512 release-validation artifact sets, each with 25 metadata.json files under `artifacts/test-results`, where every generated scene and shell artifact recorded `PngSha256`, `DriftMeasurement`, `Policy=record-only`, `PngHashPurpose=drift-evidence`, `StrictPixelBaselineEnforced=false`, `HostRuntimeDescription`, `OsDescription`, and `ProcessArchitecture`. One same-path full-window shell popup artifact, `shell-cookbook-default-host-command-tooltip-popup`, produced different `PngSha256` values across those two successful Windows release-validation runs while the metadata stayed record-only. That is enough evidence to keep the current invariants, but not enough to make a brittle single-hash pass/fail gate. The trigger for a future pass/fail policy is repeatable no-drift evidence for the same route/state set across supported CI hosts, or an explicitly host-keyed or tolerant comparator with reviewed thresholds. diff --git a/docs/en/phase-0-reactflow-parity-audit.md b/docs/en/phase-0-reactflow-parity-audit.md index b15b2966..cd1180a4 100644 --- a/docs/en/phase-0-reactflow-parity-audit.md +++ b/docs/en/phase-0-reactflow-parity-audit.md @@ -186,6 +186,10 @@ Phase 534 is GitHub #191 / `avalonia-node-map-lzy`, the lasso visual gesture fee Phase 535 is GitHub #193 / `avalonia-node-map-8l6`, the post-Phase-534 parity queue refresh. This docs/tests-only slice records Phase 534 as closed and turns the remaining whiteboard pressure into tracker-backed follow-up candidates for lasso screenshot proof, lasso toolbar UX/public activation ergonomics, eraser behavior/API feasibility, rectangle/freehand drawing primitives, and whiteboard persistence/render-layer readiness. It authorizes no runtime behavior changes, no public API changes, no UI redesign, no screenshot manifest expansion, no strict pixel baseline enforcement, no retained API removal, and no whiteboard implementation. +## Phase 536 Update + +Phase 536 is GitHub #195 / `avalonia-node-map-uvd`, the lasso screenshot route and Cookbook proof boundary. This implementation slice adds the scene route `cookbook-interaction-lasso-screenshot-proof` / `interaction-lasso-screenshot-proof-route` on the existing `selection-marquee-workbench` fixture and the shell state `shell-cookbook-lasso-screenshot-proof`. The shell state targets `PART_NodeCanvas`, sets `NodeCanvasSelectionMode.Lasso`, captures the active transient lasso path before release, and records `full-window-shell-lasso-state` metadata tied to `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` plus proof marker `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`. It authorizes no toolbar UX, no eraser behavior, no drawing primitives, no persistence, no renderer rewrite, no strict pixel baseline enforcement, no retained API removal, and no full whiteboard parity. + ## Phase 489 Update Phase 489 closed GitHub #101 / `avalonia-node-map-6sc` through PR #102 as a renderer virtualization design spike on branch `perf/renderer-virtualization-spike`. This slice was docs/tests only: it defined the proof contract required before any future ItemsRepeater/Skia-style renderer virtualization, background graph index, or graph-size claim expansion. It made no public API change and no runtime change. The current evidence remains viewport-budgeted scene projection/rendering, not a true renderer virtualization contract; `xlarge` stays telemetry-only. @@ -489,10 +493,12 @@ Phase 533 adds public lasso pointer-mode activation through GitHub #189 / `avalo Phase 534 adds the lasso visual gesture feedback route through GitHub #191 / `avalonia-node-map-lzy`. It renders a transient lasso path on the existing `OverlayLayer` during active lasso drags through `UpdateLassoFeedback(...)` and clears it through `ClearLassoFeedback()` on release or capture loss, while retaining toolbar UX, lasso screenshot route, eraser tool behavior, drawing primitives, persistence, renderer layer, React Flow-like examples, and full React Flow whiteboard parity as gaps. +Phase 536 adds the bounded lasso screenshot proof through GitHub #195 / `avalonia-node-map-uvd`. It adds `cookbook-interaction-lasso-screenshot-proof`, `interaction-lasso-screenshot-proof-route`, and `shell-cookbook-lasso-screenshot-proof` as visual evidence for the existing transient lasso path, while retaining toolbar UX, eraser behavior, drawing primitives, persistence, renderer rewrite, strict pixel baseline enforcement, retained API removal, and full whiteboard parity as gaps. + | GitHub | Bead | Title | Priority | Likely write set | Parallelism | | --- | --- | --- | --- | --- | --- | | #193 | `avalonia-node-map-8l6` | Phase 535: refresh post-lasso visual feedback parity queue | P2 | parity roadmap docs and focused docs tests | Current docs/test queue refresh. Blocks the next implementation wave because it replaces the stale Phase 534 current row with tracker-backed follow-ups. | -| TBD | TBD | Phase 536: lasso screenshot route and Cookbook proof boundary | P2 | Cookbook screenshot manifest, Demo fixture route, scene/shell screenshot tests, and parity docs | Ready after Phase 535. Sequential with lasso toolbar work because both may touch lasso Cookbook route copy and screenshot documentation. | +| #195 | `avalonia-node-map-uvd` | Phase 536: lasso screenshot route and Cookbook proof boundary | P2 | Cookbook screenshot manifest, Demo fixture route, scene/shell screenshot tests, and parity docs | Current visual proof slice. Blocks lasso toolbar work until the screenshot route and Cookbook proof boundary are stable. | | TBD | TBD | Phase 537: lasso toolbar UX and public activation ergonomics boundary | P2 | hosted authoring tools, `NodeCanvas.SelectionMode` activation surface, Demo/Cookbook route, and editor/Avalonia tests | Ready after Phase 535. Do not run in parallel with Phase 536 if both need the same lasso route text or visual proof fixture. | | TBD | TBD | Phase 538: eraser behavior/API feasibility gate | P3 | editor selection/delete commands, Avalonia hit-testing route, parity docs, and focused feasibility tests | Can run after Phase 535 in parallel with drawing model planning if it avoids shared pointer-mode state edits. | | TBD | TBD | Phase 539: rectangle/freehand drawing primitive model gate | P3 | Core/Editor model contract docs/tests and whiteboard primitive API inventory | Can run after Phase 535 in parallel with eraser feasibility if it stays docs/model-only and does not touch Avalonia pointer coordinators. | @@ -533,7 +539,7 @@ Phase 534 adds the lasso visual gesture feedback route through GitHub #191 / `av - `docs/phase-527-background-variant-gate`: owns #177 / `avalonia-node-map-dim`; current docs/test worktree for the Background variant public surface gate. - `docs/phase-528-panel-overlay-boundary`: owns #179 / `avalonia-node-map-9ow`; current docs/test worktree for the Panel versus viewport-attached overlay boundary. - `docs/phase-535-post-lasso-queue-refresh`: owns #193 / `avalonia-node-map-8l6`; current docs/test queue refresh after transient lasso feedback, with no runtime/API/UI/screenshot-manifest/whiteboard implementation changes. -- `visual/phase-536-lasso-screenshot-proof`: future candidate for the first lasso screenshot route and Cookbook proof boundary after Phase 535. +- `visual/phase-536-lasso-screenshot-proof`: owns #195 / `avalonia-node-map-uvd`; current slice for `cookbook-interaction-lasso-screenshot-proof`, `shell-cookbook-lasso-screenshot-proof`, and the bounded lasso screenshot proof boundary. - `feature/phase-537-lasso-toolbar-ergonomics`: future candidate for lasso toolbar UX and public activation ergonomics after Phase 535. - `feature/phase-538-eraser-feasibility`: future candidate for eraser behavior/API feasibility after Phase 535. - `docs/phase-539-drawing-primitive-model-gate`: future candidate for rectangle/freehand drawing primitive model decisions after Phase 535. @@ -591,4 +597,5 @@ Current coverage includes scene-level route captures plus ten manifest-driven fu - Phase 530 is GitHub #183 / `avalonia-node-map-8um`; it adds the backend/editor lasso selection query contract without Avalonia gesture capture, pointer-mode state machine, eraser behavior, drawing primitives, persistence, renderer changes, screenshot manifest expansion, strict visual-baseline enforcement, retained API removal, or a full React Flow whiteboard parity claim. - Phase 534 is GitHub #191 / `avalonia-node-map-lzy`; it adds transient lasso visual gesture feedback without toolbar UX, lasso screenshot route, eraser behavior, drawing primitives, persistence, renderer changes, screenshot manifest expansion, strict visual-baseline enforcement, retained API removal, or a full React Flow whiteboard parity claim. - Phase 535 is GitHub #193 / `avalonia-node-map-8l6`; it refreshes the post-Phase-534 parity queue without runtime behavior changes, public API changes, UI redesign, screenshot manifest expansion, strict pixel baseline enforcement, retained API removal, or whiteboard implementation. +- Phase 536 is GitHub #195 / `avalonia-node-map-uvd`; it adds `cookbook-interaction-lasso-screenshot-proof`, `interaction-lasso-screenshot-proof-route`, `shell-cookbook-lasso-screenshot-proof`, `full-window-shell-lasso-state`, and `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK` without toolbar UX, eraser behavior, drawing primitives, persistence, renderer rewrite, strict pixel baseline enforcement, retained API removal, or full whiteboard parity. - Product code remains out of scope for Phase 478, Phase 484, Phase 490, Phase 491, Phase 492, Phase 493, Phase 494, Phase 495, Phase 497, Phase 498, Phase 499, Phase 500, Phase 501, Phase 502, Phase 503, Phase 504, Phase 505, Phase 506, Phase 507, Phase 508, Phase 509, Phase 510, Phase 511, Phase 512, Phase 513, Phase 520, Phase 521, Phase 522, Phase 523, Phase 524, Phase 525, Phase 526, Phase 527, Phase 528, Phase 529, and Phase 535 unless a focused test proves a specific missing contract. diff --git a/docs/zh-CN/demo-cookbook.md b/docs/zh-CN/demo-cookbook.md index 7c0af05f..63c65414 100644 --- a/docs/zh-CN/demo-cookbook.md +++ b/docs/zh-CN/demo-cookbook.md @@ -35,6 +35,7 @@ Demo cookbook 是面向 AsterGraph 评估者的“代码 + 演示”索引。它 | `builtin-background-grid-route` / Built-in Background/Grid route | PerformanceViewport | `src/AsterGraph.Avalonia/Controls/GridBackground.cs` (`CalculateVisibleLineMetrics`);`src/AsterGraph.Abstractions/Styling/CanvasStyleOptions.cs` (`GridBackgroundHex`);`src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`TrySnapSelectedNodesToGrid`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`background-grid-density`);`tests/AsterGraph.Editor.Tests/GridBackgroundTests.cs` (`CalculateVisibleLineMetrics_WithExtremeZoomSpacing_KeepsLineDensityBounded`);`tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `GRID_BACKGROUND_DENSITY_OK` | `GRID_BACKGROUND_DENSITY_OK`、`LAYOUT_PROVIDER_SEAM_OK` | | `builtin-hosted-controls-route` / Built-in hosted Controls/Panel route | Authoring | `src/AsterGraph.Avalonia/Hosting/AsterGraphHostBuilder.cs` (`UseDefaultWorkbench`、`BuildAvaloniaView`);`src/AsterGraph.Avalonia/Hosting/AsterGraphWorkbenchOptions.cs` (`AsterGraphWorkbenchPanelState`);`src/AsterGraph.Editor/Runtime/GraphEditorCommandDescriptorSnapshot.cs` (`RecoveryHint`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`hosted-controls-panel`);`tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`);`tests/AsterGraph.Demo.Tests/DemoCookbookNavigationTests.cs` (`PART_CookbookWorkspaceNavigationPanel`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `HOSTED_CONTROLS_PANEL_COMPOSITION_OK` | `HOSTED_CONTROLS_PANEL_COMPOSITION_OK`、`DESIGNER_WORKBENCH_AUTHORING_OK` | | `interaction-selection-marquee-route` / Interaction selection marquee route | Authoring | `src/AsterGraph.Editor/Runtime/IGraphEditorQueries.cs` (`GetSelectionRectangleSnapshot`);`src/AsterGraph.Avalonia/Controls/Internal/Overlay/NodeCanvasOverlayCoordinator.cs` (`UpdateMarqueeSelection`);`src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SelectAll`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`);`tests/AsterGraph.Editor.Tests/GraphEditorSelectionTransformContractsTests.cs` (`Queries_GetSelectionRectangleSnapshot_ReturnsNodesAndConnectionsInRectangle`);`tests/AsterGraph.Editor.Tests/NodeCanvasOverlayCoordinatorTests.cs` (`UpdateMarqueeSelection_WithFinalizeTrue_UsesBackendSelectionRectangleQuery`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` | `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`、`SELECTION_RECTANGLE_QUERY_OK`、`SELECTION_RECTANGLE_MARQUEE_OK` | +| `interaction-lasso-screenshot-proof-route` / Interaction lasso screenshot proof route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvasSelectionMode.cs` (`Lasso`);`src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`UpdateLassoFeedback`、`ClearLassoFeedback`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`selection-marquee-workbench`);`tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`);`tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` (`shell-cookbook-lasso-screenshot-proof`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK` | `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`、`NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` | | `interaction-keyboard-navigation-route` / Interaction keyboard navigation route | Authoring | `src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs` (`HandleCanvasKeyDown`、`TryHandleCanvasArrowKey`);`src/AsterGraph.Avalonia/Controls/Internal/Automation/NodeCanvasAutomationPeer.cs` (`NodeCanvasAutomationPeer`);`src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`viewport.zoom-in`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`keyboard-navigation-lab`);`tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs` (`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`、`ArrowKey_Navigate_SelectsNearestNodeWhenNoSelection`);`tests/AsterGraph.Editor.Tests/GraphEditorViewTests.cs` (`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK` | `INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK`、`CANVAS_KEYBOARD_NAVIGATION_OK`、`ARROW_KEY_NEAREST_NODE_OK` | | `interaction-host-event-inspector-route` / Interaction host event inspector route | ReviewHelp | `src/AsterGraph.Editor/Runtime/IGraphEditorEvents.cs` (`IGraphEditorEvents`);`src/AsterGraph.Editor/Runtime/Mutation/GraphEditorSessionMutation.cs` (`BeginMutation`、`FlushPendingEvents`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`host-event-inspector`);`tests/AsterGraph.Editor.Tests/GraphEditorSessionTests.cs` (`SessionEvents_SubscribeAndUnsubscribe_DoNotLeakMemory`、`SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` | `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK`、`HOST_EVENT_SUBSCRIPTION_OK`、`EVENT_BATCHING_CADENCE_OK` | | `lifecycle-workspace-save-restore-route` / Lifecycle workspace save/restore route | DiagnosticsSupport | `src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`SaveWorkspace`、`LoadWorkspace`);`src/AsterGraph.Editor/Runtime/Internal/GraphEditorCommandDescriptorCatalog.cs` (`workspace.save`) | `src/AsterGraph.Demo/DemoGraphFactory.cs` (`workspace-save-restore`);`tests/AsterGraph.Editor.Tests/GraphEditorDiagnosticsInspectionTests.cs` (`workspace.save.succeeded`);`tests/AsterGraph.Editor.Tests/GraphEditorServiceSeamsTests.cs` (`workspace.load.succeeded`) | [Demo Cookbook](./demo-cookbook.md);catalog path: `docs/en/demo-cookbook.md`;evidence: `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK` | `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`、`WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK` | @@ -52,7 +53,7 @@ Demo cookbook 是面向 AsterGraph 评估者的“代码 + 演示”索引。它 `DEMO_COOKBOOK_ROUTE_COVERAGE_OK` 证明 cookbook 明确区分路线姿态: -- `Supported SDK route`: `starter-host-route`、`interaction-selection-marquee-route`、`interaction-keyboard-navigation-route`、`lifecycle-clipboard-fragment-route` 和 authoring contract routes。 +- `Supported SDK route`: `starter-host-route`、`interaction-selection-marquee-route`、`interaction-lasso-screenshot-proof-route`、`interaction-keyboard-navigation-route`、`lifecycle-clipboard-fragment-route` 和 authoring contract routes。 - `Proof/demo route`: `builtin-minimap-workbench-route`、`builtin-background-grid-route`、`interaction-host-event-inspector-route`、`lifecycle-workspace-save-restore-route`、`lifecycle-validation-helper-route`、`groups-subgraphs-route`、`plugin-trust-route`、`diagnostics-support-route` 和 `review-help-route`。 - `Hosted UI route`: 复制 `AsterGraphEditorFactory.Create(...)` + `AsterGraphAvaloniaViewFactory.Create(...)`,或薄封装 `AsterGraphHostBuilder` 组合。 - `Runtime-only route`: 宿主持有自己的 UI 时使用 `AsterGraphEditorFactory.CreateSession(...)` + `IGraphEditorSession`。 @@ -75,6 +76,7 @@ Category-derived route posture 来自 `DemoCookbookRecipeCategory` 和 `Recipe.I - `builtin-edge-toolbar-route` / Built-in EdgeToolbar route / Authoring: `src/AsterGraph.Avalonia/Controls/EdgeToolbar.cs` (`EdgeToolbar`);`src/AsterGraph.Avalonia/Hosting/AsterGraphAuthoringToolActionFactory.cs` (`CreateConnectionActions`);`src/AsterGraph.Avalonia/Hosting/AsterGraphBuiltInComponentCatalog.cs` (`builtin-edge-toolbar-route`);`src/AsterGraph.Demo/DemoGraphFactory.cs` (`clipboard-fragment-roundtrip`);`tests/AsterGraph.Editor.Tests/AsterGraphBuiltInToolbarTests.cs` (`EdgeToolbar_RendersCanonicalEdgeActionsAndExecutesDisconnectCommand`);`docs/en/demo-cookbook.md` (`BUILTIN_EDGE_TOOLBAR_OK`)。Built-in EdgeToolbar route: compose `EdgeToolbar` with an `IGraphEditorSession` and connection id, then inspect CreateConnectionActions evidence. Supported seams live in `AsterGraph.Avalonia` EdgeToolbar and AsterGraphAuthoringToolActionFactory over `AsterGraph.Editor` command descriptors. Demo cookbook provides a screenshot fixture and proof anchors only; Demo does not add connection-local command logic. EdgeToolbar coverage is limited to the public EdgeToolbar control, action projection, and screenshot fixture; it does not add connection-local command logic, telemetry, or workflow automation. - `builtin-node-resizer-route` / Built-in NodeResizer route / Authoring: `src/AsterGraph.Avalonia/Controls/NodeResizer.cs` (`NodeResizer`);`src/AsterGraph.Editor/Runtime/IGraphEditorCommands.cs` (`TrySetNodeSize`);`src/AsterGraph.Avalonia/Hosting/AsterGraphBuiltInComponentCatalog.cs` (`builtin-node-resizer-route`);`src/AsterGraph.Demo/DemoGraphFactory.cs` (`validation-prevent-cycle`);`tests/AsterGraph.Editor.Tests/AsterGraphBuiltInNodeResizerTests.cs` (`NodeResizer_RendersStableHandlesAndCommitsResizeThroughSessionCommand`);`docs/en/demo-cookbook.md` (`BUILTIN_NODE_RESIZER_OK`)。Built-in NodeResizer route: compose `NodeResizer` with an `IGraphEditorSession` and node id, then inspect TrySetNodeSize evidence. Supported seams live in `AsterGraph.Avalonia` NodeResizer and `AsterGraph.Editor` node surface size commands. Demo cookbook provides a screenshot fixture and proof anchors only; Demo does not add an alternate resize engine. NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Interaction selection marquee route: launch `selection-marquee-workbench` and inspect GetSelectionRectangleSnapshot plus marquee-selection proof evidence. Supported seams live in `AsterGraph.Editor` selection query/command contracts and `AsterGraph.Avalonia` overlay coordinator controls. Demo cookbook provides a rectangular fixture and screenshot-gate coverage only; Demo does not add another selection model or hit-test path. +- `interaction-lasso-screenshot-proof-route`: Interaction lasso screenshot proof route: launch `selection-marquee-workbench`, set `NodeCanvasSelectionMode.Lasso`, and capture the active transient lasso overlay. Supported seams live in `AsterGraph.Avalonia` NodeCanvas lasso feedback and the existing `AsterGraph.Editor` selection contracts. Demo cookbook provides a screenshot proof fixture and manifest state only; Demo does not add toolbar UX, eraser behavior, drawing primitives, persistence, or a renderer rewrite. - `interaction-keyboard-navigation-route`: Interaction keyboard navigation route: launch `keyboard-navigation-lab` and inspect NodeCanvas arrow-key, viewport shortcut, and automation peer evidence. Supported seams live in `AsterGraph.Avalonia` canvas controls and `AsterGraph.Editor` command descriptor/shortcut contracts. Demo cookbook provides a spatial fixture and screenshot-gate coverage only; Demo does not add a separate input model or accessibility framework. - `interaction-host-event-inspector-route`: Interaction host event inspector route: launch `host-event-inspector` and inspect IGraphEditorEvents plus mutation batching evidence. Supported seams live in `AsterGraph.Editor` session event contracts and mutation batching internals. Demo cookbook provides a host-observable fixture and screenshot-gate coverage only; Demo does not add telemetry, remote sync, or a new event broker. - `lifecycle-workspace-save-restore-route`: Lifecycle workspace save/restore route: launch `workspace-save-restore` and inspect SaveWorkspace, LoadWorkspace, and workspace diagnostics evidence. Supported seams live in `AsterGraph.Editor` workspace command contracts, command descriptors, and diagnostics surfaces. Demo cookbook provides a save/restore fixture and screenshot-gate coverage only; Demo does not add a persistence engine or storage policy. @@ -99,6 +101,7 @@ Category-derived route posture 来自 `DemoCookbookRecipeCategory` 和 `Recipe.I - `builtin-edge-toolbar-route`: EdgeToolbar coverage is limited to the public EdgeToolbar control, action projection, and screenshot fixture; it does not add connection-local command logic, telemetry, or workflow automation. - `builtin-node-resizer-route`: NodeResizer coverage is limited to the public NodeResizer control, TrySetNodeSize, and screenshot fixture; it does not add an alternate resize engine, layout runtime, or workflow automation. - `interaction-selection-marquee-route`: Selection marquee fixture coverage is limited to existing selection query, command, and overlay contracts; it does not add a spatial index, alternate selection model, or custom hit-test runtime. +- `interaction-lasso-screenshot-proof-route`: Lasso screenshot proof coverage is limited to existing lasso selection activation, transient overlay feedback, Cookbook scene capture, and shell visual metadata; the explicit exclusions remain no toolbar UX, no eraser behavior, no drawing primitives, no persistence, no strict pixel baselines, no retained API removal, and no full whiteboard parity. - `interaction-keyboard-navigation-route`: Keyboard navigation fixture coverage is limited to existing Avalonia canvas controls, automation peers, and command shortcut contracts; it does not add a custom input framework or full accessibility provider suite. - `interaction-host-event-inspector-route`: Host event fixture coverage is bounded to existing session event contracts and mutation batching; it does not add telemetry, remote sync, time-based throttling, or a separate event broker. - `lifecycle-workspace-save-restore-route`: Workspace lifecycle fixture coverage is limited to existing save/load commands, command descriptors, and diagnostics; it does not add a persistence engine, cloud sync, storage policy, or migration runtime. @@ -139,6 +142,7 @@ v0.79 catalog 的 route boundary 字符串: - `GraphOperations`: graph 创建、command、overlay 或 review 路线都回链到现有代码 / Demo 锚点。 - `GraphOperations`: built-in screenshot fixtures 使用 `minimap-workbench`、`background-grid-density`、`hosted-controls-panel`、`keyboard-navigation-lab`、`host-event-inspector`、`selection-marquee-workbench`、`clipboard-fragment-roundtrip` 和 `validation-prevent-cycle`,确保 Cookbook 路线捕获的是不同 graph document,而不是复用同一个场景。 - `GraphOperations`: interaction screenshot fixtures 使用 `selection-marquee-workbench`、`keyboard-navigation-lab` 和 `host-event-inspector`,确保 selection、keyboard 和 event 路线捕获不同 graph document。 +- `GraphOperations`: Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 在 `selection-marquee-workbench` 上增加 `cookbook-interaction-lasso-screenshot-proof`,作为有界 lasso screenshot proof route,而不是新的 graph fixture family。 - `GraphOperations`: lifecycle screenshot fixtures 使用 `workspace-save-restore`、`clipboard-fragment-roundtrip` 和 `validation-prevent-cycle`,确保 save/load、clipboard 和 validation helper 路线捕获不同 graph document。 - `GraphOperations`: `LAYOUT_PROVIDER_EVIDENCE_EXPANSION` 表示 layout evidence 同步且由宿主拥有,入口是 `UseLayoutProvider(...)` / `IGraphLayoutProvider.CreateLayoutPlan(GraphLayoutRequest)`。`GraphLayoutRequest`、`GraphLayoutPlan`、`PreviewLayoutPlan`、`TryApplyLayoutPlan`、`TryApplyLayoutRequest`、`TrySnapSelectedNodesToGrid` 和 `TrySnapAllNodesToGrid` 继续走 `IGraphEditorSession` commands,并由 `GraphEditorLayoutProviderSeamTests`、`LAYOUT_PROVIDER_SEAM_OK`、`LAYOUT_PREVIEW_APPLY_CANCEL_OK` 和 `LAYOUT_UNDO_TRANSACTION_OK` 验证。这个 proof 不表示 layout execution 已变成异步或可取消。 - `NodeMetadata`: metadata 与 trust evidence 只锚定到 recipe 代码或 proof marker。 @@ -147,7 +151,7 @@ v0.79 catalog 的 route boundary 字符串: - `ValidationRuntimeOverlay`: validation、runtime overlay、support bundle、repair/help 和 designer outline evidence 都有表达,但不启用 workflow engine;outline state 继续由 `GetNavigatorOutlineSnapshot` 查询拥有,并由 `DESIGNER_WORKBENCH_AUTHORING_OK` 标记证明。Built-in recipe 增加 `ToMiniMapBudgetMarker`、`CalculateVisibleLineMetrics_WithExtremeZoomSpacing_KeepsLineDensityBounded` 和 `RecoveryHint` 作为 route-specific validation cue。 - `SupportEvidence`: 每条路线的支持声明都绑定到本地 proof 或 docs evidence。 - `SupportEvidence`: built-in proof marker `BUILTIN_MINIMAP_WORKBENCH_OK`、`GRID_BACKGROUND_DENSITY_OK`、`HOSTED_CONTROLS_PANEL_COMPOSITION_OK`、`BUILTIN_STANDALONE_CONTROLS_OK`、`BUILTIN_STANDALONE_PANEL_OK`、`BUILTIN_NODE_TOOLBAR_OK`、`BUILTIN_EDGE_TOOLBAR_OK` 和 `BUILTIN_NODE_RESIZER_OK` 把这批 recipe 绑定到现有 workbench 与 standalone surfaces。 -- `SupportEvidence`: interaction proof marker `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`、`INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK` 和 `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` 把这批 recipe 绑定到现有 interaction 和 event surfaces。 +- `SupportEvidence`: interaction proof marker `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK`、`LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`、`INTERACTION_KEYBOARD_NAVIGATION_FIXTURE_OK` 和 `INTERACTION_HOST_EVENT_INSPECTOR_FIXTURE_OK` 把这批 recipe 绑定到现有 interaction 和 event surfaces。 - `SupportEvidence`: lifecycle proof marker `LIFECYCLE_WORKSPACE_SAVE_RESTORE_FIXTURE_OK`、`LIFECYCLE_CLIPBOARD_FRAGMENT_FIXTURE_OK`、`LIFECYCLE_VALIDATION_HELPER_FIXTURE_OK`、`WORKSPACE_SAVE_LOAD_DIAGNOSTICS_OK`、`CLIPBOARD_FRAGMENT_ROUNDTRIP_OK` 和 `VALIDATION_HELPER_ROUTE_OK` 把这批 recipe 绑定到现有 lifecycle surfaces。 - `HostCodeExample`: 可复制的宿主示例仍锚定到 starter 或 consumer sample 代码。 @@ -164,6 +168,7 @@ v0.79 catalog 的 route boundary 字符串: - `Selection`: built-in Background/Grid 和 hosted Controls/Panel recipe 复用 `TrySnapSelectedNodesToGrid` 与 `AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`。 - `Selection`: marquee selection 通过 modifier key 支持 union 和 toggle 模式(`UpdateMarqueeSelection`);arrow-key nudge 在按住 shift 时移动更大步长(`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`)。 - `Selection`: 可运行 interaction fixture 让 rectangle selection(`selection-marquee-workbench`)、arrow-key movement(`ArrowKey_Nudge_MovesSelectedNodesWhenNodesAreSelected`)和 event cadence(`SessionEvents_SelectionChanges_AreThrottledToBoundedCadence`)保持 source-backed。 +- `Selection`: Phase 536 通过 `NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`、`shell-cookbook-lasso-screenshot-proof` 和 `full-window-shell-lasso-state` 让 lasso screenshot proof 保持 source-backed;边界明确排除 no toolbar UX、no eraser、no drawing primitives、no persistence 和 no full whiteboard parity。 - `Connection`: interaction fixture 通过 `INTERACTION_SELECTION_MARQUEE_FIXTURE_OK` 和 `IGraphEditorEvents` 保持 selected-route 与 mutation effects 可检查。 - `Connection`: lifecycle fixture 通过 `SessionCommands_RoundTripFragmentWorkspaceAndTemplateLibrary` 与 `validation.connection.route.reset` 保持 fragment connection remapping 和 invalid route repair 可检查。 - `Inspection`: hosted entry points、minimap cadence、trust decisions、support logs 和 proof panels 都保持可检查(`BuildAvaloniaApp`、`ToMiniMapBudgetMarker`、`PluginTrustDecision`、`RuntimeLogs`、`DemoHostMenuGroups.Proof`)。 @@ -171,6 +176,7 @@ v0.79 catalog 的 route boundary 字符串: - `Inspection`: standalone built-in recipe 通过 `AsterGraphControls`、`AsterGraphPanel`、`NodeToolbar`、`EdgeToolbar`、`NodeResizer`、`AsterGraphPanelPosition`、`NodeToolbar_RendersCanonicalNodeActionsAndExecutesDuplicateCommand`、`EdgeToolbar_RendersCanonicalEdgeActionsAndExecutesDisconnectCommand` 和 `NodeResizer_RendersStableHandlesAndCommitsResizeThroughSessionCommand` 保持可检查。 - `Inspection`: automation peers 向 accessibility tools 暴露节点标题和画布组结构(`DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers`)。 - `Inspection`: interaction fixture 把 `GetSelectionRectangleSnapshot`、`keyboard-navigation-lab`、`host-event-inspector` 和 `DefaultChromeMode_ExposesCanvasAndNodeAutomationPeers` 作为 screenshot-backed cues。 +- `Inspection`: lasso proof artifacts 把 `PART_NodeCanvas`、`shell-cookbook-lasso-screenshot-proof` 和 `full-window-shell-lasso-state` 作为 active transient lasso path 的 screenshot-backed cues。 - `Inspection`: lifecycle fixture 把 `workspace.save`、`workspace.save.succeeded`、`workspace.load.succeeded`、`astergraph.clipboard/v1`、`TryFocusValidationIssue` 和 `validation-prevent-cycle` 作为 screenshot-backed cues。 - `ValidationRuntimeFeedback`: extension、runtime、review 和 projection feedback 都保持本地、source-backed(`PluginTrust`、`RuntimeDiagnosticEntry`、`ValidationFeedback`、`MINIMAP_LIGHTWEIGHT_PROJECTION_OK`)。 - `ValidationRuntimeFeedback`: built-in route 继续通过 `ToMiniMapBudgetMarker`、`AuthoringToolsChrome_ProjectsStockSelectionLayoutActions`、`RecoveryHint`、`CreateCommandActions`、`CreateNodeActions`、`CreateConnectionActions` 和 `TrySetNodeSize` 保持 source-backed。 @@ -198,13 +204,13 @@ dotnet test tests/AsterGraph.Demo.Tests/AsterGraph.Demo.Tests.csproj --configura `DemoCookbookScreenshotGateTests` 会读取 `tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json`,通过 canonical scene PNG exporter 捕获每条路线,并把 PNG 与 metadata 写到 `artifacts/test-results/cookbook-screenshot-gate`。这个 scene gate 只证明 graph document、viewport、node/edge rendering 和 route metadata;它不捕获 Demo window chrome、host drawer、左侧 Cookbook navigation、overlays 或 shell panels。它的 metadata 会记录 `PngSha256`,并新增 Phase 512 `DriftMeasurement` object:`Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`,用于跨 host 比较 generated artifacts,但不引入 strict pixel hash pass/fail。后续要增加 Cookbook 路线时,只追加 manifest 行;常规路线覆盖不要修改 test internals。 -同一个 test class 还包含 manifest-driven full-window shell visual gate。`tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` 当前从 default `starter-host-route` / `ai-pipeline` route 捕获 `shell-cookbook-default-open`、`shell-cookbook-default-open-zh-cn`、`shell-cookbook-default-closed`、`shell-cookbook-default-closed-zh-cn`、`shell-runtime-diagnostics-open`、`shell-runtime-diagnostics-open-zh-cn`、`shell-runtime-diagnostics-closed`、有界的 Phase 508 flyout state:`shell-cookbook-default-view-menu-flyout`、有界的 Phase 509 tooltip popup state:`shell-cookbook-default-host-command-tooltip-popup`,以及有界的 Phase 510 context-menu state:`shell-cookbook-default-canvas-context-menu`。每个 shell state 都声明自己的 language/theme metadata,所以 gate 现在覆盖 English Cookbook drawer、Chinese Cookbook drawer、English 和 Chinese Cookbook closed-drawer shell、English 和 Chinese runtime diagnostics drawer、selected English runtime diagnostics closed-shell state、一条 English View menu flyout、一条 English disabled host-command tooltip popup,以及一条 English canvas context menu,且不改变 route scene fixture。它会用 Avalonia headless Skia rendering 打开 Demo、切到每个 shell state、捕获 full-window PNG,并把 PNG 与 metadata 写到 `artifacts/test-results/cookbook-shell-visual-gate`。metadata 会为基础 shell states 记录 `CaptureScope=full-window-shell-state`,为 flyout row 记录 `CaptureScope=full-window-shell-flyout-state`,为 tooltip popup row 记录 `CaptureScope=full-window-shell-popup-state`,为 context-menu row 记录 `CaptureScope=full-window-shell-context-menu-state`,并记录 requested/actual pixel dimensions、选中的 route 和 recipe、选中的 language/theme、覆盖到的 shell parts、可选 flyout menu part、covered flyout headers、可选 popup target part、covered popup text、可选 context-menu target part、covered context-menu headers、non-transparent pixel count、distinct color count、`PngSha256`,以及和 scene gate 相同的 record-only `DriftMeasurement` host metadata。 +同一个 test class 还包含 manifest-driven full-window shell visual gate。`tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json` 当前捕获 `shell-cookbook-default-open`、`shell-cookbook-default-open-zh-cn`、`shell-cookbook-default-closed`、`shell-cookbook-default-closed-zh-cn`、`shell-runtime-diagnostics-open`、`shell-runtime-diagnostics-open-zh-cn`、`shell-runtime-diagnostics-closed`、有界的 Phase 508 flyout state:`shell-cookbook-default-view-menu-flyout`、有界的 Phase 509 tooltip popup state:`shell-cookbook-default-host-command-tooltip-popup`、有界的 Phase 510 context-menu state:`shell-cookbook-default-canvas-context-menu`,以及有界的 Phase 536 lasso state:`shell-cookbook-lasso-screenshot-proof`。每个 shell state 都声明自己的 language/theme metadata,所以 gate 现在覆盖 English Cookbook drawer、Chinese Cookbook drawer、English 和 Chinese Cookbook closed-drawer shell、English 和 Chinese runtime diagnostics drawer、selected English runtime diagnostics closed-shell state、一条 English View menu flyout、一条 English disabled host-command tooltip popup、一条 English canvas context menu,以及一条 English active lasso overlay capture。它会用 Avalonia headless Skia rendering 打开 Demo、切到每个 shell state、捕获 full-window PNG,并把 PNG 与 metadata 写到 `artifacts/test-results/cookbook-shell-visual-gate`。metadata 会为基础 shell states 记录 `CaptureScope=full-window-shell-state`,为 flyout row 记录 `CaptureScope=full-window-shell-flyout-state`,为 tooltip popup row 记录 `CaptureScope=full-window-shell-popup-state`,为 context-menu row 记录 `CaptureScope=full-window-shell-context-menu-state`,为 lasso row 记录 `CaptureScope=full-window-shell-lasso-state`,并记录 requested/actual pixel dimensions、选中的 route 和 recipe、选中的 language/theme、覆盖到的 shell parts、可选 flyout menu part、covered flyout headers、可选 popup target part、covered popup text、可选 context-menu target part、covered context-menu headers、可选 lasso target part、`NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`、non-transparent pixel count、distinct color count、`PngSha256`,以及和 scene gate 相同的 record-only `DriftMeasurement` host metadata。 `shell-cookbook-default-open` 和 `shell-cookbook-default-open-zh-cn` 覆盖 `PART_HostMenu`、`PART_HostShellSplitView`、`PART_CookbookWorkspaceNavigationPanel`、`PART_MainGraphEditorHost` 和 `PART_CookbookWorkspaceRecipeContentPanel`。`shell-cookbook-default-view-menu-flyout` 还会打开 `PART_ViewMenu`,写出 `shell-cookbook-default-view-menu-flyout.png`,并要求 flyout headers:`Show header`、`Show library`、`Show inspector`、`Show status bar`、`Show mini map` 和 `Open view controls`。`shell-cookbook-default-host-command-tooltip-popup` 会打开 `PART_HostCommand_history.undo` 上的 disabled undo command tooltip,写出 `shell-cookbook-default-host-command-tooltip-popup.png`,记录 `full-window-shell-popup-state`,并要求 popup text:`Nothing to undo yet.`。`shell-cookbook-default-canvas-context-menu` 会打开 `PART_NodeCanvas` 上的 default canvas context menu,写出 `shell-cookbook-default-canvas-context-menu.png`,记录 `full-window-shell-context-menu-state`,并要求 context-menu headers:`Add Node`、`Fit View` 和 `Reset View`。`shell-cookbook-default-closed`、`shell-cookbook-default-closed-zh-cn` 和 `shell-runtime-diagnostics-closed` 只把 required parts 限定为 closed-shell chrome:`PART_HostMenu`、`PART_HostShellSplitView` 和 `PART_MainGraphEditorHost`。`shell-runtime-diagnostics-open` 和 `shell-runtime-diagnostics-open-zh-cn` 覆盖 runtime drawer 中的 `PART_RuntimeSummarySection`、`PART_RuntimeInspectionSection`、`PART_RuntimeDiagnosticsSection` 和 `PART_MainGraphEditorHost`。Phase 511 / GitHub #142 / `avalonia-node-map-9rq` 只新增两条有界 `zh-CN` + `canonical-dark` shell-state rows:`shell-cookbook-default-closed-zh-cn` 和 `shell-runtime-diagnostics-open-zh-cn`。所有 state 都会验证预期 drawer open/closed 状态、最小渲染尺寸、nonblank pixels、distinct colors、output path、metadata source、声明的 language/theme 和 required named shell parts。 -第一条 gate 路线是 `starter-host-route` + `ai-pipeline` scenario,记录 `1480x900` viewport metadata、English UI text 和 `canonical-dark` theme。Built-in batch 还加入 `builtin-minimap-workbench-route` / `minimap-workbench`、`builtin-background-grid-route` / `background-grid-density`、`builtin-hosted-controls-route` / `hosted-controls-panel`、`builtin-standalone-controls-route` / `keyboard-navigation-lab`、`builtin-standalone-panel-route` / `host-event-inspector`、`builtin-node-toolbar-route` / `selection-marquee-workbench`、`builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip` 和 `builtin-node-resizer-route` / `validation-prevent-cycle`。Interaction batch 加入 `interaction-selection-marquee-route` / `selection-marquee-workbench`、`interaction-keyboard-navigation-route` / `keyboard-navigation-lab` 和 `interaction-host-event-inspector-route` / `host-event-inspector`。Lifecycle batch 加入 `lifecycle-workspace-save-restore-route` / `workspace-save-restore`、`lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip` 和 `lifecycle-validation-helper-route` / `validation-prevent-cycle`;每个 manifest row 都记录 `expectedDocumentTitle`、`minimumNodeCount`、`minimumConnectionCount` 和 `requiredNodeIds`,确保 gate 证明这些是不同的 scene fixture。视觉 PR 需要附上生成的 before/after PNG 和 `metadata.json`。 +第一条 gate 路线是 `starter-host-route` + `ai-pipeline` scenario,记录 `1480x900` viewport metadata、English UI text 和 `canonical-dark` theme。Built-in batch 还加入 `builtin-minimap-workbench-route` / `minimap-workbench`、`builtin-background-grid-route` / `background-grid-density`、`builtin-hosted-controls-route` / `hosted-controls-panel`、`builtin-standalone-controls-route` / `keyboard-navigation-lab`、`builtin-standalone-panel-route` / `host-event-inspector`、`builtin-node-toolbar-route` / `selection-marquee-workbench`、`builtin-edge-toolbar-route` / `clipboard-fragment-roundtrip` 和 `builtin-node-resizer-route` / `validation-prevent-cycle`。Interaction batch 加入 `interaction-selection-marquee-route` / `selection-marquee-workbench`、`interaction-lasso-screenshot-proof-route` / `selection-marquee-workbench`、`interaction-keyboard-navigation-route` / `keyboard-navigation-lab` 和 `interaction-host-event-inspector-route` / `host-event-inspector`。Lifecycle batch 加入 `lifecycle-workspace-save-restore-route` / `workspace-save-restore`、`lifecycle-clipboard-fragment-route` / `clipboard-fragment-roundtrip` 和 `lifecycle-validation-helper-route` / `validation-prevent-cycle`;每个 manifest row 都记录 `expectedDocumentTitle`、`minimumNodeCount`、`minimumConnectionCount` 和 `requiredNodeIds`,确保 gate 证明这些是不同的 scene fixture。Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 只新增 lasso screenshot proof rows:`cookbook-interaction-lasso-screenshot-proof` 和 `shell-cookbook-lasso-screenshot-proof`;边界明确排除 no strict pixel baseline、no toolbar UX、no eraser、no drawing primitives、no persistence、no renderer rewrite、no retained API removal 和 no full whiteboard parity。视觉 PR 需要附上生成的 before/after PNG 和 `metadata.json`。 -CI 姿态:scene PNG gate 和 full-window shell visual gate 都已经接入 `AsterGraph.Demo.Tests`,所以正常 net9 validation lane 会执行它们。scene gate 验证确定性 artifact 生成、PNG 有效性、route metadata 和最小图片不变量;shell gate 还会在 full-window artifact 缺失、空白、尺寸过小、drawer state 错误、缺少预期 language/theme metadata、未覆盖预期 shell parts、Phase 508 flyout row 无法打开 `PART_ViewMenu` 并记录 required headers、Phase 509 popup row 无法打开 `PART_HostCommand_history.undo` 并记录 `Nothing to undo yet.`、Phase 510 context-menu row 无法打开 `PART_NodeCanvas` 并记录 `Add Node`、`Fit View` 和 `Reset View`,或 Phase 511 rows 无法为 closed Cookbook shell 和 runtime diagnostics drawer 记录 `zh-CN` + `canonical-dark` metadata 时失败。Phase 512 / GitHub #143 / `avalonia-node-map-1j4` 把两个 gate 保持为 record-only pixel-baseline drift measurement:generated metadata 记录 `PngSha256`、`DriftMeasurement.Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`,并明确 no strict pixel baseline enforcement。超出这条 canvas row 的 broad context-menu coverage、超出这条 tooltip popup 的广泛 popup coverage、超出列明 shell states 的广泛 language/theme variants、visual redesign、runtime behavior changes、public API changes、retained API removal 和 strict pixel baselines 仍不属于这个 gate;Phase 508 / GitHub #139 / `avalonia-node-map-2nu` 只新增一条有界的 View menu flyout manifest state,Phase 509 / GitHub #140 / `avalonia-node-map-0ff` 只新增一条有界的 host-command tooltip popup manifest state,Phase 510 / GitHub #141 / `avalonia-node-map-8lu` 只新增一条有界的 canvas context-menu manifest state,Phase 511 / GitHub #142 / `avalonia-node-map-9rq` 只新增两条有界的 `zh-CN` + `canonical-dark` shell-state rows。 +CI 姿态:scene PNG gate 和 full-window shell visual gate 都已经接入 `AsterGraph.Demo.Tests`,所以正常 net9 validation lane 会执行它们。scene gate 验证确定性 artifact 生成、PNG 有效性、route metadata 和最小图片不变量;shell gate 还会在 full-window artifact 缺失、空白、尺寸过小、drawer state 错误、缺少预期 language/theme metadata、未覆盖预期 shell parts、Phase 508 flyout row 无法打开 `PART_ViewMenu` 并记录 required headers、Phase 509 popup row 无法打开 `PART_HostCommand_history.undo` 并记录 `Nothing to undo yet.`、Phase 510 context-menu row 无法打开 `PART_NodeCanvas` 并记录 `Add Node`、`Fit View` 和 `Reset View`、Phase 511 rows 无法为 closed Cookbook shell 和 runtime diagnostics drawer 记录 `zh-CN` + `canonical-dark` metadata,或 Phase 536 lasso row 无法打开 `PART_NodeCanvas`、设置 `NodeCanvasSelectionMode.Lasso`、记录 `full-window-shell-lasso-state`,并在 capture 时保持 `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` transient path 可见时失败。Phase 512 / GitHub #143 / `avalonia-node-map-1j4` 把两个 gate 保持为 record-only pixel-baseline drift measurement:generated metadata 记录 `PngSha256`、`DriftMeasurement.Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`,并明确 no strict pixel baseline enforcement。超出这条 canvas row 的 broad context-menu coverage、超出这条 tooltip popup 的广泛 popup coverage、超出列明 shell states 的广泛 language/theme variants、visual redesign、runtime behavior changes、public API changes、retained API removal 和 strict pixel baselines 仍不属于这个 gate;Phase 508 / GitHub #139 / `avalonia-node-map-2nu` 只新增一条有界的 View menu flyout manifest state,Phase 509 / GitHub #140 / `avalonia-node-map-0ff` 只新增一条有界的 host-command tooltip popup manifest state,Phase 510 / GitHub #141 / `avalonia-node-map-8lu` 只新增一条有界的 canvas context-menu manifest state,Phase 511 / GitHub #142 / `avalonia-node-map-9rq` 只新增两条有界的 `zh-CN` + `canonical-dark` shell-state rows,Phase 536 / GitHub #195 / `avalonia-node-map-uvd` 只新增一条有界 lasso screenshot state。 Phase 515 / GitHub #151 / `avalonia-node-map-t44` 基于 Phase 512 evidence 决定 strict pixel-baseline policy:strict single-hash enforcement 继续 deferred。已审查的证据是 two successful Phase 512 release-validation artifact sets,每组都在 `artifacts/test-results` 下包含 25 metadata.json files;每个 generated scene 和 shell artifact 都记录 `PngSha256`、`DriftMeasurement`、`Policy=record-only`、`PngHashPurpose=drift-evidence`、`StrictPixelBaselineEnforced=false`、`HostRuntimeDescription`、`OsDescription` 和 `ProcessArchitecture`。其中一条同路径 full-window shell popup artifact,`shell-cookbook-default-host-command-tooltip-popup`,在两次成功 Windows release-validation run 中产生了不同的 `PngSha256`,但 metadata 仍保持 record-only。这足以继续防守当前不变量,但不足以引入脆弱的 single-hash pass/fail gate。后续 pass/fail policy 的触发条件是:同一 route/state 集合在 supported CI hosts 上取得 repeatable no-drift evidence,或者先审查并落地一个明确的 host-keyed or tolerant comparator 和阈值。 diff --git a/docs/zh-CN/phase-0-reactflow-parity-audit.md b/docs/zh-CN/phase-0-reactflow-parity-audit.md index 4aac93de..bd123848 100644 --- a/docs/zh-CN/phase-0-reactflow-parity-audit.md +++ b/docs/zh-CN/phase-0-reactflow-parity-audit.md @@ -186,6 +186,10 @@ Phase 534 是 GitHub #191 / `avalonia-node-map-lzy`,承接 public lasso pointe Phase 535 是 GitHub #193 / `avalonia-node-map-8l6`,承接 Phase 534 关闭后的 post-Phase-534 parity queue refresh。本 slice 只修改 docs/tests:记录 Phase 534 已关闭,并把剩余 whiteboard 压力拆成 tracker-backed follow-up candidates,包括 lasso screenshot proof、lasso toolbar UX/public activation ergonomics、eraser behavior/API feasibility、rectangle/freehand drawing primitives,以及 whiteboard persistence/render-layer readiness。不授权 no runtime behavior changes、no public API changes、no UI redesign、no screenshot manifest expansion、no strict pixel baseline enforcement、no retained API removal 或 no whiteboard implementation。 +## Phase 536 更新 + +Phase 536 是 GitHub #195 / `avalonia-node-map-uvd`,承接 lasso screenshot route and Cookbook proof boundary。本实现 slice 新增 scene route `cookbook-interaction-lasso-screenshot-proof` / `interaction-lasso-screenshot-proof-route`,复用现有 `selection-marquee-workbench` fixture,并新增 shell state `shell-cookbook-lasso-screenshot-proof`。该 shell state 目标为 `PART_NodeCanvas`,设置 `NodeCanvasSelectionMode.Lasso`,在 release 前捕获 active transient lasso path,并记录 `full-window-shell-lasso-state` metadata;证据绑定到 `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag` 与 proof marker `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`。它不授权 no toolbar UX、no eraser behavior、no drawing primitives、no persistence、no renderer rewrite、no strict pixel baseline enforcement、no retained API removal 或 no full whiteboard parity。 + ## Phase 489 更新 Phase 489 通过 PR #102 关闭 GitHub #101 / `avalonia-node-map-6sc`,完成 `perf/renderer-virtualization-spike` 分支上的 renderer virtualization design spike。本 slice 只做 docs/tests:先定义未来声明 ItemsRepeater/Skia-style renderer virtualization、background graph index 或扩大 graph-size claim 前必须满足的 proof contract。不做 public API change,也不做 runtime change。当前证据仍只支持 viewport-budgeted scene projection/rendering,不是真正的 renderer virtualization contract;`xlarge` 继续保持 telemetry-only。 @@ -489,10 +493,12 @@ Phase 533 通过 GitHub #189 / `avalonia-node-map-cxe` 新增 public lasso point Phase 534 通过 GitHub #191 / `avalonia-node-map-lzy` 新增 lasso visual gesture feedback route。它在 active lasso drag 期间通过 `UpdateLassoFeedback(...)` 把 transient lasso path 渲染到现有 `OverlayLayer`,并在 release 或 capture loss 时通过 `ClearLassoFeedback()` 清理,同时继续把 toolbar UX、lasso screenshot route、eraser tool behavior、drawing primitives、persistence、renderer layer、React Flow-like examples 和 full React Flow whiteboard parity 保留为 gaps。 +Phase 536 通过 GitHub #195 / `avalonia-node-map-uvd` 新增 bounded lasso screenshot proof。它加入 `cookbook-interaction-lasso-screenshot-proof`、`interaction-lasso-screenshot-proof-route` 和 `shell-cookbook-lasso-screenshot-proof`,作为 existing transient lasso path 的 visual evidence,同时继续把 toolbar UX、eraser behavior、drawing primitives、persistence、renderer rewrite、strict pixel baseline enforcement、retained API removal 和 full whiteboard parity 保留为 gaps。 + | GitHub | Bead | 标题 | 优先级 | 可能 write set | 并行边界 | | --- | --- | --- | --- | --- | --- | | #193 | `avalonia-node-map-8l6` | Phase 535: refresh post-lasso visual feedback parity queue | P2 | parity roadmap docs 和 focused docs tests | Current docs/test queue refresh。它用 tracker-backed follow-ups 替换 stale Phase 534 current row,因此会阻塞下一批 implementation wave。 | -| TBD | TBD | Phase 536: lasso screenshot route and Cookbook proof boundary | P2 | Cookbook screenshot manifest、Demo fixture route、scene/shell screenshot tests 和 parity docs | Phase 535 后 ready。与 lasso toolbar work 顺序推进,因为两者可能触碰同一 lasso route copy 与 visual proof fixture。 | +| #195 | `avalonia-node-map-uvd` | Phase 536: lasso screenshot route and Cookbook proof boundary | P2 | Cookbook screenshot manifest、Demo fixture route、scene/shell screenshot tests 和 parity docs | Current visual proof slice。它会先稳定 screenshot route 与 Cookbook proof boundary,再进入 lasso toolbar work。 | | TBD | TBD | Phase 537: lasso toolbar UX and public activation ergonomics boundary | P2 | hosted authoring tools、`NodeCanvas.SelectionMode` activation surface、Demo/Cookbook route 和 editor/Avalonia tests | Phase 535 后 ready。如果 Phase 536 也需要相同 lasso route text 或 visual proof fixture,不要并行。 | | TBD | TBD | Phase 538: eraser behavior/API feasibility gate | P3 | editor selection/delete commands、Avalonia hit-testing route、parity docs 和 focused feasibility tests | Phase 535 后可与 drawing model planning 并行,只要不共享 pointer-mode state edits。 | | TBD | TBD | Phase 539: rectangle/freehand drawing primitive model gate | P3 | Core/Editor model contract docs/tests 和 whiteboard primitive API inventory | Phase 535 后可与 eraser feasibility 并行,只要保持 docs/model-only 且不触碰 Avalonia pointer coordinators。 | @@ -533,7 +539,7 @@ Phase 534 通过 GitHub #191 / `avalonia-node-map-lzy` 新增 lasso visual gestu - `docs/phase-527-background-variant-gate`:负责 #177 / `avalonia-node-map-dim`;当前 docs/test worktree,用于 Background variant public surface gate。 - `docs/phase-528-panel-overlay-boundary`:负责 #179 / `avalonia-node-map-9ow`;当前 docs/test worktree,用于 Panel versus viewport-attached overlay boundary。 - `docs/phase-535-post-lasso-queue-refresh`:负责 #193 / `avalonia-node-map-8l6`;当前 docs/test queue refresh,承接 transient lasso feedback 后续拆分,不做 runtime/API/UI/screenshot-manifest/whiteboard implementation changes。 -- `visual/phase-536-lasso-screenshot-proof`:future candidate,用于 Phase 535 后第一条 lasso screenshot route 和 Cookbook proof boundary。 +- `visual/phase-536-lasso-screenshot-proof`:负责 #195 / `avalonia-node-map-uvd`;当前 slice,用于 `cookbook-interaction-lasso-screenshot-proof`、`shell-cookbook-lasso-screenshot-proof` 和 bounded lasso screenshot proof boundary。 - `feature/phase-537-lasso-toolbar-ergonomics`:future candidate,用于 Phase 535 后的 lasso toolbar UX 和 public activation ergonomics。 - `feature/phase-538-eraser-feasibility`:future candidate,用于 Phase 535 后的 eraser behavior/API feasibility。 - `docs/phase-539-drawing-primitive-model-gate`:future candidate,用于 Phase 535 后的 rectangle/freehand drawing primitive model decisions。 @@ -591,4 +597,5 @@ Phase 534 通过 GitHub #191 / `avalonia-node-map-lzy` 新增 lasso visual gestu - Phase 530 是 GitHub #183 / `avalonia-node-map-8um`;它新增 backend/editor lasso selection query contract,不做 Avalonia gesture capture、pointer-mode state machine、eraser behavior、drawing primitives、persistence、renderer changes、screenshot manifest expansion、strict visual-baseline enforcement、retained API removal 或 full React Flow whiteboard parity claim。 - Phase 534 是 GitHub #191 / `avalonia-node-map-lzy`;它新增 transient lasso visual gesture feedback,不做 toolbar UX、lasso screenshot route、eraser behavior、drawing primitives、persistence、renderer changes、screenshot manifest expansion、strict visual-baseline enforcement、retained API removal 或 full React Flow whiteboard parity claim。 - Phase 535 是 GitHub #193 / `avalonia-node-map-8l6`;它刷新 post-Phase-534 parity queue,不做 runtime behavior changes、public API changes、UI redesign、screenshot manifest expansion、strict pixel baseline enforcement、retained API removal 或 whiteboard implementation。 +- Phase 536 是 GitHub #195 / `avalonia-node-map-uvd`;它新增 `cookbook-interaction-lasso-screenshot-proof`、`interaction-lasso-screenshot-proof-route`、`shell-cookbook-lasso-screenshot-proof`、`full-window-shell-lasso-state` 和 `LASSO_SCREENSHOT_PROOF_BOUNDARY_OK`,不做 toolbar UX、eraser behavior、drawing primitives、persistence、renderer rewrite、strict pixel baseline enforcement、retained API removal 或 full whiteboard parity。 - Phase 478、Phase 484、Phase 490、Phase 491、Phase 492、Phase 493、Phase 494、Phase 495、Phase 497、Phase 498、Phase 499、Phase 500、Phase 501、Phase 502、Phase 503、Phase 504、Phase 505、Phase 506、Phase 507、Phase 508、Phase 509、Phase 510、Phase 511、Phase 512、Phase 513、Phase 520、Phase 521、Phase 522、Phase 523、Phase 524、Phase 525、Phase 526、Phase 527、Phase 528、Phase 529 和 Phase 535 都不修改产品代码;除非 focused test 证明存在具体 missing contract。 diff --git a/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs b/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs index 6728584f..d639fc98 100644 --- a/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs +++ b/src/AsterGraph.Demo/Cookbook/DemoCookbookCatalog.Recipes.cs @@ -1277,6 +1277,91 @@ public static partial class DemoCookbookCatalog editor.Session.Commands.SelectNone(); """ ), + new DemoCookbookRecipe( + "interaction-lasso-screenshot-proof-route", + DemoCookbookRecipeCategory.Authoring, + "Interaction lasso screenshot proof route", + "Capture a bounded Cookbook and shell visual proof for the existing lasso transient feedback path.", + [ + new DemoCookbookAnchor( + "Public lasso selection mode", + "src/AsterGraph.Avalonia/Controls/NodeCanvasSelectionMode.cs", + "Lasso"), + new DemoCookbookAnchor( + "Lasso feedback update", + "src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs", + "UpdateLassoFeedback"), + new DemoCookbookAnchor( + "Lasso feedback cleanup", + "src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs", + "ClearLassoFeedback"), + ], + [ + new DemoCookbookAnchor( + "Selection marquee graph fixture", + "src/AsterGraph.Demo/DemoGraphFactory.cs", + "selection-marquee-workbench"), + new DemoCookbookAnchor( + "Transient lasso path proof", + "tests/AsterGraph.Editor.Tests/NodeCanvasStandaloneTests.cs", + "LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag"), + new DemoCookbookAnchor( + "Lasso shell visual gate state", + "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json", + "shell-cookbook-lasso-screenshot-proof"), + ], + [ + new DemoCookbookAnchor( + "Lasso screenshot proof cookbook docs", + "docs/en/demo-cookbook.md", + "LASSO_SCREENSHOT_PROOF_BOUNDARY_OK"), + ], + [ + new DemoCookbookScenarioPoint( + DemoCookbookScenarioKind.GraphOperations, + "The route reuses selection-marquee-workbench so lasso screenshot proof stays tied to a populated selection scene.", + "selection-marquee-workbench"), + new DemoCookbookScenarioPoint( + DemoCookbookScenarioKind.ValidationRuntimeOverlay, + "The shell visual gate opens PART_NodeCanvas and captures the transient lasso path while the drag is active.", + "shell-cookbook-lasso-screenshot-proof"), + new DemoCookbookScenarioPoint( + DemoCookbookScenarioKind.SupportEvidence, + "The proof remains anchored to the existing transient feedback test instead of introducing a whiteboard tool.", + "LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag"), + ], + [ + new DemoCookbookInteractionFacet( + DemoCookbookInteractionKind.Selection, + "NodeCanvasSelectionMode.Lasso routes pointer drag selection through the existing canvas surface.", + "NodeCanvasSelectionMode.Lasso"), + new DemoCookbookInteractionFacet( + DemoCookbookInteractionKind.LayoutReadability, + "The full-window shell capture keeps the lasso path visible with the Cookbook drawer and graph scene.", + "shell-cookbook-lasso-screenshot-proof"), + new DemoCookbookInteractionFacet( + DemoCookbookInteractionKind.Inspection, + "The generated metadata records PART_NodeCanvas and full-window-shell-lasso-state for evidence review.", + "shell-cookbook-lasso-screenshot-proof"), + ], + [ + "LASSO_SCREENSHOT_PROOF_BOUNDARY_OK", + "NodeCanvasSelectionMode.Lasso", + "LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag", + ], + new DemoCookbookRouteClarity( + "Interaction lasso screenshot proof route: launch `selection-marquee-workbench`, set `NodeCanvasSelectionMode.Lasso`, and capture the active transient lasso overlay.", + "Supported seams live in `AsterGraph.Avalonia` NodeCanvas lasso feedback and the existing `AsterGraph.Editor` selection contracts.", + "Demo cookbook provides a screenshot proof fixture and manifest state only; Demo does not add toolbar UX, eraser behavior, drawing primitives, persistence, or a renderer rewrite."), + "Lasso screenshot proof coverage is limited to existing lasso selection activation, transient overlay feedback, Cookbook scene capture, and shell visual metadata; the explicit exclusions remain no toolbar UX, no eraser behavior, no drawing primitives, no persistence, no strict pixel baselines, no retained API removal, and no full whiteboard parity.", + CodeSample: """ + // Activate the existing lasso selection mode before capturing proof. + var canvas = window.FindControl("PART_NodeCanvas"); + canvas.SelectionMode = NodeCanvasSelectionMode.Lasso; + + // Capture while the pointer drag is active so the transient lasso path is visible. + """ + ), new DemoCookbookRecipe( "interaction-keyboard-navigation-route", DemoCookbookRecipeCategory.Authoring, diff --git a/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json b/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json index e1143710..56b14ea5 100644 --- a/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json +++ b/tests/AsterGraph.Demo.Tests/CookbookScreenshotGateRoutes.json @@ -209,6 +209,27 @@ ], "outputFileName": "cookbook-interaction-selection-marquee.png" }, + { + "id": "cookbook-interaction-lasso-screenshot-proof", + "recipeId": "interaction-lasso-screenshot-proof-route", + "scenario": "selection-marquee-workbench", + "hostGroup": "cookbook", + "language": "en", + "theme": "canonical-dark", + "viewportWidth": 1680, + "viewportHeight": 940, + "backgroundHex": "#09141C", + "scale": 1.0, + "minimumBytes": 4096, + "expectedDocumentTitle": "Selection Rectangle Fixture", + "minimumNodeCount": 8, + "minimumConnectionCount": 10, + "requiredNodeIds": [ + "select-clock", + "select-output" + ], + "outputFileName": "cookbook-interaction-lasso-screenshot-proof.png" + }, { "id": "cookbook-interaction-keyboard-navigation", "recipeId": "interaction-keyboard-navigation-route", diff --git a/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json b/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json index 385af80e..482986c4 100644 --- a/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json +++ b/tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json @@ -102,6 +102,24 @@ ], "outputFileName": "shell-cookbook-default-canvas-context-menu.png" }, + { + "id": "shell-cookbook-lasso-screenshot-proof", + "routeId": "cookbook-interaction-lasso-screenshot-proof", + "hostGroup": "cookbook", + "language": "en", + "theme": "canonical-dark", + "expectedPaneOpen": true, + "lassoTargetPartName": "PART_NodeCanvas", + "requiredShellParts": [ + "PART_HostMenu", + "PART_HostShellSplitView", + "PART_CookbookWorkspaceNavigationPanel", + "PART_NodeCanvas", + "PART_MainGraphEditorHost", + "PART_CookbookWorkspaceRecipeContentPanel" + ], + "outputFileName": "shell-cookbook-lasso-screenshot-proof.png" + }, { "id": "shell-cookbook-default-closed", "routeId": "cookbook-default-starter-host-ai-pipeline", diff --git a/tests/AsterGraph.Demo.Tests/DemoCookbookLassoProofDocsTests.cs b/tests/AsterGraph.Demo.Tests/DemoCookbookLassoProofDocsTests.cs new file mode 100644 index 00000000..786b691f --- /dev/null +++ b/tests/AsterGraph.Demo.Tests/DemoCookbookLassoProofDocsTests.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using Xunit; + +namespace AsterGraph.Demo.Tests; + +public sealed class DemoCookbookLassoProofDocsTests +{ + [Fact] + public void DemoCookbookDocs_SurfaceLassoScreenshotProofBoundary() + { + foreach (var contents in new[] { ReadRepoFile("docs/en/demo-cookbook.md"), ReadRepoFile("docs/zh-CN/demo-cookbook.md") }) + { + Assert.Contains("interaction-lasso-screenshot-proof-route", contents, StringComparison.Ordinal); + Assert.Contains("cookbook-interaction-lasso-screenshot-proof", contents, StringComparison.Ordinal); + Assert.Contains("shell-cookbook-lasso-screenshot-proof", contents, StringComparison.Ordinal); + Assert.Contains("LASSO_SCREENSHOT_PROOF_BOUNDARY_OK", contents, StringComparison.Ordinal); + Assert.Contains("full-window-shell-lasso-state", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvasSelectionMode.Lasso", contents, StringComparison.Ordinal); + Assert.Contains("LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag", contents, StringComparison.Ordinal); + Assert.Contains("no toolbar UX", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no eraser", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no drawing primitives", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no persistence", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no full whiteboard parity", contents, StringComparison.OrdinalIgnoreCase); + } + } + + private static string ReadRepoFile(string relativePath) + => File.ReadAllText(Path.Combine(GetRepositoryRoot(), relativePath)); + + private static string GetRepositoryRoot() + { + var current = new DirectoryInfo(AppContext.BaseDirectory); + while (current is not null) + { + if (File.Exists(Path.Combine(current.FullName, "Directory.Build.props"))) + { + return current.FullName; + } + + current = current.Parent; + } + + throw new DirectoryNotFoundException("Failed to locate repository root from test base directory."); + } +} diff --git a/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs b/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs index 9fc703b0..57d35efa 100644 --- a/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs +++ b/tests/AsterGraph.Demo.Tests/DemoCookbookScreenshotGateTests.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text.Json; +using Avalonia; using AsterGraph.Abstractions.Styling; using AsterGraph.Avalonia.Controls; using AsterGraph.Avalonia.Menus; @@ -12,6 +13,7 @@ using Avalonia.Headless; using Avalonia.Headless.XUnit; using Avalonia.Input; +using Avalonia.Interactivity; using Avalonia.Threading; using Avalonia.VisualTree; using AsterGraph.Demo.Cookbook; @@ -262,6 +264,33 @@ public void CookbookScreenshotGate_IncludesInteractionFixtureBatchRoutes() && route.RequiredNodeIds.Contains("event-output", StringComparer.Ordinal)); } + [Fact] + public void CookbookScreenshotGate_IncludesPhase536LassoScreenshotProofRoute() + { + var routes = LoadRoutes(GetRepositoryRoot()); + var shellStates = LoadShellStates(GetRepositoryRoot()); + + var route = Assert.Single(routes, candidate => + string.Equals(candidate.Id, "cookbook-interaction-lasso-screenshot-proof", StringComparison.Ordinal)); + Assert.Equal("interaction-lasso-screenshot-proof-route", route.RecipeId); + Assert.Equal("selection-marquee-workbench", route.Scenario); + Assert.Equal("Selection Rectangle Fixture", route.ExpectedDocumentTitle); + Assert.Contains("select-output", route.RequiredNodeIds, StringComparer.Ordinal); + Assert.Equal("cookbook-interaction-lasso-screenshot-proof.png", route.OutputFileName); + + var shellState = Assert.Single(shellStates, state => + string.Equals(state.Id, "shell-cookbook-lasso-screenshot-proof", StringComparison.Ordinal)); + Assert.Equal(route.Id, shellState.RouteId); + Assert.Equal("cookbook", shellState.HostGroup); + Assert.Equal("en", shellState.Language); + Assert.Equal("canonical-dark", shellState.Theme); + Assert.True(shellState.ExpectedPaneOpen); + Assert.Equal("PART_NodeCanvas", shellState.LassoTargetPartName); + Assert.Contains("PART_NodeCanvas", shellState.RequiredShellParts, StringComparer.Ordinal); + Assert.Contains("PART_CookbookWorkspaceRecipeContentPanel", shellState.RequiredShellParts, StringComparer.Ordinal); + Assert.Equal("shell-cookbook-lasso-screenshot-proof.png", shellState.OutputFileName); + } + [Fact] public void CookbookScreenshotGate_IncludesLifecycleFixtureBatchRoutes() { @@ -307,7 +336,7 @@ public void CookbookScreenshotGate_IncludesSelectedRuntimeClosedShellState() ], shellState.RequiredShellParts); Assert.Equal("shell-runtime-diagnostics-closed.png", shellState.OutputFileName); - Assert.Equal(10, shellStates.Count); + Assert.Equal(11, shellStates.Count); } [Fact] @@ -534,6 +563,11 @@ private static void AssertShellStateReferencesRoute( Assert.NotEmpty(shellState.RequiredContextMenuHeaders); Assert.All(shellState.RequiredContextMenuHeaders, header => Assert.False(string.IsNullOrWhiteSpace(header))); } + + if (!string.IsNullOrWhiteSpace(shellState.LassoTargetPartName)) + { + Assert.StartsWith("PART_", shellState.LassoTargetPartName, StringComparison.Ordinal); + } } private static IReadOnlyList LoadRoutes(string repoRoot) @@ -619,6 +653,7 @@ private static void CaptureShellVisualState( var openedFlyout = OpenRequestedFlyout(window, shellState); var openedPopup = OpenRequestedPopup(window, shellState); var openedContextMenu = OpenRequestedContextMenu(window, shellState); + var capturedLassoTargetName = OpenRequestedLassoProof(window, shellState); using var frame = window.CaptureRenderedFrame(); Assert.NotNull(frame); @@ -645,7 +680,7 @@ private static void CaptureShellVisualState( imageSize.Width, imageSize.Height, "headless-avalonia-window", - ResolveShellCaptureScope(openedFlyout, openedPopup, openedContextMenu), + ResolveShellCaptureScope(openedFlyout, openedPopup, openedContextMenu, capturedLassoTargetName), ToRepoRelativePath(repoRoot, imagePath), ShellStateManifestRelativePath, viewModel.SelectedHostMenuGroupTitle, @@ -661,6 +696,9 @@ private static void CaptureShellVisualState( openedContextMenu?.TargetPartName, openedContextMenu?.IsOpen ?? false, openedContextMenu?.CoveredHeaders ?? [], + capturedLassoTargetName, + capturedLassoTargetName is null ? null : "NodeCanvasSelectionMode.Lasso", + capturedLassoTargetName is null ? null : "LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag", pixelInspection.NonTransparentPixelCount, pixelInspection.DistinctColorCount, CreateRecordOnlyDriftMeasurementMetadata(), @@ -676,7 +714,10 @@ private static void CaptureShellVisualState( Assert.Contains(route.RecipeId, metadataJson, StringComparison.Ordinal); Assert.Contains(shellState.Language, metadataJson, StringComparison.Ordinal); Assert.Contains(shellState.Theme, metadataJson, StringComparison.Ordinal); - Assert.Contains(ResolveShellCaptureScope(openedFlyout, openedPopup, openedContextMenu), metadataJson, StringComparison.Ordinal); + Assert.Contains( + ResolveShellCaptureScope(openedFlyout, openedPopup, openedContextMenu, capturedLassoTargetName), + metadataJson, + StringComparison.Ordinal); Assert.Contains(ToRepoRelativePath(repoRoot, imagePath), metadataJson, StringComparison.Ordinal); Assert.Contains(ShellStateManifestRelativePath, metadataJson, StringComparison.Ordinal); Assert.Contains("\"DriftMeasurement\"", metadataJson, StringComparison.Ordinal); @@ -714,6 +755,14 @@ private static void CaptureShellVisualState( Assert.Contains("\"IsContextMenuOpen\": true", metadataJson, StringComparison.Ordinal); Assert.All(shellState.RequiredContextMenuHeaders, header => Assert.Contains(header, metadataJson, StringComparison.Ordinal)); } + + if (!string.IsNullOrWhiteSpace(shellState.LassoTargetPartName)) + { + Assert.Contains("full-window-shell-lasso-state", metadataJson, StringComparison.Ordinal); + Assert.Contains(shellState.LassoTargetPartName, metadataJson, StringComparison.Ordinal); + Assert.Contains("NodeCanvasSelectionMode.Lasso", metadataJson, StringComparison.Ordinal); + Assert.Contains("LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag", metadataJson, StringComparison.Ordinal); + } } finally { @@ -724,7 +773,8 @@ private static void CaptureShellVisualState( private static string ResolveShellCaptureScope( MenuItem? openedFlyout, Control? openedPopup, - OpenContextMenuResult? openedContextMenu) + OpenContextMenuResult? openedContextMenu, + string? capturedLassoTargetName) { if (openedPopup is not null) { @@ -736,6 +786,11 @@ private static string ResolveShellCaptureScope( return "full-window-shell-context-menu-state"; } + if (capturedLassoTargetName is not null) + { + return "full-window-shell-lasso-state"; + } + return openedFlyout is null ? "full-window-shell-state" : "full-window-shell-flyout-state"; @@ -833,6 +888,59 @@ private static VisualDriftMeasurementMetadata CreateRecordOnlyDriftMeasurementMe presenter.CoveredHeaders); } + private static string? OpenRequestedLassoProof(MainWindow window, CookbookShellVisualGateState shellState) + { + if (string.IsNullOrWhiteSpace(shellState.LassoTargetPartName)) + { + return null; + } + + var target = Assert.IsType(FindShellControl(window, shellState.LassoTargetPartName)); + target.SelectionMode = NodeCanvasSelectionMode.Lasso; + var pointer = new global::Avalonia.Input.Pointer(1, PointerType.Mouse, isPrimary: true); + var topLeft = new Point(420, 260); + var topRight = new Point(720, 260); + var bottomRight = new Point(720, 520); + + target.RaiseEvent(CreatePointerPressedArgs(target, pointer, topLeft)); + target.RaiseEvent(CreatePointerMovedArgs(target, pointer, topRight)); + target.RaiseEvent(CreatePointerMovedArgs(target, pointer, bottomRight)); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); + + Assert.Equal(NodeCanvasSelectionMode.Lasso, target.SelectionMode); + Assert.Single(target.FindControl("OverlayLayer")!.Children.OfType()); + return shellState.LassoTargetPartName; + } + + private static PointerPressedEventArgs CreatePointerPressedArgs( + NodeCanvas canvas, + global::Avalonia.Input.Pointer pointer, + Point position) + => new( + canvas, + pointer, + canvas, + position, + 0UL, + new PointerPointProperties(RawInputModifiers.LeftMouseButton, PointerUpdateKind.LeftButtonPressed), + KeyModifiers.None, + 1); + + private static PointerEventArgs CreatePointerMovedArgs( + NodeCanvas canvas, + global::Avalonia.Input.Pointer pointer, + Point position) + => new( + InputElement.PointerMovedEvent, + canvas, + pointer, + canvas, + position, + 0UL, + new PointerPointProperties(RawInputModifiers.LeftMouseButton, PointerUpdateKind.Other), + KeyModifiers.None); + private static Control? FindShellControl(MainWindow window, string partName) => window.FindControl(partName) ?? window.GetVisualDescendants() @@ -947,6 +1055,7 @@ private sealed record CookbookShellVisualGateState( string[] RequiredPopupText, string? ContextMenuTargetPartName, string[] RequiredContextMenuHeaders, + string? LassoTargetPartName, string[] RequiredShellParts, string OutputFileName); @@ -1001,6 +1110,9 @@ private sealed record CookbookShellVisualGateMetadata( string? ContextMenuTargetPartName, bool IsContextMenuOpen, string[] CoveredContextMenuHeaders, + string? LassoTargetPartName, + string? LassoSelectionMode, + string? LassoFeedbackProof, int NonTransparentPixelCount, int DistinctColorCount, VisualDriftMeasurementMetadata DriftMeasurement, diff --git a/tests/AsterGraph.Demo.Tests/PostPhase506VisualQueueDocsTests.cs b/tests/AsterGraph.Demo.Tests/PostPhase506VisualQueueDocsTests.cs index 75737747..70f256db 100644 --- a/tests/AsterGraph.Demo.Tests/PostPhase506VisualQueueDocsTests.cs +++ b/tests/AsterGraph.Demo.Tests/PostPhase506VisualQueueDocsTests.cs @@ -60,7 +60,7 @@ private static void AssertClosedVisualQueue(string contents) private static void AssertCurrentQueue(string table) { Assert.Contains("| #193 | `avalonia-node-map-8l6` | Phase 535: refresh post-lasso visual feedback parity queue", table, StringComparison.Ordinal); - Assert.Contains("Phase 536: lasso screenshot route and Cookbook proof boundary", table, StringComparison.Ordinal); + Assert.Contains("| #195 | `avalonia-node-map-uvd` | Phase 536: lasso screenshot route and Cookbook proof boundary", table, StringComparison.Ordinal); Assert.Contains("Phase 537: lasso toolbar UX and public activation ergonomics boundary", table, StringComparison.Ordinal); Assert.Contains("Phase 538: eraser behavior/API feasibility gate", table, StringComparison.Ordinal); Assert.Contains("Phase 539: rectangle/freehand drawing primitive model gate", table, StringComparison.Ordinal); @@ -76,6 +76,7 @@ private static void AssertCurrentQueue(string table) Assert.DoesNotContain("Phase 527: Background variant public surface gate", table, StringComparison.Ordinal); Assert.DoesNotContain("Phase 528: Panel versus viewport-attached overlay boundary", table, StringComparison.Ordinal); Assert.DoesNotContain("Phase 529: whiteboard/lasso/eraser feasibility audit", table, StringComparison.Ordinal); + Assert.DoesNotContain("| TBD | TBD | Phase 536: lasso screenshot route and Cookbook proof boundary", table, StringComparison.Ordinal); Assert.DoesNotContain("| #137 | `avalonia-node-map-3tw` | Phase 507: post-Phase-506 visual queue refresh", table, StringComparison.Ordinal); Assert.DoesNotContain("| #143 | `avalonia-node-map-1j4` | Phase 512: pixel-baseline drift measurement", table, StringComparison.Ordinal); Assert.DoesNotContain("| #149 | `avalonia-node-map-d8q` | Phase 513: post-Phase-512 roadmap refresh", table, StringComparison.Ordinal); diff --git a/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs b/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs index 6a7fba4f..d6af9afa 100644 --- a/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs +++ b/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs @@ -966,6 +966,39 @@ public void ParityRoadmapDocs_RecordPhase535PostLassoQueueRefreshInBothLocales() AssertPostPhase534Queue(ExtractIssueWaveTable(chineseParity)); } + [Fact] + public void ParityRoadmapDocs_RecordPhase536LassoScreenshotProofBoundaryInBothLocales() + { + var englishParity = ReadRepoFile("docs/en/phase-0-reactflow-parity-audit.md"); + var chineseParity = ReadRepoFile("docs/zh-CN/phase-0-reactflow-parity-audit.md"); + + foreach (var contents in new[] { englishParity, chineseParity }) + { + Assert.Contains("Phase 536", contents, StringComparison.Ordinal); + Assert.Contains("GitHub #195", contents, StringComparison.Ordinal); + Assert.Contains("avalonia-node-map-uvd", contents, StringComparison.Ordinal); + Assert.Contains("lasso screenshot route and Cookbook proof boundary", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("cookbook-interaction-lasso-screenshot-proof", contents, StringComparison.Ordinal); + Assert.Contains("interaction-lasso-screenshot-proof-route", contents, StringComparison.Ordinal); + Assert.Contains("shell-cookbook-lasso-screenshot-proof", contents, StringComparison.Ordinal); + Assert.Contains("full-window-shell-lasso-state", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvasSelectionMode.Lasso", contents, StringComparison.Ordinal); + Assert.Contains("LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag", contents, StringComparison.Ordinal); + Assert.Contains("LASSO_SCREENSHOT_PROOF_BOUNDARY_OK", contents, StringComparison.Ordinal); + Assert.Contains("toolbar UX", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("eraser", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("drawing primitives", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("persistence", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("renderer rewrite", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("strict pixel baseline", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("retained API removal", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("full whiteboard parity", contents, StringComparison.OrdinalIgnoreCase); + } + + AssertPostPhase534Queue(ExtractIssueWaveTable(englishParity)); + AssertPostPhase534Queue(ExtractIssueWaveTable(chineseParity)); + } + [Fact] public void ParityRoadmapDocs_RecordPhase501PostPhase500QueueRefreshInBothLocales() { @@ -1056,7 +1089,7 @@ private static void AssertPostPhase518Queue(string table) private static void AssertPostPhase534Queue(string table) { Assert.Contains("| #193 | `avalonia-node-map-8l6` | Phase 535: refresh post-lasso visual feedback parity queue", table, StringComparison.Ordinal); - Assert.Contains("Phase 536: lasso screenshot route and Cookbook proof boundary", table, StringComparison.Ordinal); + Assert.Contains("| #195 | `avalonia-node-map-uvd` | Phase 536: lasso screenshot route and Cookbook proof boundary", table, StringComparison.Ordinal); Assert.Contains("Phase 537: lasso toolbar UX and public activation ergonomics boundary", table, StringComparison.Ordinal); Assert.Contains("Phase 538: eraser behavior/API feasibility gate", table, StringComparison.Ordinal); Assert.Contains("Phase 539: rectangle/freehand drawing primitive model gate", table, StringComparison.Ordinal); @@ -1074,6 +1107,7 @@ private static void AssertPostPhase534Queue(string table) table.Contains("Can run after Phase 535 in parallel", StringComparison.OrdinalIgnoreCase) || table.Contains("Phase 535 后可与", StringComparison.OrdinalIgnoreCase)); Assert.DoesNotContain("| #191 | `avalonia-node-map-lzy` | Phase 534: lasso visual gesture feedback route", table, StringComparison.Ordinal); + Assert.DoesNotContain("| TBD | TBD | Phase 536: lasso screenshot route and Cookbook proof boundary", table, StringComparison.Ordinal); Assert.DoesNotContain("Current Avalonia visual feedback slice", table, StringComparison.OrdinalIgnoreCase); Assert.DoesNotContain("当前 Avalonia overlay feedback slice", table, StringComparison.OrdinalIgnoreCase); } diff --git a/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs b/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs index 02a894e5..16cb66c8 100644 --- a/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs +++ b/tests/AsterGraph.Demo.Tests/ShellVisualCoveragePlanningDocsTests.cs @@ -61,7 +61,7 @@ public void ShellVisualCoveragePlanningDocs_KeepPlanningSliceDistinctFromPhase50 { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(10, document.RootElement.GetArrayLength()); + Assert.Equal(11, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -74,7 +74,7 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase509TooltipPopupRow() { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(10, document.RootElement.GetArrayLength()); + Assert.Equal(11, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -102,7 +102,7 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase510CanvasContextMenuRow() { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(10, document.RootElement.GetArrayLength()); + Assert.Equal(11, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -132,7 +132,7 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase511BoundedLanguageThemeRo { var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); - Assert.Equal(10, document.RootElement.GetArrayLength()); + Assert.Equal(11, document.RootElement.GetArrayLength()); Assert.Contains( document.RootElement.EnumerateArray(), state => @@ -189,6 +189,36 @@ public void ShellVisualCoveragePlanningDocs_RecordPhase512DriftMeasurementContra } } + [Fact] + public void ShellVisualCoveragePlanningDocs_RecordPhase536LassoScreenshotState() + { + var manifestPath = Path.Combine(GetRepositoryRoot(), "tests/AsterGraph.Demo.Tests/CookbookShellVisualGateStates.json"); + using var document = JsonDocument.Parse(File.ReadAllText(manifestPath)); + Assert.Equal(11, document.RootElement.GetArrayLength()); + Assert.Contains( + document.RootElement.EnumerateArray(), + state => + string.Equals(state.GetProperty("id").GetString(), "shell-cookbook-lasso-screenshot-proof", StringComparison.Ordinal) + && string.Equals(state.GetProperty("routeId").GetString(), "cookbook-interaction-lasso-screenshot-proof", StringComparison.Ordinal) + && string.Equals(state.GetProperty("lassoTargetPartName").GetString(), "PART_NodeCanvas", StringComparison.Ordinal)); + + var englishCookbook = ReadRepoFile("docs/en/demo-cookbook.md"); + var chineseCookbook = ReadRepoFile("docs/zh-CN/demo-cookbook.md"); + foreach (var contents in new[] { englishCookbook, chineseCookbook }) + { + Assert.Contains("Phase 536", contents, StringComparison.Ordinal); + Assert.Contains("GitHub #195", contents, StringComparison.Ordinal); + Assert.Contains("avalonia-node-map-uvd", contents, StringComparison.Ordinal); + Assert.Contains("shell-cookbook-lasso-screenshot-proof", contents, StringComparison.Ordinal); + Assert.Contains("full-window-shell-lasso-state", contents, StringComparison.Ordinal); + Assert.Contains("PART_NodeCanvas", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvasSelectionMode.Lasso", contents, StringComparison.Ordinal); + Assert.Contains("LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag", contents, StringComparison.Ordinal); + Assert.Contains("no strict pixel baseline", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no full whiteboard parity", contents, StringComparison.OrdinalIgnoreCase); + } + } + private static string ReadRepoFile(string relativePath) => File.ReadAllText(Path.Combine(GetRepositoryRoot(), relativePath));