diff --git a/docs/en/authoring-surface-recipe.md b/docs/en/authoring-surface-recipe.md index e0886eb9..e11187f2 100644 --- a/docs/en/authoring-surface-recipe.md +++ b/docs/en/authoring-surface-recipe.md @@ -81,7 +81,8 @@ Use one hosted handoff from definitions to proof instead of stitching together s 4. Write values back through `TrySetSelectedNodeParameterValue(...)` or `TrySetNodeParameterValue(...)`; keep validation on the shared session command path instead of adding a second editor model. 5. Project host commands from `GetCommandDescriptors()` so toolbars, menus, shortcuts, and palette actions stay on the same shared command route. 6. For hosted pointer-mode controls, render `AsterGraphAuthoringToolActionFactory.CreatePointerSelectionModeActions(canvas)` into the same authoring toolbar row. These actions set `NodeCanvas.SelectionMode` to `NodeCanvasSelectionMode.Marquee` or `NodeCanvasSelectionMode.Lasso`; they do not add a session command, a parallel selection model, or whiteboard drawing state. -7. Close the handoff with `src/AsterGraph.Demo -- --proof` and expect `PORT_HANDLE_ID_OK:True`, `PORT_GROUP_AUTHORING_OK:True`, `PORT_CONNECTION_HINT_OK:True`, `PORT_AUTHORING_SCOPE_BOUNDARY_OK:True`, `CUSTOM_EXTENSION_SURFACE_OK:True`, and `AUTHORING_SURFACE_OK:True`. +7. For hosted whiteboard drawing-tool activation, render `AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(canvas)` beside the pointer-mode controls. These actions set only `NodeCanvas.WhiteboardDrawingMode` to `NodeCanvasWhiteboardDrawingMode.Rectangle` or `NodeCanvasWhiteboardDrawingMode.Freehand`; `whiteboard-drawing.rectangle` and `whiteboard-drawing.freehand` have no runtime command id and do not start pointer capture or create drawing primitives. +8. Close the handoff with `src/AsterGraph.Demo -- --proof` and expect `PORT_HANDLE_ID_OK:True`, `PORT_GROUP_AUTHORING_OK:True`, `PORT_CONNECTION_HINT_OK:True`, `PORT_AUTHORING_SCOPE_BOUNDARY_OK:True`, `CUSTOM_EXTENSION_SURFACE_OK:True`, and `AUTHORING_SURFACE_OK:True`. ## Copy path @@ -91,7 +92,8 @@ Use one hosted handoff from definitions to proof instead of stitching together s 4. Replace editor bodies through `INodeParameterEditorRegistry`. 5. Render any host-owned edge overlay from `GetConnectionGeometrySnapshots()`. 6. Add lasso activation through `CreatePointerSelectionModeActions(...)` when the host needs a toolbar affordance for `NodeCanvasSelectionMode.Lasso`. -7. Keep runtime decorations on `IGraphRuntimeOverlayProvider` and inspector snapshots; do not move graph execution into the editor. +7. Add rectangle/freehand whiteboard activation through `CreateWhiteboardDrawingToolActions(...)` when the host needs a public activation affordance for `NodeCanvas.WhiteboardDrawingMode`, `NodeCanvasWhiteboardDrawingMode.Rectangle`, and `NodeCanvasWhiteboardDrawingMode.Freehand`. This remains activation only: no runtime command id, no pointer gesture capture, no persistence/schema changes, and no renderer changes. +8. Keep runtime decorations on `IGraphRuntimeOverlayProvider` and inspector snapshots; do not move graph execution into the editor. ## Related docs diff --git a/docs/en/phase-0-reactflow-parity-audit.md b/docs/en/phase-0-reactflow-parity-audit.md index 5cb7cba0..d2a67472 100644 --- a/docs/en/phase-0-reactflow-parity-audit.md +++ b/docs/en/phase-0-reactflow-parity-audit.md @@ -250,6 +250,10 @@ Phase 550 is GitHub #223 / `avalonia-node-map-wjr`, the whiteboard primitive Coo Phase 551 is GitHub #225 / `avalonia-node-map-3c9`, the post-Phase-550 whiteboard authoring interaction queue refresh. This docs/tests-only slice records `WHITEBOARD_AUTHORING_INTERACTION_QUEUE_REFRESH`: after the internal model, renderer adapter, persistence decision, and Cookbook screenshot proof route gates, the next authoring work is split into tracker-backed follow-ups for Phase 552 public drawing tool activation, Phase 553 pointer gesture capture, Phase 554 authoring toolbar/Cookbook UX, Phase 555 eraser-on-primitive behavior, and Phase 556 persistence implementation decision. It authorizes no runtime behavior changes, no public API changes, no pointer coordinator edits, no toolbar implementation, no eraser implementation, no persistence/schema changes, no renderer rewrite, no screenshot manifest expansion, no UI redesign, no retained API removal, and no full React Flow whiteboard parity. +## Phase 552 Update + +Phase 552 is GitHub #226 / `avalonia-node-map-718`, the whiteboard drawing tool public activation contract. This hosted Avalonia slice adds `NodeCanvas.WhiteboardDrawingMode`, `NodeCanvasWhiteboardDrawingMode.Rectangle`, `NodeCanvasWhiteboardDrawingMode.Freehand`, and `AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(...)`. The hosted actions `whiteboard-drawing.rectangle` and `whiteboard-drawing.freehand` are `GraphEditorCommandSourceKind.Host` actions with no runtime command id; they set only the canvas activation state and do not mutate the graph document or start drawing. `HostedActions_WhiteboardDrawingToolActionsSwitchNodeCanvasDrawingModeWithoutRuntimeCommandRoute` and `CommandRegistry_DoesNotExposeWhiteboardDrawingToolActivationAsRuntimeSessionCommand` guard the public activation contract and the runtime-command boundary. Phase 552 is stacked after PR #231 and must not merge before Phase 551. It authorizes no pointer gesture capture, no runtime drawing creation, no toolbar UI implementation, no eraser behavior, no persistence/schema changes, no renderer rewrite, no screenshot manifest expansion, no UI redesign, no retained API removal, and no full React Flow 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. @@ -329,7 +333,7 @@ Phase 483 closes GitHub #82 by choosing the bounded-docs path instead of a rende | Edge labels and markers | Present | Labels and source/target marker fields are modeled in edge presentation/geometry snapshots. | Keep guarded. | | Drag, pan, zoom | Present | Canvas pointer/wheel coordinators and viewport commands exist. | Keep guarded. | | Marquee/box selection and multi-select | Present | `GetSelectionRectangleSnapshot`, `GetSelectionLassoSnapshot`, overlay coordinator marquee selection, internal lasso bridge selection, internal lasso gesture capture, public `NodeCanvasSelectionMode.Lasso`, hosted pointer-mode actions, transient lasso overlay feedback, `SelectAll`, `SelectNone`, and `InvertSelection` have tests and Cookbook routes. Rectangle marquee is UI-backed and remains the default; lasso/freehand has backend/editor query support, an internal Avalonia bridge, internal pointer gesture capture, a public `NodeCanvas.SelectionMode` activation path, hosted toolbar activation, and transient visual feedback while dragging. | Keep guarded. | -| Whiteboard / lasso / eraser / rectangle draw | Gap retained / public lasso selection started | React Flow whiteboard docs list Freehand draw, Lasso selection, Eraser, and Rectangle draw as distinct advanced examples. AsterGraph now has rectangle marquee selection, command projection evidence, a guarded backend `GetSelectionLassoSnapshot(...)` query, an internal Avalonia lasso selection bridge, internal lasso gesture capture, public lasso pointer-mode activation, hosted toolbar UX through `CreatePointerSelectionModeActions(...)`, and transient visual feedback for active lasso drags, but no whiteboard primitives or drawing tools. | Phase 537 closes only the toolbar activation ergonomics boundary; later candidates remain tracker-backed before any whiteboard behavior claim expands. | +| Whiteboard / lasso / eraser / rectangle draw | Gap retained / public lasso selection and drawing-tool activation started | React Flow whiteboard docs list Freehand draw, Lasso selection, Eraser, and Rectangle draw as distinct advanced examples. AsterGraph now has rectangle marquee selection, command projection evidence, a guarded backend `GetSelectionLassoSnapshot(...)` query, an internal Avalonia lasso selection bridge, internal lasso gesture capture, public lasso pointer-mode activation, hosted toolbar UX through `CreatePointerSelectionModeActions(...)`, transient visual feedback for active lasso drags, internal whiteboard primitive skeleton/proof gates, and hosted drawing-tool activation through `NodeCanvas.WhiteboardDrawingMode`, `NodeCanvasWhiteboardDrawingMode.Rectangle`, `NodeCanvasWhiteboardDrawingMode.Freehand`, `CreateWhiteboardDrawingToolActions(...)`, `whiteboard-drawing.rectangle`, and `whiteboard-drawing.freehand`, but no pointer gesture capture or runtime drawing creation. | Phase 552 closes only the hosted activation contract; later candidates remain tracker-backed before any whiteboard behavior claim expands. | | Connection preview and validation | Present / guarded | Pending connection, compatible targets, validation snapshots, repair commands, and `validation-prevent-cycle` fixture exist. Cycle prevention is enforced by the canonical connection completion path and exposes `GraphEditorPendingConnectionRejectionReason.WouldCreateCycle` through pending snapshots. | Keep `RuntimeSession_CompleteConnection_RejectsDirectCycleWithStableReason`, `RuntimeSession_CompleteConnection_RejectsIndirectCycleThroughNormalCommandPath`, and `RuntimeSession_TryExecuteCommand_RejectsCycleThroughConnectionsConnectRoute` guarded. | | Context menu | Present | Menu descriptors and hosted context menu plumbing exist. | Keep guarded. | | Undo/redo | Present | Session commands and history tests cover normal command semantics. | Keep guarded. | @@ -423,9 +427,9 @@ Phase 529 records the whiteboard/lasso/eraser feasibility audit through GitHub # | React Flow whiteboard reference | Gap-scoping reference | React Flow's whiteboard guide at https://reactflow.dev/learn/advanced-use/whiteboard separates Freehand draw, Lasso selection, Eraser, and Rectangle draw examples from the core node/edge editor. | The reference scopes comparison only; Phase 529 does not import React Flow behavior or claim a complete whiteboard application. | | Rectangle marquee selection | Present / guarded | `GetSelectionRectangleSnapshot`, `UpdateMarqueeSelection`, `Queries_GetSelectionRectangleSnapshot_ReturnsNodesAndConnectionsInRectangle`, `UpdateMarqueeSelection_WithFinalizeTrue_UsesBackendSelectionRectangleQuery`, `interaction-selection-marquee-route`, `selection-marquee-workbench`, and `v079-selection-rectangle-route` prove rectangular selection evidence. | This is axis-aligned rectangle/marquee selection, not lasso/freehand selection or a drawing tool. | | Multi-select and command projection | Present / guarded | `selection.select-all`, `selection.select-none`, `selection.invert`, `selection.delete`, and `selection.transform.move` expose current selection and manipulation commands. | Command projection operates on graph selections; it does not add eraser hit trails, drawn shape lifecycle, or whiteboard-specific edit modes. | -| Lasso/freehand selection | Public Avalonia lasso selection mode with hosted toolbar activation and transient visual feedback / whiteboard gap retained | `GetSelectionLassoSnapshot`, `GraphEditorSelectionLassoSnapshot`, `Queries_GetSelectionLassoSnapshot_ReturnsCenterContainedNodesAndConnections`, `Queries_GetSelectionLassoSnapshot_UsesNodeCenterInsteadOfBoundsIntersection`, `Queries_GetSelectionLassoSnapshot_WithOpenOrDegeneratePath_ReturnsDeterministicResults`, `UpdateLassoSelection_WithFinalizeTrue_UsesBackendSelectionLassoQuery`, `LassoSelection_RoutesThroughCanvasBridge_AndSelectsContainedNodes`, `NodeCanvasSelectionGestureKind.Lasso`, `TryBeginLassoSelection_WithLassoGestureKind_RecordsStartAndMoveAfterThreshold`, `HandleMoved_WhenCanvasSelectionUsesLassoGesture_RecordsLassoPointAndSkipsMarqueeUpdate`, `HandleReleased_AfterLassoSelection_FinalizesLassoSelectionAndResetsSession`, `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RoutesThroughCanvasPointerHandlers_AndSelectsContainedNodes`, `UpdateLassoFeedback`, `ClearLassoFeedback`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`, `CreatePointerSelectionModeActions`, `pointer-mode.lasso-selection`, `PART_PointerModeLassoButton`, `HostedActions_PointerModeActionsSwitchNodeCanvasSelectionModeWithoutRuntimeCommandRoute`, and `AuthoringToolsChrome_ProjectsPointerModeActionsThroughNodeCanvasSelectionMode` prove the active-scope backend query, internal Avalonia bridge, internal gesture capture route, public pointer-mode activation route, hosted toolbar route, and transient visual feedback route. The query treats freeform points as a closed polygon, selects nodes by center-point containment, and selects connections when both endpoint nodes are selected. | No eraser behavior, drawing primitives, persistence, renderer layer, strict pixel baselines, retained API removal, or full React Flow whiteboard parity is implemented. This remains not full React Flow whiteboard parity. | +| Lasso/freehand selection | Public Avalonia lasso selection mode with hosted toolbar activation and transient visual feedback / whiteboard gap retained | `GetSelectionLassoSnapshot`, `GraphEditorSelectionLassoSnapshot`, `Queries_GetSelectionLassoSnapshot_ReturnsCenterContainedNodesAndConnections`, `Queries_GetSelectionLassoSnapshot_UsesNodeCenterInsteadOfBoundsIntersection`, `Queries_GetSelectionLassoSnapshot_WithOpenOrDegeneratePath_ReturnsDeterministicResults`, `UpdateLassoSelection_WithFinalizeTrue_UsesBackendSelectionLassoQuery`, `LassoSelection_RoutesThroughCanvasBridge_AndSelectsContainedNodes`, `NodeCanvasSelectionGestureKind.Lasso`, `TryBeginLassoSelection_WithLassoGestureKind_RecordsStartAndMoveAfterThreshold`, `HandleMoved_WhenCanvasSelectionUsesLassoGesture_RecordsLassoPointAndSkipsMarqueeUpdate`, `HandleReleased_AfterLassoSelection_FinalizesLassoSelectionAndResetsSession`, `NodeCanvasSelectionMode.Lasso`, `LassoSelectionMode_RoutesThroughCanvasPointerHandlers_AndSelectsContainedNodes`, `UpdateLassoFeedback`, `ClearLassoFeedback`, `LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`, `CreatePointerSelectionModeActions`, `pointer-mode.lasso-selection`, `PART_PointerModeLassoButton`, `HostedActions_PointerModeActionsSwitchNodeCanvasSelectionModeWithoutRuntimeCommandRoute`, and `AuthoringToolsChrome_ProjectsPointerModeActionsThroughNodeCanvasSelectionMode` prove the active-scope backend query, internal Avalonia bridge, internal gesture capture route, public pointer-mode activation route, hosted toolbar route, and transient visual feedback route. The query treats freeform points as a closed polygon, selects nodes by center-point containment, and selects connections when both endpoint nodes are selected. | No eraser behavior, drawing primitive pointer capture, persistence, renderer layer, strict pixel baselines, retained API removal, or full React Flow whiteboard parity is implemented. This remains not full React Flow whiteboard parity. | | Eraser tool | Feasibility gate recorded / gap retained | `selection.delete`, the `Delete Selection` descriptor, and the stock `selection-delete` placement prove graph-selection deletion evidence only. | Phase 538 records that graph-selection deletion is not an eraser cursor, collision trail, or eraser-specific hit-testing path. No eraser cursor, no collision trail, no eraser-specific hit-testing, no drawing primitives, no persistence, no renderer rewrite, no strict pixel baseline enforcement, no retained API removal, and no full whiteboard parity are implemented. | -| Rectangle/freehand drawing | Model gate recorded / gap retained | Phase 539 / GitHub #201 / `avalonia-node-map-rwr` ties current reusable evidence to `GraphDocument`, `GraphNode`, `GraphConnection`, `GraphEditorSelectionRectangleSnapshot`, `GraphEditorSelectionLassoSnapshot`, `GetSelectionRectangleSnapshot`, `GetSelectionLassoSnapshot`, `interaction-selection-marquee-route`, `selection-marquee-workbench`, and `cookbook-interaction-lasso-screenshot-proof`. These prove graph-scene, selection, and visual-proof seams only. | Before rectangle/freehand drawing behavior can be implemented or claimed, a later API/model issue must define model identity, geometry, style/brush state, hit-testing/edit lifecycle, renderer projection, persistence/drawing persistence, and Cookbook/screenshot evidence. Phase 539 adds no runtime behavior changes, no public API changes, no Avalonia pointer coordinator edits, no renderer-layer changes, no persistence/schema changes, no screenshot manifest expansion, and no full React Flow whiteboard parity. | +| Rectangle/freehand drawing | Public activation contract only / behavior gap retained | Phase 552 / GitHub #226 / `avalonia-node-map-718` adds the hosted activation surface `NodeCanvas.WhiteboardDrawingMode`, `NodeCanvasWhiteboardDrawingMode.Rectangle`, `NodeCanvasWhiteboardDrawingMode.Freehand`, `AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(...)`, `whiteboard-drawing.rectangle`, and `whiteboard-drawing.freehand`. The actions are `GraphEditorCommandSourceKind.Host` actions with no runtime command id, and `HostedActions_WhiteboardDrawingToolActionsSwitchNodeCanvasDrawingModeWithoutRuntimeCommandRoute` plus `CommandRegistry_DoesNotExposeWhiteboardDrawingToolActivationAsRuntimeSessionCommand` prove they only set canvas activation state. | This is not drawing behavior. Phase 552 adds no pointer gesture capture, no runtime drawing creation, no toolbar UI implementation, no eraser behavior, no persistence/schema changes, no drawing persistence, no renderer rewrite, no screenshot manifest expansion, and no full React Flow whiteboard parity. | | Whiteboard persistence/render layer | Readiness gate recorded / gap retained | Phase 540 / GitHub #203 / `avalonia-node-map-91b` ties current reusable evidence to `GraphDocumentSerializer`, `GraphDocumentCompatibility`, `CurrentSchemaVersion`, `GraphWorkspaceService`, `GraphEditorSceneSnapshot`, `NodeCanvasConnectionSceneRenderer`, `CookbookScreenshotGateRoutes.json`, and `CookbookShellVisualGateStates.json`. These prove graph document/workspace persistence, graph-scene projection, committed connection rendering, and screenshot fixture coverage only. | Future whiteboard annotation persistence and whiteboard annotation rendering need a later implementation issue with a persistence/schema contract, renderer projection contract, hit-testing/edit lifecycle, screenshot/Cookbook evidence, migration policy, and explicit non-goals. Phase 540 adds no runtime behavior changes, no public API changes, no persistence/schema changes, no renderer-layer changes, no screenshot manifest expansion, and no full React Flow whiteboard parity. | ## Completed Phase 0 Issue Wave @@ -585,6 +589,8 @@ Phase 550 records the whiteboard primitive Cookbook screenshot implementation ga Phase 551 records the post-Phase-550 whiteboard authoring interaction queue refresh through GitHub #225 / `avalonia-node-map-3c9`. It adds only `WHITEBOARD_AUTHORING_INTERACTION_QUEUE_REFRESH` and the next tracker-backed authoring split: public drawing tool activation, pointer gesture capture, authoring toolbar/Cookbook UX, eraser-on-primitive behavior, and persistence implementation decision. It does not implement runtime behavior, public API, pointer coordinator changes, toolbar UI, eraser behavior, persistence/schema changes, renderer changes, screenshot manifest expansion, UI redesign, or full React Flow whiteboard parity. +Phase 552 records the whiteboard drawing tool public activation contract through GitHub #226 / `avalonia-node-map-718`. It adds `NodeCanvas.WhiteboardDrawingMode`, `NodeCanvasWhiteboardDrawingMode.Rectangle`, `NodeCanvasWhiteboardDrawingMode.Freehand`, `CreateWhiteboardDrawingToolActions(...)`, `whiteboard-drawing.rectangle`, and `whiteboard-drawing.freehand` as hosted activation only. The actions are host-source actions with no runtime command id and do not add pointer gesture capture, runtime drawing creation, toolbar UI implementation, eraser behavior, persistence/schema changes, renderer changes, screenshot manifest expansion, or full React Flow whiteboard parity. + | 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. | @@ -733,4 +739,5 @@ Current coverage includes scene-level route captures plus ten manifest-driven fu - Phase 549 is GitHub #221 / `avalonia-node-map-3l6`; it records `WHITEBOARD_PRIMITIVE_PERSISTENCE_DECISION_GATE` with the internal `GraphWhiteboardPrimitivePersistenceDecision` policy contract only. GraphDocument schema changes, schema version bumps, workspace persistence behavior changes, public drawing APIs, pointer coordinators, toolbar/tool activation, eraser behavior, screenshot manifest expansion, UI redesign, retained API removal, and full React Flow whiteboard parity remain out of scope. - Phase 550 is GitHub #223 / `avalonia-node-map-wjr`; it records `WHITEBOARD_PRIMITIVE_COOKBOOK_SCREENSHOT_GATE` with `cookbook-whiteboard-primitive-screenshot-proof`, `shell-cookbook-whiteboard-primitive-screenshot-proof`, route metadata, shell state, non-overlap visual proof, and `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` only. Public drawing APIs, pointer coordinators, toolbar work, eraser behavior, persisted whiteboard primitive state, GraphDocument schema changes, renderer rewrites, UI redesign, retained API removal, and full React Flow whiteboard parity remain out of scope. - Phase 551 is GitHub #225 / `avalonia-node-map-3c9`; it records `WHITEBOARD_AUTHORING_INTERACTION_QUEUE_REFRESH` and splits the next whiteboard authoring work into Phase 552 public drawing tool activation, Phase 553 pointer gesture capture, Phase 554 authoring toolbar/Cookbook UX, Phase 555 eraser-on-primitive behavior, and Phase 556 persistence implementation decision without implementing those behaviors. +- Phase 552 is GitHub #226 / `avalonia-node-map-718`; it records the whiteboard drawing tool public activation contract through `NodeCanvas.WhiteboardDrawingMode`, `NodeCanvasWhiteboardDrawingMode.Rectangle`, `NodeCanvasWhiteboardDrawingMode.Freehand`, `CreateWhiteboardDrawingToolActions(...)`, `whiteboard-drawing.rectangle`, and `whiteboard-drawing.freehand`, with no runtime command id, pointer gesture capture, runtime drawing creation, toolbar UI implementation, eraser behavior, persistence/schema changes, renderer changes, screenshot manifest expansion, or full React Flow 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, Phase 535, Phase 539, Phase 540, Phase 541, Phase 546, Phase 550, and Phase 551 unless a focused test proves a specific missing contract. diff --git a/docs/en/public-api-inventory.md b/docs/en/public-api-inventory.md index ae41a946..90d0798f 100644 --- a/docs/en/public-api-inventory.md +++ b/docs/en/public-api-inventory.md @@ -33,7 +33,7 @@ Use this page with [Host Integration](./host-integration.md) and [Extension Cont | `AsterGraph.Abstractions` | Node definitions, port definitions, provider/plugin-facing contracts, identifiers, metadata DTOs used by the canonical route, and thin definition builders that produce those DTOs. | None. | None. | None currently published as a primary support tier. | Implementation helpers not exposed through package docs. | | `AsterGraph.Core` | Graph document, current-schema serialization model contracts, explicit legacy import, group container/collapse semantics, persisted node surface state including `GraphNodeSurfaceState.RotationDegrees`, compatibility rule inputs, thin implicit-conversion rule builder, and shared data types used by editor/session composition. | None. | `GraphDocumentSerializer.ImportLegacy(...)` as a bounded import/migration entry point for older payloads. | None in the primary v1 surface. Retired surfaces are listed in the v1 removal policy. | Core internals and persistence implementation details. | | `AsterGraph.Editor` | `AsterGraphEditorFactory.CreateSession(...)`, `IGraphEditorSession`, `IGraphEditorCommands`, `IGraphEditorQueries`, DTO/snapshot queries including `GraphEditorNodeSurfaceSnapshot.RotationDegrees`, diagnostics, automation, validation snapshots, runtime overlay snapshots/providers, layout plan snapshots/providers, viewport visible scene projection snapshots/projector, hierarchy/group snapshots including collapsed-boundary connection projection, navigator/outline snapshots, plugin discovery/inspection, export services including raster export progress/cancel options. | `AsterGraphEditorFactory.Create(...)` as a hosted composition helper that still exposes the retained facade. | `GraphEditorViewModel`, `GraphEditorViewModel.Session`, retained menu/context-menu hooks, and `NodeViewModel.RotationDegrees` used by migrating hosts. | None in the primary v1 surface. Retired surfaces are listed in the v1 removal policy. | `Runtime.Internal`, `Kernel.Internal`, projection/apply internals, proof-only helpers. | -| `AsterGraph.Avalonia` | Adapter projection over the canonical editor/session route and hosted factories that consume the same runtime owner. | `AsterGraphAvaloniaViewFactory.Create(...)`, `AsterGraphCanvasViewFactory`, `AsterGraphInspectorViewFactory`, `AsterGraphMiniMapViewFactory`, `AsterGraphControls`, `NodeToolbar`, `EdgeToolbar`, `NodeResizer`, `AsterGraphPanel`, `AsterGraphHostBuilder`, `AsterGraphWorkbenchOptions`, `AsterGraphBuiltInComponentCatalog`, hosted pointer selection mode actions, hosted workbench layout/panel-state options, and hosted performance policy budget markers. | `GraphEditorView` embedding for hosts that still use the retained editor facade. | Adapter-specific compatibility glue only when it bridges existing hosts to the canonical route. | Control internals, templates, interaction session internals, visual-only implementation details. | +| `AsterGraph.Avalonia` | Adapter projection over the canonical editor/session route and hosted factories that consume the same runtime owner. | `AsterGraphAvaloniaViewFactory.Create(...)`, `AsterGraphCanvasViewFactory`, `AsterGraphInspectorViewFactory`, `AsterGraphMiniMapViewFactory`, `AsterGraphControls`, `NodeToolbar`, `EdgeToolbar`, `NodeResizer`, `AsterGraphPanel`, `AsterGraphHostBuilder`, `AsterGraphWorkbenchOptions`, `AsterGraphBuiltInComponentCatalog`, hosted pointer selection mode actions, hosted whiteboard drawing tool activation actions, hosted workbench layout/panel-state options, and hosted performance policy budget markers. | `GraphEditorView` embedding for hosts that still use the retained editor facade. | Adapter-specific compatibility glue only when it bridges existing hosts to the canonical route. | Control internals, templates, interaction session internals, visual-only implementation details. | ## Route Mapping @@ -45,6 +45,7 @@ Use this page with [Host Integration](./host-integration.md) and [Extension Cont | Phase 530 lasso selection query route | Stable canonical | `IGraphEditorQueries.GetSelectionLassoSnapshot(...)`, `GraphEditorSelectionLassoSnapshot`, `GraphEditorSelectionLassoSnapshot.NodeIds`, `GraphEditorSelectionLassoSnapshot.ConnectionIds` | Backend/editor lasso query route for custom UIs that capture freeform polygon points themselves. The query stays on the session route, uses active-scope graph state, selects nodes by center-point polygon containment, and selects connections only when both endpoint nodes are selected. This does not add Avalonia gesture capture, pointer-mode state, drawing primitives, eraser behavior, persistence, renderer changes, screenshot rows, or full React Flow whiteboard parity. | | Phase 533 lasso pointer-mode route | Supported hosted helper | `NodeCanvas.SelectionMode`, `NodeCanvasSelectionMode.Marquee`, `NodeCanvasSelectionMode.Lasso` | Hosted Avalonia activation path for stock canvas lasso selection. Marquee remains the default; hosts can opt into lasso for empty-canvas selection drags. This reuses the existing backend lasso query and Avalonia lasso bridge, and does not add toolbar UX, visual gesture capture, screenshot rows, eraser behavior, drawing primitives, persistence, renderer changes, or full React Flow whiteboard parity. | | Phase 537 lasso toolbar ergonomics route | Supported hosted helper | `AsterGraphAuthoringToolActionFactory.CreatePointerSelectionModeActions(...)`, `pointer-mode.marquee-selection`, `pointer-mode.lasso-selection`, `NodeCanvas.SelectionMode`, `NodeCanvasSelectionMode.Lasso` | Hosted toolbar action projection for switching the stock canvas between marquee and lasso selection. The helper writes only the existing `NodeCanvas.SelectionMode` property, returns host-source actions with no runtime command id, and does not add a parallel selection model, eraser behavior, drawing primitives, persistence, renderer changes, strict pixel baselines, retained API removal, or full React Flow whiteboard parity. | +| Phase 552 whiteboard drawing tool activation route | Supported hosted helper | `NodeCanvas.WhiteboardDrawingMode`, `NodeCanvasWhiteboardDrawingMode.None`, `NodeCanvasWhiteboardDrawingMode.Rectangle`, `NodeCanvasWhiteboardDrawingMode.Freehand`, `AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(...)`, `whiteboard-drawing.rectangle`, `whiteboard-drawing.freehand` | Hosted activation contract for rectangle/freehand whiteboard drawing tools. The helper writes only the `NodeCanvas.WhiteboardDrawingMode` property, returns host-source actions with no runtime command id, and does not add pointer gesture capture, runtime drawing creation, toolbar UI implementation, eraser behavior, persistence/schema changes, renderer changes, screenshot manifest expansion, retained API removal, or full React Flow whiteboard parity. | | Shipped Avalonia UI | Supported hosted helper | `AsterGraphEditorFactory.Create(...)`, `AsterGraphAvaloniaViewFactory.Create(...)` | Uses the same runtime owner; not a second runtime model. | | Thin hosted builder | Supported hosted helper | `AsterGraphHostBuilder.Create(...).UseDefaultWorkbench().UseLayoutProvider(...).BuildAvaloniaView()`, plus `UseBehaviorOptions(...)`, `UseContextMenuAugmentor(...)`, `UseNodePresentationProvider(...)`, `UseToolProvider(...)`, `UseRuntimeOverlayProvider(...)`, and `UseLayoutProvider(...)` | Reduces common Avalonia setup boilerplate while delegating to canonical factories; the pass-throughs expose existing `AsterGraphEditorOptions` seams, including the host-owned synchronous layout provider seam, and do not create a new runtime model. `AsterGraphWorkbenchOptions` only controls stock hosted chrome. | | Built-in component catalog | Supported hosted helper | `AsterGraphBuiltInComponentCatalog.Components`, `AsterGraphBuiltInComponentCatalog.TryGet(...)`, `AsterGraphBuiltInComponentDescriptor`, `AsterGraphBuiltInComponentStatus`, `AsterGraphControls`, `NodeToolbar`, `EdgeToolbar`, `NodeResizer`, `AsterGraphPanel` | Discovery-only Phase 3 map for built-in component tracks. Public entries point to existing canvas, minimap, background-grid, inspector, controls-panel, `controls`, command-tool-projection, `node-toolbar`, `edge-toolbar`, `node-resizer`, and `panel` surfaces. `AsterGraphControls` is a thin Avalonia control over `IGraphEditorSession.Commands.TryExecuteCommand(...)` for viewport zoom, fit, and reset actions; `NodeToolbar` and `EdgeToolbar` are thin Avalonia controls over `AsterGraphAuthoringToolActionFactory.CreateNodeActions(...)` and `CreateConnectionActions(...)`; `NodeResizer` is a thin Avalonia control over `IGraphEditorSession.Commands.TrySetNodeSize(...)`; `AsterGraphPanel` is a host-owned content overlay primitive with stable positional placement. | diff --git a/docs/zh-CN/authoring-surface-recipe.md b/docs/zh-CN/authoring-surface-recipe.md index 3836b297..1ce92cb8 100644 --- a/docs/zh-CN/authoring-surface-recipe.md +++ b/docs/zh-CN/authoring-surface-recipe.md @@ -81,7 +81,8 @@ 4. 写回时继续走 `TrySetSelectedNodeParameterValue(...)` 或 `TrySetNodeParameterValue(...)`,把 validation 保留在共享 session command 路线上,不再引入第二套 editor model。 5. 宿主命令继续从 `GetCommandDescriptors()` 投影,这样 toolbar、menu、shortcut 和 palette action 都停留在同一条共享 command route 上。 6. hosted pointer-mode 控件直接把 `AsterGraphAuthoringToolActionFactory.CreatePointerSelectionModeActions(canvas)` 投到同一行 authoring toolbar。它们只把 `NodeCanvas.SelectionMode` 设为 `NodeCanvasSelectionMode.Marquee` 或 `NodeCanvasSelectionMode.Lasso`,不新增 session command、第二套 selection model 或 whiteboard drawing state。 -7. 最后用 `src/AsterGraph.Demo -- --proof` 收口,并期待看到 `PORT_HANDLE_ID_OK:True`、`PORT_GROUP_AUTHORING_OK:True`、`PORT_CONNECTION_HINT_OK:True`、`PORT_AUTHORING_SCOPE_BOUNDARY_OK:True`、`CUSTOM_EXTENSION_SURFACE_OK:True` 和 `AUTHORING_SURFACE_OK:True`。 +7. hosted whiteboard drawing-tool activation 控件可以把 `AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(canvas)` 放到 pointer-mode controls 旁边。这些 action 只把 `NodeCanvas.WhiteboardDrawingMode` 设为 `NodeCanvasWhiteboardDrawingMode.Rectangle` 或 `NodeCanvasWhiteboardDrawingMode.Freehand`;`whiteboard-drawing.rectangle` 和 `whiteboard-drawing.freehand` 没有 runtime command id,也不会启动 pointer capture 或创建 drawing primitives。 +8. 最后用 `src/AsterGraph.Demo -- --proof` 收口,并期待看到 `PORT_HANDLE_ID_OK:True`、`PORT_GROUP_AUTHORING_OK:True`、`PORT_CONNECTION_HINT_OK:True`、`PORT_AUTHORING_SCOPE_BOUNDARY_OK:True`、`CUSTOM_EXTENSION_SURFACE_OK:True` 和 `AUTHORING_SURFACE_OK:True`。 ## 复制顺序 @@ -91,7 +92,8 @@ 4. 通过 `INodeParameterEditorRegistry` 替换 editor body。 5. 通过 `GetConnectionGeometrySnapshots()` 渲染宿主自管 edge overlay。 6. 宿主需要 toolbar affordance 时,用 `CreatePointerSelectionModeActions(...)` 接入 `NodeCanvasSelectionMode.Lasso`。 -7. runtime decoration 保持在 `IGraphRuntimeOverlayProvider` 和 inspector snapshots 上;不要把图执行搬进 editor。 +7. 宿主需要 public activation affordance 时,用 `CreateWhiteboardDrawingToolActions(...)` 接入 `NodeCanvas.WhiteboardDrawingMode`、`NodeCanvasWhiteboardDrawingMode.Rectangle` 和 `NodeCanvasWhiteboardDrawingMode.Freehand`。这仍然只是 activation:no runtime command id、no pointer gesture capture、no persistence/schema changes、no renderer changes。 +8. runtime decoration 保持在 `IGraphRuntimeOverlayProvider` 和 inspector snapshots 上;不要把图执行搬进 editor。 ## 相关文档 diff --git a/docs/zh-CN/phase-0-reactflow-parity-audit.md b/docs/zh-CN/phase-0-reactflow-parity-audit.md index b131e365..0a5f6505 100644 --- a/docs/zh-CN/phase-0-reactflow-parity-audit.md +++ b/docs/zh-CN/phase-0-reactflow-parity-audit.md @@ -250,6 +250,10 @@ Phase 550 是 GitHub #223 / `avalonia-node-map-wjr`,记录 whiteboard primitiv Phase 551 是 GitHub #225 / `avalonia-node-map-3c9`,记录 post-Phase-550 whiteboard authoring interaction queue refresh。本 docs/tests-only slice 记录 `WHITEBOARD_AUTHORING_INTERACTION_QUEUE_REFRESH`:在 internal model、renderer adapter、persistence decision 和 Cookbook screenshot proof route gates 之后,把下一批 authoring work 拆成 tracker-backed follow-ups:Phase 552 public drawing tool activation、Phase 553 pointer gesture capture、Phase 554 authoring toolbar/Cookbook UX、Phase 555 eraser-on-primitive behavior 和 Phase 556 persistence implementation decision。它不授权 no runtime behavior changes、no public API changes、no pointer coordinator edits、no toolbar implementation、no eraser implementation、no persistence/schema changes、no renderer rewrite、no screenshot manifest expansion、no UI redesign、no retained API removal 或 no full React Flow whiteboard parity。 +## Phase 552 更新 + +Phase 552 是 GitHub #226 / `avalonia-node-map-718`,记录 whiteboard drawing tool public activation contract。本 hosted Avalonia slice 新增 `NodeCanvas.WhiteboardDrawingMode`、`NodeCanvasWhiteboardDrawingMode.Rectangle`、`NodeCanvasWhiteboardDrawingMode.Freehand` 和 `AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(...)`。Hosted actions `whiteboard-drawing.rectangle` 与 `whiteboard-drawing.freehand` 是 no runtime command id 的 `GraphEditorCommandSourceKind.Host` actions;它们只设置 canvas activation state,不修改 graph document,也不启动 drawing。`HostedActions_WhiteboardDrawingToolActionsSwitchNodeCanvasDrawingModeWithoutRuntimeCommandRoute` 与 `CommandRegistry_DoesNotExposeWhiteboardDrawingToolActivationAsRuntimeSessionCommand` 守住 public activation contract 和 runtime-command boundary。Phase 552 stacked after PR #231,不能早于 Phase 551 合并。它不授权 no pointer gesture capture、no runtime drawing creation、no toolbar UI implementation、no eraser behavior、no persistence/schema changes、no renderer rewrite、no screenshot manifest expansion、no UI redesign、no retained API removal 或 no full React Flow 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。 @@ -329,7 +333,7 @@ Phase 483 通过选择 bounded-docs 路径关闭 GitHub #82,而不是重写 re | Edge labels and markers | Present | Labels 与 source/target marker fields 已在 edge presentation/geometry snapshots 中建模。 | Keep guarded。 | | Drag, pan, zoom | Present | Canvas pointer/wheel coordinators 与 viewport commands 已存在。 | Keep guarded。 | | Marquee/box selection and multi-select | Present | `GetSelectionRectangleSnapshot`、`GetSelectionLassoSnapshot`、overlay coordinator marquee selection、internal lasso bridge selection、internal lasso gesture capture、public `NodeCanvasSelectionMode.Lasso`、transient lasso overlay feedback、`SelectAll`、`SelectNone`、`InvertSelection` 有 tests 和 Cookbook routes。Rectangle marquee 已有 UI-backed route 且仍是 default;lasso/freehand 有 backend/editor query、internal Avalonia bridge、internal pointer gesture capture、public `NodeCanvas.SelectionMode` activation path 和拖拽中的 transient visual feedback。 | Keep guarded。 | -| Whiteboard / lasso / eraser / rectangle draw | Gap retained / public lasso selection started | React Flow whiteboard docs 把 Freehand draw、Lasso selection、Eraser 和 Rectangle draw 列为 distinct advanced examples。AsterGraph 现在有 rectangle marquee selection、command projection evidence、受防守的 backend `GetSelectionLassoSnapshot(...)` query、internal Avalonia lasso selection bridge、internal lasso gesture capture、public lasso pointer-mode activation、通过 `CreatePointerSelectionModeActions(...)` 提供的 hosted toolbar UX,以及 active lasso drag 的 transient visual feedback,但没有 whiteboard primitives 或 drawing tools。 | Phase 537 只关闭 toolbar activation ergonomics boundary;任何 whiteboard behavior claim 扩大前,后续候选仍保持 tracker-backed。 | +| Whiteboard / lasso / eraser / rectangle draw | Gap retained / public lasso selection and drawing-tool activation started | React Flow whiteboard docs 把 Freehand draw、Lasso selection、Eraser 和 Rectangle draw 列为 distinct advanced examples。AsterGraph 现在有 rectangle marquee selection、command projection evidence、受防守的 backend `GetSelectionLassoSnapshot(...)` query、internal Avalonia lasso selection bridge、internal lasso gesture capture、public lasso pointer-mode activation、通过 `CreatePointerSelectionModeActions(...)` 提供的 hosted toolbar UX、active lasso drag 的 transient visual feedback、internal whiteboard primitive skeleton/proof gates,以及通过 `NodeCanvas.WhiteboardDrawingMode`、`NodeCanvasWhiteboardDrawingMode.Rectangle`、`NodeCanvasWhiteboardDrawingMode.Freehand`、`CreateWhiteboardDrawingToolActions(...)`、`whiteboard-drawing.rectangle` 和 `whiteboard-drawing.freehand` 提供的 hosted drawing-tool activation,但没有 pointer gesture capture 或 runtime drawing creation。 | Phase 552 只关闭 hosted activation contract;任何 whiteboard behavior claim 扩大前,后续候选仍保持 tracker-backed。 | | Connection preview and validation | Present / guarded | Pending connection、compatible targets、validation snapshots、repair commands 和 `validation-prevent-cycle` fixture 已存在。Cycle prevention 由 canonical connection completion path 强制执行,并通过 pending snapshot 暴露 `GraphEditorPendingConnectionRejectionReason.WouldCreateCycle`。 | 继续守住 `RuntimeSession_CompleteConnection_RejectsDirectCycleWithStableReason`、`RuntimeSession_CompleteConnection_RejectsIndirectCycleThroughNormalCommandPath` 和 `RuntimeSession_TryExecuteCommand_RejectsCycleThroughConnectionsConnectRoute`。 | | Context menu | Present | Menu descriptors 与 hosted context menu plumbing 已存在。 | Keep guarded。 | | Undo/redo | Present | Session commands 与 history tests 覆盖正常 command semantics。 | Keep guarded。 | @@ -423,9 +427,9 @@ Phase 529 记录 whiteboard/lasso/eraser feasibility audit,对应 GitHub #181 | React Flow whiteboard reference | Gap-scoping reference | React Flow 的 whiteboard guide at https://reactflow.dev/learn/advanced-use/whiteboard 把 Freehand draw、Lasso selection、Eraser 和 Rectangle draw examples 与 core node/edge editor 分开。 | 该 reference 只用于界定比较范围;Phase 529 不导入 React Flow behavior,也不声明 complete whiteboard application。 | | Rectangle marquee selection | Present / guarded | `GetSelectionRectangleSnapshot`、`UpdateMarqueeSelection`、`Queries_GetSelectionRectangleSnapshot_ReturnsNodesAndConnectionsInRectangle`、`UpdateMarqueeSelection_WithFinalizeTrue_UsesBackendSelectionRectangleQuery`、`interaction-selection-marquee-route`、`selection-marquee-workbench` 和 `v079-selection-rectangle-route` 证明 rectangular selection evidence。 | 这是 axis-aligned rectangle/marquee selection,不是 lasso/freehand selection 或 drawing tool。 | | Multi-select and command projection | Present / guarded | `selection.select-all`、`selection.select-none`、`selection.invert`、`selection.delete` 和 `selection.transform.move` 暴露当前 selection 与 manipulation commands。 | Command projection 操作 graph selections;它不新增 eraser hit trails、drawn shape lifecycle 或 whiteboard-specific edit modes。 | -| Lasso/freehand selection | Public Avalonia lasso selection mode with hosted toolbar activation and transient visual feedback / whiteboard gap retained | `GetSelectionLassoSnapshot`、`GraphEditorSelectionLassoSnapshot`、`Queries_GetSelectionLassoSnapshot_ReturnsCenterContainedNodesAndConnections`、`Queries_GetSelectionLassoSnapshot_UsesNodeCenterInsteadOfBoundsIntersection`、`Queries_GetSelectionLassoSnapshot_WithOpenOrDegeneratePath_ReturnsDeterministicResults`、`UpdateLassoSelection_WithFinalizeTrue_UsesBackendSelectionLassoQuery`、`LassoSelection_RoutesThroughCanvasBridge_AndSelectsContainedNodes`、`NodeCanvasSelectionGestureKind.Lasso`、`TryBeginLassoSelection_WithLassoGestureKind_RecordsStartAndMoveAfterThreshold`、`HandleMoved_WhenCanvasSelectionUsesLassoGesture_RecordsLassoPointAndSkipsMarqueeUpdate`、`HandleReleased_AfterLassoSelection_FinalizesLassoSelectionAndResetsSession`、`NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RoutesThroughCanvasPointerHandlers_AndSelectsContainedNodes`、`UpdateLassoFeedback`、`ClearLassoFeedback`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`、`CreatePointerSelectionModeActions`、`pointer-mode.lasso-selection`、`PART_PointerModeLassoButton`、`HostedActions_PointerModeActionsSwitchNodeCanvasSelectionModeWithoutRuntimeCommandRoute` 和 `AuthoringToolsChrome_ProjectsPointerModeActionsThroughNodeCanvasSelectionMode` 证明 active-scope backend query、internal Avalonia bridge、internal gesture capture route、public pointer-mode activation route、hosted toolbar route 和 transient visual feedback route。该 query 把 freeform points 当作 closed polygon,用 center-point containment 选择 node,并在两个 endpoint node 都选中时选择 connection。 | 尚未实现 eraser behavior、drawing primitives、persistence、renderer layer、strict pixel baselines、retained API removal 或 full React Flow whiteboard parity。该边界仍是 not full React Flow whiteboard parity。 | +| Lasso/freehand selection | Public Avalonia lasso selection mode with hosted toolbar activation and transient visual feedback / whiteboard gap retained | `GetSelectionLassoSnapshot`、`GraphEditorSelectionLassoSnapshot`、`Queries_GetSelectionLassoSnapshot_ReturnsCenterContainedNodesAndConnections`、`Queries_GetSelectionLassoSnapshot_UsesNodeCenterInsteadOfBoundsIntersection`、`Queries_GetSelectionLassoSnapshot_WithOpenOrDegeneratePath_ReturnsDeterministicResults`、`UpdateLassoSelection_WithFinalizeTrue_UsesBackendSelectionLassoQuery`、`LassoSelection_RoutesThroughCanvasBridge_AndSelectsContainedNodes`、`NodeCanvasSelectionGestureKind.Lasso`、`TryBeginLassoSelection_WithLassoGestureKind_RecordsStartAndMoveAfterThreshold`、`HandleMoved_WhenCanvasSelectionUsesLassoGesture_RecordsLassoPointAndSkipsMarqueeUpdate`、`HandleReleased_AfterLassoSelection_FinalizesLassoSelectionAndResetsSession`、`NodeCanvasSelectionMode.Lasso`、`LassoSelectionMode_RoutesThroughCanvasPointerHandlers_AndSelectsContainedNodes`、`UpdateLassoFeedback`、`ClearLassoFeedback`、`LassoSelectionMode_RendersTransientFeedbackPathOnlyDuringDrag`、`CreatePointerSelectionModeActions`、`pointer-mode.lasso-selection`、`PART_PointerModeLassoButton`、`HostedActions_PointerModeActionsSwitchNodeCanvasSelectionModeWithoutRuntimeCommandRoute` 和 `AuthoringToolsChrome_ProjectsPointerModeActionsThroughNodeCanvasSelectionMode` 证明 active-scope backend query、internal Avalonia bridge、internal gesture capture route、public pointer-mode activation route、hosted toolbar route 和 transient visual feedback route。该 query 把 freeform points 当作 closed polygon,用 center-point containment 选择 node,并在两个 endpoint node 都选中时选择 connection。 | 尚未实现 eraser behavior、drawing primitive pointer capture、persistence、renderer layer、strict pixel baselines、retained API removal 或 full React Flow whiteboard parity。该边界仍是 not full React Flow whiteboard parity。 | | Eraser tool | Feasibility gate recorded / gap retained | `selection.delete`、`Delete Selection` descriptor 和 stock `selection-delete` placement 只证明 graph-selection deletion evidence。 | Phase 538 记录 graph-selection deletion is not an eraser cursor、collision trail 或 eraser-specific hit-testing path。尚未实现 no eraser cursor、no collision trail、no eraser-specific hit-testing、no drawing primitives、no persistence、no renderer rewrite、no strict pixel baseline enforcement、no retained API removal 或 no full whiteboard parity。 | -| Rectangle/freehand drawing | Model gate recorded / gap retained | Phase 539 / GitHub #201 / `avalonia-node-map-rwr` 将当前 reusable evidence 绑定到 `GraphDocument`、`GraphNode`、`GraphConnection`、`GraphEditorSelectionRectangleSnapshot`、`GraphEditorSelectionLassoSnapshot`、`GetSelectionRectangleSnapshot`、`GetSelectionLassoSnapshot`、`interaction-selection-marquee-route`、`selection-marquee-workbench` 和 `cookbook-interaction-lasso-screenshot-proof`。这些只证明 graph-scene、selection 和 visual-proof seams。 | 后续实现或声明 rectangle/freehand drawing behavior 前,必须由新的 API/model issue 定义 model identity、geometry、style/brush state、hit-testing/edit lifecycle、renderer projection、persistence/drawing persistence 和 Cookbook/screenshot evidence。Phase 539 不做 no runtime behavior changes、no public API changes、no Avalonia pointer coordinator edits、no renderer-layer changes、no persistence/schema changes、no screenshot manifest expansion,也不声明 no full React Flow whiteboard parity。 | +| Rectangle/freehand drawing | Public activation contract only / behavior gap retained | Phase 552 / GitHub #226 / `avalonia-node-map-718` 新增 hosted activation surface:`NodeCanvas.WhiteboardDrawingMode`、`NodeCanvasWhiteboardDrawingMode.Rectangle`、`NodeCanvasWhiteboardDrawingMode.Freehand`、`AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(...)`、`whiteboard-drawing.rectangle` 和 `whiteboard-drawing.freehand`。这些 actions 是 no runtime command id 的 `GraphEditorCommandSourceKind.Host` actions;`HostedActions_WhiteboardDrawingToolActionsSwitchNodeCanvasDrawingModeWithoutRuntimeCommandRoute` 和 `CommandRegistry_DoesNotExposeWhiteboardDrawingToolActivationAsRuntimeSessionCommand` 证明它们只设置 canvas activation state。 | 这不是 drawing behavior。Phase 552 不做 no pointer gesture capture、no runtime drawing creation、no toolbar UI implementation、no eraser behavior、no persistence/schema changes、no drawing persistence、no renderer rewrite、no screenshot manifest expansion 或 no full React Flow whiteboard parity。 | | Whiteboard persistence/render layer | Readiness gate recorded / gap retained | Phase 540 / GitHub #203 / `avalonia-node-map-91b` 将当前 reusable evidence 绑定到 `GraphDocumentSerializer`、`GraphDocumentCompatibility`、`CurrentSchemaVersion`、`GraphWorkspaceService`、`GraphEditorSceneSnapshot`、`NodeCanvasConnectionSceneRenderer`、`CookbookScreenshotGateRoutes.json` 和 `CookbookShellVisualGateStates.json`。这些只证明 graph document/workspace persistence、graph-scene projection、committed connection rendering 和 screenshot fixture coverage。 | 后续 whiteboard annotation persistence 与 whiteboard annotation rendering 需要新的 implementation issue 定义 persistence/schema contract、renderer projection contract、hit-testing/edit lifecycle、screenshot/Cookbook evidence、migration policy 和 explicit non-goals。Phase 540 不做 no runtime behavior changes、no public API changes、no persistence/schema changes、no renderer-layer changes、no screenshot manifest expansion,也不声明 no full React Flow whiteboard parity。 | ## 已完成的 Phase 0 Issue Wave @@ -585,6 +589,8 @@ Phase 550 记录 whiteboard primitive Cookbook screenshot implementation gate, Phase 551 记录 post-Phase-550 whiteboard authoring interaction queue refresh,通过 GitHub #225 / `avalonia-node-map-3c9`。它只添加 `WHITEBOARD_AUTHORING_INTERACTION_QUEUE_REFRESH` 和下一批 tracker-backed authoring split:public drawing tool activation、pointer gesture capture、authoring toolbar/Cookbook UX、eraser-on-primitive behavior 和 persistence implementation decision。它不实现 runtime behavior、public API、pointer coordinator changes、toolbar UI、eraser behavior、persistence/schema changes、renderer changes、screenshot manifest expansion、UI redesign 或 full React Flow whiteboard parity。 +Phase 552 记录 whiteboard drawing tool public activation contract,通过 GitHub #226 / `avalonia-node-map-718`。它只添加 `NodeCanvas.WhiteboardDrawingMode`、`NodeCanvasWhiteboardDrawingMode.Rectangle`、`NodeCanvasWhiteboardDrawingMode.Freehand`、`CreateWhiteboardDrawingToolActions(...)`、`whiteboard-drawing.rectangle` 和 `whiteboard-drawing.freehand` 作为 hosted activation。Actions 是 host-source actions,且 no runtime command id;不新增 pointer gesture capture、runtime drawing creation、toolbar UI implementation、eraser behavior、persistence/schema changes、renderer changes、screenshot manifest expansion 或 full React Flow whiteboard parity。 + | 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。 | @@ -733,4 +739,5 @@ Phase 551 记录 post-Phase-550 whiteboard authoring interaction queue refresh - Phase 549 是 GitHub #221 / `avalonia-node-map-3l6`;它只用 internal `GraphWhiteboardPrimitivePersistenceDecision` policy contract 记录 `WHITEBOARD_PRIMITIVE_PERSISTENCE_DECISION_GATE`。GraphDocument schema changes、schema version bumps、workspace persistence behavior changes、public drawing APIs、pointer coordinators、toolbar/tool activation、eraser behavior、screenshot manifest expansion、UI redesign、retained API removal 和 full React Flow whiteboard parity 都保持 out of scope。 - Phase 550 是 GitHub #223 / `avalonia-node-map-wjr`;它只用 `cookbook-whiteboard-primitive-screenshot-proof`、`shell-cookbook-whiteboard-primitive-screenshot-proof`、route metadata、shell state、non-overlap visual proof 和 `WHITEBOARD_PRIMITIVE_SCREENSHOT_GATE_OK` 记录 `WHITEBOARD_PRIMITIVE_COOKBOOK_SCREENSHOT_GATE`。Public drawing APIs、pointer coordinators、toolbar work、eraser behavior、persisted whiteboard primitive state、GraphDocument schema changes、renderer rewrites、UI redesign、retained API removal 和 full React Flow whiteboard parity 都保持 out of scope。 - Phase 551 是 GitHub #225 / `avalonia-node-map-3c9`;它记录 `WHITEBOARD_AUTHORING_INTERACTION_QUEUE_REFRESH`,并把下一批 whiteboard authoring work 拆成 Phase 552 public drawing tool activation、Phase 553 pointer gesture capture、Phase 554 authoring toolbar/Cookbook UX、Phase 555 eraser-on-primitive behavior 和 Phase 556 persistence implementation decision,但不实现这些行为。 +- Phase 552 是 GitHub #226 / `avalonia-node-map-718`;它通过 `NodeCanvas.WhiteboardDrawingMode`、`NodeCanvasWhiteboardDrawingMode.Rectangle`、`NodeCanvasWhiteboardDrawingMode.Freehand`、`CreateWhiteboardDrawingToolActions(...)`、`whiteboard-drawing.rectangle` 和 `whiteboard-drawing.freehand` 记录 whiteboard drawing tool public activation contract,不新增 runtime command id、pointer gesture capture、runtime drawing creation、toolbar UI implementation、eraser behavior、persistence/schema changes、renderer changes、screenshot manifest expansion 或 full React Flow 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、Phase 539、Phase 540、Phase 541、Phase 546、Phase 550 和 Phase 551 都不修改产品代码;除非 focused test 证明存在具体 missing contract。 diff --git a/docs/zh-CN/public-api-inventory.md b/docs/zh-CN/public-api-inventory.md index dac78102..7eed212c 100644 --- a/docs/zh-CN/public-api-inventory.md +++ b/docs/zh-CN/public-api-inventory.md @@ -33,7 +33,7 @@ | `AsterGraph.Abstractions` | 节点定义、端口定义、provider / plugin-facing contract、identifier、metadata DTO,以及只产出这些 DTO 的 thin definition builder。 | 无。 | 无。 | 当前不作为主支持层级发布。 | 未通过 package docs 暴露的实现 helper。 | | `AsterGraph.Core` | 图文档、当前 schema 的序列化模型契约、显式 legacy import、group container/collapse semantics、包含 `GraphNodeSurfaceState.RotationDegrees` 的持久化节点 surface state、兼容规则输入、thin implicit-conversion rule builder,以及 editor/session 组合使用的共享数据类型。 | 无。 | `GraphDocumentSerializer.ImportLegacy(...)` 是旧 payload 的 bounded import / migration 入口。 | v1 primary surface 中没有。已退役 surface 见 v1 removal policy。 | Core internals 与持久化实现细节。 | | `AsterGraph.Editor` | `AsterGraphEditorFactory.CreateSession(...)`、`IGraphEditorSession`、`IGraphEditorCommands`、`IGraphEditorQueries`、包含 `GraphEditorNodeSurfaceSnapshot.RotationDegrees` 的 DTO / snapshot 查询、diagnostics、automation、validation snapshots、runtime overlay snapshots/providers、layout plan snapshots/providers、viewport visible scene projection snapshots/projector、hierarchy/group snapshots,包括 collapsed-boundary connection projection、navigator/outline snapshots、plugin discovery / inspection、export services,包括 raster export progress/cancel options。 | `AsterGraphEditorFactory.Create(...)` 是 hosted 组合 helper,但仍返回 retained facade。 | `GraphEditorViewModel`、`GraphEditorViewModel.Session`、迁移宿主使用的 retained menu / context-menu hook,以及 `NodeViewModel.RotationDegrees`。 | v1 primary surface 中没有。已退役 surface 见 v1 removal policy。 | `Runtime.Internal`、`Kernel.Internal`、projection/apply internals、proof-only helper。 | -| `AsterGraph.Avalonia` | canonical editor/session route 上的 adapter 投影,以及复用同一 runtime owner 的 hosted factory。 | `AsterGraphAvaloniaViewFactory.Create(...)`、`AsterGraphCanvasViewFactory`、`AsterGraphInspectorViewFactory`、`AsterGraphMiniMapViewFactory`、`AsterGraphControls`、`NodeToolbar`、`EdgeToolbar`、`NodeResizer`、`AsterGraphPanel`、`AsterGraphHostBuilder`、`AsterGraphWorkbenchOptions`、`AsterGraphBuiltInComponentCatalog`、hosted pointer selection mode actions、hosted workbench layout / panel-state options,以及 hosted performance policy budget markers。 | 仍使用 retained editor facade 的宿主可嵌入 `GraphEditorView`。 | 仅限把现有宿主桥接到 canonical route 的 adapter-specific compatibility glue。 | Control internals、templates、interaction session internals、visual-only implementation details。 | +| `AsterGraph.Avalonia` | canonical editor/session route 上的 adapter 投影,以及复用同一 runtime owner 的 hosted factory。 | `AsterGraphAvaloniaViewFactory.Create(...)`、`AsterGraphCanvasViewFactory`、`AsterGraphInspectorViewFactory`、`AsterGraphMiniMapViewFactory`、`AsterGraphControls`、`NodeToolbar`、`EdgeToolbar`、`NodeResizer`、`AsterGraphPanel`、`AsterGraphHostBuilder`、`AsterGraphWorkbenchOptions`、`AsterGraphBuiltInComponentCatalog`、hosted pointer selection mode actions、hosted whiteboard drawing tool activation actions、hosted workbench layout / panel-state options,以及 hosted performance policy budget markers。 | 仍使用 retained editor facade 的宿主可嵌入 `GraphEditorView`。 | 仅限把现有宿主桥接到 canonical route 的 adapter-specific compatibility glue。 | Control internals、templates、interaction session internals、visual-only implementation details。 | ## 路线映射 @@ -45,6 +45,7 @@ | Phase 530 lasso selection query route | Stable canonical | `IGraphEditorQueries.GetSelectionLassoSnapshot(...)`、`GraphEditorSelectionLassoSnapshot`、`GraphEditorSelectionLassoSnapshot.NodeIds`、`GraphEditorSelectionLassoSnapshot.ConnectionIds` | 面向自定义 UI 的 backend/editor lasso query route;宿主自行捕获 freeform polygon points 后调用。该 query 仍走 session route,读取 active-scope graph state,用 center-point polygon containment 选择 node,并且只在 connection 两端 node 都被选中时选择 connection。不新增 Avalonia gesture capture、pointer-mode state、drawing primitives、eraser behavior、persistence、renderer changes、screenshot rows 或 full React Flow whiteboard parity。 | | Phase 533 lasso pointer-mode route | Supported hosted helper | `NodeCanvas.SelectionMode`、`NodeCanvasSelectionMode.Marquee`、`NodeCanvasSelectionMode.Lasso` | stock canvas lasso selection 的 hosted Avalonia activation path。Marquee 仍是默认;宿主可为 empty-canvas selection drag 选择 lasso。它复用现有 backend lasso query 与 Avalonia lasso bridge,不新增 toolbar UX、visual gesture capture、screenshot rows、eraser behavior、drawing primitives、persistence、renderer changes 或 full React Flow whiteboard parity。 | | Phase 537 lasso toolbar ergonomics route | Supported hosted helper | `AsterGraphAuthoringToolActionFactory.CreatePointerSelectionModeActions(...)`、`pointer-mode.marquee-selection`、`pointer-mode.lasso-selection`、`NodeCanvas.SelectionMode`、`NodeCanvasSelectionMode.Lasso` | 用 hosted toolbar action projection 在 stock canvas 的 marquee 与 lasso selection 之间切换。这个 helper 只写现有 `NodeCanvas.SelectionMode` property,返回无 runtime command id 的 host-source actions,不新增第二套 selection model、eraser behavior、drawing primitives、persistence、renderer changes、strict pixel baselines、retained API removal 或 full React Flow whiteboard parity。 | +| Phase 552 whiteboard drawing tool activation route | Supported hosted helper | `NodeCanvas.WhiteboardDrawingMode`、`NodeCanvasWhiteboardDrawingMode.None`、`NodeCanvasWhiteboardDrawingMode.Rectangle`、`NodeCanvasWhiteboardDrawingMode.Freehand`、`AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(...)`、`whiteboard-drawing.rectangle`、`whiteboard-drawing.freehand` | rectangle/freehand whiteboard drawing tools 的 hosted activation contract。这个 helper 只写 `NodeCanvas.WhiteboardDrawingMode` property,返回 no runtime command id 的 host-source actions,不新增 pointer gesture capture、runtime drawing creation、toolbar UI implementation、eraser behavior、persistence/schema changes、renderer changes、screenshot manifest expansion、retained API removal 或 full React Flow whiteboard parity。 | | Shipped Avalonia UI | Supported hosted helper | `AsterGraphEditorFactory.Create(...)`、`AsterGraphAvaloniaViewFactory.Create(...)` | 使用同一个 runtime owner,不是第二套 runtime model。 | | Thin hosted builder | Supported hosted helper | `AsterGraphHostBuilder.Create(...).UseDefaultWorkbench().UseLayoutProvider(...).BuildAvaloniaView()` | 减少常见 Avalonia 组合样板,并委托给 canonical factories;这些 pass-through 暴露现有 `AsterGraphEditorOptions` seam,包括 host-owned synchronous layout provider seam;`AsterGraphWorkbenchOptions` 只控制 stock hosted chrome。 | | Built-in component catalog | Supported hosted helper | `AsterGraphBuiltInComponentCatalog.Components`、`AsterGraphBuiltInComponentCatalog.TryGet(...)`、`AsterGraphBuiltInComponentDescriptor`、`AsterGraphBuiltInComponentStatus`、`AsterGraphControls`、`NodeToolbar`、`EdgeToolbar`、`NodeResizer`、`AsterGraphPanel` | Phase 3 的内置 component track discovery map。Public 条目指向现有 canvas、minimap、background-grid、inspector、controls-panel、`controls`、command-tool-projection、`node-toolbar`、`edge-toolbar`、`node-resizer` 和 `panel` surface。`AsterGraphControls` 是基于 `IGraphEditorSession.Commands.TryExecuteCommand(...)` 的薄 Avalonia viewport 控件,覆盖 zoom、fit 和 reset action;`NodeToolbar` 与 `EdgeToolbar` 是基于 `AsterGraphAuthoringToolActionFactory.CreateNodeActions(...)` 和 `CreateConnectionActions(...)` 的薄 Avalonia 控件;`NodeResizer` 是基于 `IGraphEditorSession.Commands.TrySetNodeSize(...)` 的薄 Avalonia 控件;`AsterGraphPanel` 是宿主自管内容的定位 overlay primitive。 | diff --git a/eng/public-api-baseline.txt b/eng/public-api-baseline.txt index 104066f4..6cebd570 100644 --- a/eng/public-api-baseline.txt +++ b/eng/public-api-baseline.txt @@ -346,8 +346,12 @@ F:AsterGraph.Avalonia.Controls.NodeCanvas.NodeVisualPresenterProperty:Avalonia.S F:AsterGraph.Avalonia.Controls.NodeCanvas.ResizeFeedbackPolicyProperty:Avalonia.StyledProperty F:AsterGraph.Avalonia.Controls.NodeCanvas.SelectionModeProperty:Avalonia.StyledProperty F:AsterGraph.Avalonia.Controls.NodeCanvas.ViewModelProperty:Avalonia.StyledProperty +F:AsterGraph.Avalonia.Controls.NodeCanvas.WhiteboardDrawingModeProperty:Avalonia.StyledProperty F:AsterGraph.Avalonia.Controls.NodeCanvasSelectionMode.Lasso:AsterGraph.Avalonia.Controls.NodeCanvasSelectionMode F:AsterGraph.Avalonia.Controls.NodeCanvasSelectionMode.Marquee:AsterGraph.Avalonia.Controls.NodeCanvasSelectionMode +F:AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode.Freehand:AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode +F:AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode.None:AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode +F:AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode.Rectangle:AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode F:AsterGraph.Avalonia.Controls.NodeParameterEditorHost.NodeParameterEditorRegistryProperty:Avalonia.StyledProperty F:AsterGraph.Avalonia.Controls.NodeParameterEditorHost.ParameterProperty:Avalonia.StyledProperty F:AsterGraph.Avalonia.Controls.NodeParameterEditorHost.TemplateKeyProperty:Avalonia.StyledProperty @@ -724,6 +728,7 @@ M:AsterGraph.Avalonia.Hosting.AsterGraphAuthoringToolActionFactory.CreateConnect M:AsterGraph.Avalonia.Hosting.AsterGraphAuthoringToolActionFactory.CreateNodeActions(AsterGraph.Editor.Runtime.IGraphEditorSession,System.String,System.Collections.Generic.IReadOnlyList,System.String):System.Collections.Generic.IReadOnlyList M:AsterGraph.Avalonia.Hosting.AsterGraphAuthoringToolActionFactory.CreatePointerSelectionModeActions(AsterGraph.Avalonia.Controls.NodeCanvas):System.Collections.Generic.IReadOnlyList M:AsterGraph.Avalonia.Hosting.AsterGraphAuthoringToolActionFactory.CreateSelectionActions(AsterGraph.Editor.Runtime.IGraphEditorSession):System.Collections.Generic.IReadOnlyList +M:AsterGraph.Avalonia.Hosting.AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(AsterGraph.Avalonia.Controls.NodeCanvas):System.Collections.Generic.IReadOnlyList M:AsterGraph.Avalonia.Hosting.AsterGraphAvaloniaViewFactory.Create(AsterGraph.Avalonia.Hosting.AsterGraphAvaloniaViewOptions):AsterGraph.Avalonia.Controls.GraphEditorView M:AsterGraph.Avalonia.Hosting.AsterGraphAvaloniaViewOptions.$():AsterGraph.Avalonia.Hosting.AsterGraphAvaloniaViewOptions M:AsterGraph.Avalonia.Hosting.AsterGraphAvaloniaViewOptions.Equals(AsterGraph.Avalonia.Hosting.AsterGraphAvaloniaViewOptions):System.Boolean @@ -2514,6 +2519,7 @@ P:AsterGraph.Avalonia.Controls.NodeCanvas.NodeVisualPresenter:AsterGraph.Avaloni P:AsterGraph.Avalonia.Controls.NodeCanvas.ResizeFeedbackPolicy:AsterGraph.Avalonia.Presentation.IGraphResizeFeedbackPolicy:get,set P:AsterGraph.Avalonia.Controls.NodeCanvas.SelectionMode:AsterGraph.Avalonia.Controls.NodeCanvasSelectionMode:get,set P:AsterGraph.Avalonia.Controls.NodeCanvas.ViewModel:AsterGraph.Editor.ViewModels.GraphEditorViewModel:get,set +P:AsterGraph.Avalonia.Controls.NodeCanvas.WhiteboardDrawingMode:AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode:get,set P:AsterGraph.Avalonia.Controls.NodeParameterEditorHost.NodeParameterEditorRegistry:AsterGraph.Avalonia.Presentation.INodeParameterEditorRegistry:get,set P:AsterGraph.Avalonia.Controls.NodeParameterEditorHost.Parameter:AsterGraph.Editor.ViewModels.NodeParameterViewModel:get,set P:AsterGraph.Avalonia.Controls.NodeParameterEditorHost.TemplateKey:System.String:get,set @@ -4161,6 +4167,7 @@ T:enum AsterGraph.Abstractions.Definitions.ParameterEditorKind T:enum AsterGraph.Avalonia.Controls.AsterGraphPanelPosition T:enum AsterGraph.Avalonia.Controls.GraphEditorViewChromeMode T:enum AsterGraph.Avalonia.Controls.NodeCanvasSelectionMode +T:enum AsterGraph.Avalonia.Controls.NodeCanvasWhiteboardDrawingMode T:enum AsterGraph.Avalonia.Hosting.AsterGraphBuiltInComponentStatus T:enum AsterGraph.Avalonia.Hosting.AsterGraphWorkbenchLayoutPreset T:enum AsterGraph.Avalonia.Hosting.AsterGraphWorkbenchPerformanceMode diff --git a/src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs b/src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs index 2f6c1c39..184a1806 100644 --- a/src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs +++ b/src/AsterGraph.Avalonia/Controls/NodeCanvas.axaml.cs @@ -66,6 +66,14 @@ public partial class NodeCanvas : UserControl nameof(SelectionMode), NodeCanvasSelectionMode.Marquee); + /// + /// Controls the host-selected whiteboard drawing tool without starting pointer capture. + /// + public static readonly StyledProperty WhiteboardDrawingModeProperty = + AvaloniaProperty.Register( + nameof(WhiteboardDrawingMode), + NodeCanvasWhiteboardDrawingMode.None); + /// /// 控制节点可视树替换的展示器。 /// @@ -216,6 +224,15 @@ public NodeCanvasSelectionMode SelectionMode set => SetValue(SelectionModeProperty, value); } + /// + /// Host-selected whiteboard drawing tool. This activation state does not create primitives by itself. + /// + public NodeCanvasWhiteboardDrawingMode WhiteboardDrawingMode + { + get => GetValue(WhiteboardDrawingModeProperty); + set => SetValue(WhiteboardDrawingModeProperty, value); + } + internal bool AttachPlatformSeams { get; set; } = true; /// diff --git a/src/AsterGraph.Avalonia/Controls/NodeCanvasWhiteboardDrawingMode.cs b/src/AsterGraph.Avalonia/Controls/NodeCanvasWhiteboardDrawingMode.cs new file mode 100644 index 00000000..3a0dcd1e --- /dev/null +++ b/src/AsterGraph.Avalonia/Controls/NodeCanvasWhiteboardDrawingMode.cs @@ -0,0 +1,22 @@ +namespace AsterGraph.Avalonia.Controls; + +/// +/// Defines the host-selected whiteboard drawing tool activation state for the canvas. +/// +public enum NodeCanvasWhiteboardDrawingMode +{ + /// + /// No whiteboard drawing tool is active. + /// + None, + + /// + /// Rectangle whiteboard drawing is selected for future pointer capture. + /// + Rectangle, + + /// + /// Freehand whiteboard drawing is selected for future pointer capture. + /// + Freehand, +} diff --git a/src/AsterGraph.Avalonia/Hosting/AsterGraphAuthoringToolActionFactory.cs b/src/AsterGraph.Avalonia/Hosting/AsterGraphAuthoringToolActionFactory.cs index 01dd7300..4f737302 100644 --- a/src/AsterGraph.Avalonia/Hosting/AsterGraphAuthoringToolActionFactory.cs +++ b/src/AsterGraph.Avalonia/Hosting/AsterGraphAuthoringToolActionFactory.cs @@ -26,6 +26,27 @@ public static IReadOnlyList CreatePointerSelec ]; } + public static IReadOnlyList CreateWhiteboardDrawingToolActions(NodeCanvas canvas) + { + ArgumentNullException.ThrowIfNull(canvas); + + return + [ + CreateWhiteboardDrawingToolAction( + canvas, + "whiteboard-drawing.rectangle", + "Rectangle Drawing Tool", + "Activate rectangle whiteboard drawing for a future pointer-capture route.", + NodeCanvasWhiteboardDrawingMode.Rectangle), + CreateWhiteboardDrawingToolAction( + canvas, + "whiteboard-drawing.freehand", + "Freehand Drawing Tool", + "Activate freehand whiteboard drawing for a future pointer-capture route.", + NodeCanvasWhiteboardDrawingMode.Freehand), + ]; + } + public static IReadOnlyList CreateSelectionActions(IGraphEditorSession session) { ArgumentNullException.ThrowIfNull(session); @@ -159,4 +180,26 @@ private static AsterGraphHostedActionDescriptor CreatePointerSelectionModeAction canvas.SelectionMode = mode; return true; }); + + private static AsterGraphHostedActionDescriptor CreateWhiteboardDrawingToolAction( + NodeCanvas canvas, + string id, + string title, + string recoveryHint, + NodeCanvasWhiteboardDrawingMode mode) + => AsterGraphHostedActionFactory.CreateHostAction( + new GraphEditorCommandDescriptorSnapshot( + id, + title, + "whiteboard-drawing", + null, + null, + GraphEditorCommandSourceKind.Host, + isEnabled: true, + recoveryHint: recoveryHint), + () => + { + canvas.WhiteboardDrawingMode = mode; + return true; + }); } diff --git a/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs b/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs index 6accb1d2..dfde709a 100644 --- a/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs +++ b/tests/AsterGraph.Demo.Tests/ReactFlowParityRoadmapDocsTests.cs @@ -1546,6 +1546,58 @@ public void ParityRoadmapDocs_RecordPhase551WhiteboardAuthoringQueueRefreshInBot Assert.Contains("Phase 551 记录 post-Phase-550 whiteboard authoring interaction queue refresh", chineseParity, StringComparison.Ordinal); } + [Fact] + public void ParityRoadmapDocs_RecordPhase552WhiteboardDrawingToolActivationContractInBothLocales() + { + var englishParity = ReadRepoFile("docs/en/phase-0-reactflow-parity-audit.md"); + var chineseParity = ReadRepoFile("docs/zh-CN/phase-0-reactflow-parity-audit.md"); + var englishInventory = ReadRepoFile("docs/en/public-api-inventory.md"); + var chineseInventory = ReadRepoFile("docs/zh-CN/public-api-inventory.md"); + var englishRecipe = ReadRepoFile("docs/en/authoring-surface-recipe.md"); + var chineseRecipe = ReadRepoFile("docs/zh-CN/authoring-surface-recipe.md"); + + foreach (var contents in new[] { englishParity, chineseParity }) + { + Assert.Contains("Phase 552", contents, StringComparison.Ordinal); + Assert.Contains("GitHub #226", contents, StringComparison.Ordinal); + Assert.Contains("avalonia-node-map-718", contents, StringComparison.Ordinal); + Assert.Contains("whiteboard drawing tool public activation contract", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("NodeCanvas.WhiteboardDrawingMode", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvasWhiteboardDrawingMode.Rectangle", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvasWhiteboardDrawingMode.Freehand", contents, StringComparison.Ordinal); + Assert.Contains("CreateWhiteboardDrawingToolActions", contents, StringComparison.Ordinal); + Assert.Contains("whiteboard-drawing.rectangle", contents, StringComparison.Ordinal); + Assert.Contains("whiteboard-drawing.freehand", contents, StringComparison.Ordinal); + Assert.Contains("GraphEditorCommandSourceKind.Host", contents, StringComparison.Ordinal); + Assert.Contains("no runtime command id", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no pointer gesture capture", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no runtime drawing creation", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no toolbar UI implementation", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no eraser behavior", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no persistence/schema changes", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no renderer rewrite", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no screenshot manifest expansion", contents, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no full React Flow whiteboard parity", contents, StringComparison.OrdinalIgnoreCase); + Assert.DoesNotContain("whiteboard drawing parity is supported", contents, StringComparison.OrdinalIgnoreCase); + } + + foreach (var contents in new[] { englishInventory, chineseInventory, englishRecipe, chineseRecipe }) + { + Assert.Contains("CreateWhiteboardDrawingToolActions", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvas.WhiteboardDrawingMode", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvasWhiteboardDrawingMode.Rectangle", contents, StringComparison.Ordinal); + Assert.Contains("NodeCanvasWhiteboardDrawingMode.Freehand", contents, StringComparison.Ordinal); + Assert.Contains("whiteboard-drawing.rectangle", contents, StringComparison.Ordinal); + Assert.Contains("whiteboard-drawing.freehand", contents, StringComparison.Ordinal); + Assert.Contains("no runtime command id", contents, StringComparison.OrdinalIgnoreCase); + } + + AssertPostPhase551Queue(ExtractIssueWaveTable(englishParity)); + AssertPostPhase551Queue(ExtractIssueWaveTable(chineseParity)); + Assert.Contains("Phase 552 records the whiteboard drawing tool public activation contract", englishParity, StringComparison.Ordinal); + Assert.Contains("Phase 552 记录 whiteboard drawing tool public activation contract", chineseParity, StringComparison.Ordinal); + } + [Fact] public void ParityRoadmapDocs_RecordPhase501PostPhase500QueueRefreshInBothLocales() { @@ -1943,7 +1995,7 @@ private static void AssertWhiteboardLassoEraserFeasibilityAudit(string table) Assert.Contains("| Multi-select and command projection | Present / guarded |", table, StringComparison.Ordinal); Assert.Contains("| Lasso/freehand selection | Public Avalonia lasso selection mode with hosted toolbar activation and transient visual feedback / whiteboard gap retained |", table, StringComparison.Ordinal); Assert.Contains("| Eraser tool | Feasibility gate recorded / gap retained |", table, StringComparison.Ordinal); - Assert.Contains("| Rectangle/freehand drawing | Model gate recorded / gap retained |", table, StringComparison.Ordinal); + Assert.Contains("| Rectangle/freehand drawing | Public activation contract only / behavior gap retained |", table, StringComparison.Ordinal); Assert.Contains("| Whiteboard persistence/render layer | Readiness gate recorded / gap retained |", table, StringComparison.Ordinal); Assert.Contains("https://reactflow.dev/learn/advanced-use/whiteboard", table, StringComparison.Ordinal); Assert.Contains("Freehand draw", table, StringComparison.Ordinal); @@ -1993,6 +2045,15 @@ private static void AssertWhiteboardLassoEraserFeasibilityAudit(string table) Assert.Contains("public pointer-mode activation route", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("transient visual feedback", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("hosted toolbar", table, StringComparison.OrdinalIgnoreCase); + Assert.Contains("NodeCanvas.WhiteboardDrawingMode", table, StringComparison.Ordinal); + Assert.Contains("NodeCanvasWhiteboardDrawingMode.Rectangle", table, StringComparison.Ordinal); + Assert.Contains("NodeCanvasWhiteboardDrawingMode.Freehand", table, StringComparison.Ordinal); + Assert.Contains("CreateWhiteboardDrawingToolActions", table, StringComparison.Ordinal); + Assert.Contains("whiteboard-drawing.rectangle", table, StringComparison.Ordinal); + Assert.Contains("whiteboard-drawing.freehand", table, StringComparison.Ordinal); + Assert.Contains("no runtime command id", table, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no pointer gesture capture", table, StringComparison.OrdinalIgnoreCase); + Assert.Contains("no runtime drawing creation", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("drawing persistence", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("renderer layer", table, StringComparison.OrdinalIgnoreCase); Assert.Contains("screenshot manifest expansion", table, StringComparison.OrdinalIgnoreCase); diff --git a/tests/AsterGraph.Editor.Tests/GraphEditorActionContributionContractTests.cs b/tests/AsterGraph.Editor.Tests/GraphEditorActionContributionContractTests.cs index 23082043..00775e10 100644 --- a/tests/AsterGraph.Editor.Tests/GraphEditorActionContributionContractTests.cs +++ b/tests/AsterGraph.Editor.Tests/GraphEditorActionContributionContractTests.cs @@ -138,6 +138,49 @@ public void HostedActions_PointerModeActionsSwitchNodeCanvasSelectionModeWithout Assert.Equal(NodeCanvasSelectionMode.Marquee, canvas.SelectionMode); } + [AvaloniaFact] + public void HostedActions_WhiteboardDrawingToolActionsSwitchNodeCanvasDrawingModeWithoutRuntimeCommandRoute() + { + var canvas = new NodeCanvas(); + + var actions = AsterGraphAuthoringToolActionFactory.CreateWhiteboardDrawingToolActions(canvas); + + var rectangleAction = Assert.Single(actions, action => action.Id == "whiteboard-drawing.rectangle"); + var freehandAction = Assert.Single(actions, action => action.Id == "whiteboard-drawing.freehand"); + + Assert.Equal("Rectangle Drawing Tool", rectangleAction.Title); + Assert.Equal("Freehand Drawing Tool", freehandAction.Title); + Assert.Equal(GraphEditorCommandSourceKind.Host, rectangleAction.CommandSource); + Assert.Equal(GraphEditorCommandSourceKind.Host, freehandAction.CommandSource); + Assert.Null(rectangleAction.CommandId); + Assert.Null(freehandAction.CommandId); + Assert.Equal(NodeCanvasSelectionMode.Marquee, canvas.SelectionMode); + Assert.Equal(NodeCanvasWhiteboardDrawingMode.None, canvas.WhiteboardDrawingMode); + + Assert.True(freehandAction.TryExecute()); + + Assert.Equal(NodeCanvasSelectionMode.Marquee, canvas.SelectionMode); + Assert.Equal(NodeCanvasWhiteboardDrawingMode.Freehand, canvas.WhiteboardDrawingMode); + + Assert.True(rectangleAction.TryExecute()); + + Assert.Equal(NodeCanvasSelectionMode.Marquee, canvas.SelectionMode); + Assert.Equal(NodeCanvasWhiteboardDrawingMode.Rectangle, canvas.WhiteboardDrawingMode); + } + + [Fact] + public void CommandRegistry_DoesNotExposeWhiteboardDrawingToolActivationAsRuntimeSessionCommand() + { + var session = CreateSession(); + + var commandIds = session.Queries.GetCommandRegistry() + .Select(entry => entry.CommandId) + .ToHashSet(StringComparer.Ordinal); + + Assert.DoesNotContain("whiteboard-drawing.rectangle", commandIds); + Assert.DoesNotContain("whiteboard-drawing.freehand", commandIds); + } + private static void AssertPlacement( GraphEditorCommandRegistryEntrySnapshot entry, GraphEditorCommandSurfaceKind surfaceKind,