pmx-canvas 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/.github/extensions/pmx-canvas/extension.mjs +25 -172
- package/.github/extensions/pmx-canvas/mcp-bootstrap.ts +198 -0
- package/.github/extensions/pmx-canvas/runtime.d.mts +41 -0
- package/.github/extensions/pmx-canvas/runtime.json +3 -0
- package/.github/extensions/pmx-canvas/runtime.mjs +194 -0
- package/.github/extensions/pmx-canvas/start-mcp.mjs +51 -0
- package/.github/extensions/pmx-canvas/tools.json +2560 -0
- package/CHANGELOG.md +60 -0
- package/Readme.md +44 -16
- package/dist/canvas/fonts/OFL.txt +93 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
- package/dist/canvas/global.css +844 -710
- package/dist/canvas/index.js +92 -90
- package/dist/canvas/mcp-app.html +7878 -0
- package/dist/canvas/mermaid-entry.js +1 -1
- package/dist/canvas/surface-theme.css +151 -46
- package/dist/json-render/index.css +1 -1
- package/dist/json-render/index.js +55 -55
- package/dist/types/cli/commands/record.d.ts +1 -1
- package/dist/types/client/canvas/ChoiceMenu.d.ts +13 -0
- package/dist/types/client/canvas/NodeContextMark.d.ts +24 -0
- package/dist/types/client/canvas/SessionReceipt.d.ts +3 -1
- package/dist/types/client/icons.d.ts +1 -2
- package/dist/types/client/mcp-app/index.d.ts +1 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +2 -1
- package/dist/types/client/nodes/ax-node-actions.d.ts +2 -2
- package/dist/types/client/nodes/markdown-sanitize.d.ts +2 -0
- package/dist/types/client/nodes/use-workbench-asset.d.ts +2 -0
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -0
- package/dist/types/client/state/attention-store.d.ts +0 -5
- package/dist/types/client/state/canvas-store.d.ts +2 -0
- package/dist/types/client/state/context-status-store.d.ts +53 -0
- package/dist/types/client/state/intent-bridge.d.ts +4 -1
- package/dist/types/client/state/near-pin-store.d.ts +16 -0
- package/dist/types/client/state/presence-store.d.ts +5 -0
- package/dist/types/client/state/session-store.d.ts +33 -3
- package/dist/types/client/state/workbench-transport.d.ts +16 -0
- package/dist/types/client/theme/tokens.d.ts +1 -3
- package/dist/types/client/types.d.ts +9 -0
- package/dist/types/json-render/renderer/index.d.ts +2 -1
- package/dist/types/json-render/server.d.ts +3 -1
- package/dist/types/mcp/app-server.d.ts +3 -0
- package/dist/types/mcp/canvas-access.d.ts +1 -1
- package/dist/types/mcp/canvas-app.d.ts +4 -0
- package/dist/types/mcp/workbench-app.d.ts +4 -0
- package/dist/types/server/agent-presence.d.ts +1 -1
- package/dist/types/server/ax-state.d.ts +1 -1
- package/dist/types/server/canvas-db.d.ts +8 -0
- package/dist/types/server/canvas-geometry.d.ts +25 -0
- package/dist/types/server/canvas-operations.d.ts +1 -11
- package/dist/types/server/canvas-state.d.ts +25 -3
- package/dist/types/server/context-brief.d.ts +8 -1
- package/dist/types/server/context-reads.d.ts +30 -7
- package/dist/types/server/index.d.ts +6 -1
- package/dist/types/server/mcp-app-runtime.d.ts +1 -0
- package/dist/types/server/operations/http.d.ts +2 -0
- package/dist/types/server/operations/invoker.d.ts +4 -2
- package/dist/types/server/operations/ops/nodes.d.ts +0 -6
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/spatial-analysis.d.ts +2 -0
- package/dist/types/shared/agent-context.d.ts +22 -0
- package/dist/types/shared/agent-presence.d.ts +3 -10
- package/dist/types/shared/ax-surface-protocol.d.ts +2 -0
- package/dist/types/shared/canvas-app.d.ts +54 -0
- package/dist/types/shared/themes.d.ts +1 -1
- package/docs/RELEASE.md +7 -3
- package/docs/agent-plugin.md +123 -0
- package/docs/ax-host-adapter-contract.md +13 -2
- package/docs/ax-state-contract.md +40 -0
- package/docs/cli.md +7 -2
- package/docs/design/AgentContext.dc.html +186 -0
- package/docs/design/Before.dc.html +208 -0
- package/docs/design/BoardPins.dc.html +151 -0
- package/docs/design/Chrome.dc.html +208 -0
- package/docs/design/Context.dc.html +239 -0
- package/docs/design/Flows.dc.html +181 -0
- package/docs/design/Home.dc.html +209 -0
- package/docs/design/HomeGraph.dc.html +211 -0
- package/docs/design/HomeMap.dc.html +117 -0
- package/docs/design/HomeTeam.dc.html +176 -0
- package/docs/design/Light.dc.html +28 -0
- package/docs/design/Main.dc.html +280 -0
- package/docs/design/NearPin.dc.html +260 -0
- package/docs/design/Pane600.dc.html +120 -0
- package/docs/design/Relations.dc.html +297 -0
- package/docs/design/Themes.dc.html +121 -0
- package/docs/design/TokenSystem.dc.html +136 -0
- package/docs/design/Tokens.dc.html +163 -0
- package/docs/design/Types1.dc.html +177 -0
- package/docs/design/Types2.dc.html +226 -0
- package/docs/design/Types3.dc.html +195 -0
- package/docs/design/Uses.dc.html +152 -0
- package/docs/design/Vision.dc.html +116 -0
- package/docs/design/Zoom.dc.html +82 -0
- package/docs/design/canvas.json +274 -0
- package/docs/design.md +214 -0
- package/docs/environment.md +1 -1
- package/docs/http-api.md +80 -15
- package/docs/mcp.md +198 -3
- package/docs/node-types.md +34 -10
- package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
- package/docs/plans/plan-015-design-foundation-wave-0.md +177 -0
- package/docs/plans/plan-016-context-made-visible-wave-1.md +148 -0
- package/docs/product-vision-2026-09.md +47 -7
- package/docs/screenshot.png +0 -0
- package/docs/sdk.md +1 -0
- package/package.json +10 -4
- package/plugins/pmx-canvas/LICENSE +21 -0
- package/plugins/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/plugins/pmx-canvas/assets/preview.png +0 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +930 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/steering-delivery.mjs +79 -0
- package/plugins/pmx-canvas/mcp.json +12 -0
- package/plugins/pmx-canvas/plugin.json +24 -0
- package/plugins/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/start-mcp.mjs +51 -0
- package/plugins/pmx-canvas/tools.json +2560 -0
- package/skills/pmx-canvas/SKILL.md +31 -16
- package/skills/pmx-canvas/evals/evals.json +69 -0
- package/skills/pmx-canvas/references/full-reference.md +14 -10
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +20 -8
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +76 -4
- package/skills/pmx-canvas-testing/SKILL.md +54 -4
- package/src/cli/commands/copilot.ts +5 -0
- package/src/cli/commands/view.ts +1 -1
- package/src/cli/index.ts +8 -3
- package/src/client/App.tsx +6 -1
- package/src/client/canvas/AgentPresenceLayer.tsx +3 -3
- package/src/client/canvas/AnnotationLayer.tsx +1 -1
- package/src/client/canvas/CanvasNode.tsx +208 -220
- package/src/client/canvas/CanvasViewport.tsx +6 -2
- package/src/client/canvas/ChoiceMenu.tsx +122 -0
- package/src/client/canvas/CommandBar.tsx +25 -23
- package/src/client/canvas/CommandPalette.tsx +3 -2
- package/src/client/canvas/ContextMenu.tsx +11 -0
- package/src/client/canvas/EdgeLayer.tsx +1 -1
- package/src/client/canvas/ExportDialog.tsx +33 -6
- package/src/client/canvas/FeedbackDialog.tsx +10 -8
- package/src/client/canvas/IntentLayer.tsx +2 -2
- package/src/client/canvas/Minimap.tsx +12 -7
- package/src/client/canvas/NodeContextMark.tsx +156 -0
- package/src/client/canvas/SessionPanel.tsx +4 -7
- package/src/client/canvas/SessionReceipt.tsx +114 -17
- package/src/client/canvas/ShortcutOverlay.tsx +6 -1
- package/src/client/canvas/SnapshotPanel.tsx +11 -2
- package/src/client/canvas/ToolRail.tsx +58 -108
- package/src/client/canvas/TopBar.tsx +16 -11
- package/src/client/icons.tsx +2 -12
- package/src/client/mcp-app/index.tsx +137 -0
- package/src/client/mcp-app/style.css +10 -0
- package/src/client/nodes/AttachmentNode.tsx +8 -1
- package/src/client/nodes/AxStepControls.tsx +1 -1
- package/src/client/nodes/ContextNode.tsx +9 -9
- package/src/client/nodes/ExtAppFrame.tsx +13 -8
- package/src/client/nodes/FileNode.tsx +8 -5
- package/src/client/nodes/ImageNode.tsx +4 -2
- package/src/client/nodes/InlineMarkdownEditor.tsx +2 -1
- package/src/client/nodes/LedgerNode.tsx +2 -2
- package/src/client/nodes/MarkdownNode.tsx +44 -9
- package/src/client/nodes/McpAppNode.tsx +22 -4
- package/src/client/nodes/PromptNode.tsx +5 -17
- package/src/client/nodes/ResponseNode.tsx +3 -15
- package/src/client/nodes/StatusNode.tsx +2 -2
- package/src/client/nodes/StatusSummary.tsx +3 -1
- package/src/client/nodes/TraceNode.tsx +6 -4
- package/src/client/nodes/WebpageNode.tsx +1 -1
- package/src/client/nodes/ax-node-actions.ts +2 -2
- package/src/client/nodes/iframe-document-url.ts +3 -2
- package/src/client/nodes/markdown-sanitize.ts +10 -0
- package/src/client/nodes/surface-url.ts +10 -0
- package/src/client/nodes/use-ax-surface-bridge.ts +4 -2
- package/src/client/nodes/use-surface-frame.ts +3 -1
- package/src/client/nodes/use-workbench-asset.ts +26 -0
- package/src/client/nodes/viewer-runtime-state.ts +78 -0
- package/src/client/state/attention-bridge.ts +0 -8
- package/src/client/state/attention-store.ts +0 -17
- package/src/client/state/canvas-store.ts +7 -2
- package/src/client/state/context-status-store.ts +66 -0
- package/src/client/state/human-store.ts +2 -0
- package/src/client/state/iframe-mode.ts +5 -0
- package/src/client/state/intent-bridge.ts +24 -6
- package/src/client/state/near-pin-store.ts +53 -0
- package/src/client/state/presence-store.ts +5 -10
- package/src/client/state/session-store.ts +106 -9
- package/src/client/state/sse-bridge.ts +26 -7
- package/src/client/state/theme-override.ts +1 -1
- package/src/client/state/workbench-transport.ts +36 -0
- package/src/client/theme/global.css +844 -710
- package/src/client/theme/surface-theme.css +151 -46
- package/src/client/theme/tokens.ts +7 -11
- package/src/client/types.ts +10 -0
- package/src/json-render/renderer/index.tsx +58 -38
- package/src/json-render/server.ts +79 -15
- package/src/mcp/app-server.ts +64 -0
- package/src/mcp/canvas-access.ts +14 -11
- package/src/mcp/canvas-app.ts +318 -0
- package/src/mcp/server.ts +1 -1
- package/src/mcp/workbench-app.ts +211 -0
- package/src/server/agent-presence.ts +45 -6
- package/src/server/ax-context.ts +1 -1
- package/src/server/ax-state.ts +1 -1
- package/src/server/board-export.ts +5 -2
- package/src/server/canvas-db.ts +46 -12
- package/src/server/canvas-geometry.ts +64 -0
- package/src/server/canvas-operations.ts +6 -24
- package/src/server/canvas-state.ts +105 -15
- package/src/server/canvas-validation.ts +19 -4
- package/src/server/context-brief.ts +39 -2
- package/src/server/context-reads.ts +86 -15
- package/src/server/demo-state.json +31 -31
- package/src/server/html-surface.ts +4 -5
- package/src/server/index.ts +19 -3
- package/src/server/mcp-app-runtime.ts +11 -0
- package/src/server/operations/composites.ts +4 -3
- package/src/server/operations/http.ts +4 -4
- package/src/server/operations/invoker.ts +13 -4
- package/src/server/operations/ops/ax-read.ts +1 -1
- package/src/server/operations/ops/ax-timeline.ts +37 -0
- package/src/server/operations/ops/boards.ts +1 -1
- package/src/server/operations/ops/canvas-wire.ts +3 -6
- package/src/server/operations/ops/intent.ts +31 -2
- package/src/server/operations/ops/json-render.ts +1 -1
- package/src/server/operations/ops/nodes.ts +8 -76
- package/src/server/operations/ops/query.ts +8 -2
- package/src/server/operations/ops/snapshots.ts +4 -1
- package/src/server/operations/ops/viewport.ts +29 -5
- package/src/server/operations/registry.ts +38 -4
- package/src/server/scope-fence.ts +55 -10
- package/src/server/server.ts +86 -6
- package/src/server/spatial-analysis.ts +2 -2
- package/src/server/workboard.ts +6 -1
- package/src/{server → shared}/agent-context.ts +2 -2
- package/src/shared/agent-presence.ts +3 -15
- package/src/shared/ax-surface-protocol.ts +3 -0
- package/src/shared/canvas-app.ts +62 -0
- package/src/shared/themes.ts +5 -5
- package/src/client/canvas/FocusFieldLayer.tsx +0 -116
- package/src/client/canvas/kind-colors.ts +0 -23
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRef } from 'preact/hooks';
|
|
2
|
-
import {
|
|
2
|
+
import { agentPhaseLabel, type AgentPresence } from '../../shared/agent-presence.js';
|
|
3
3
|
import { nodes, viewport } from '../state/canvas-store';
|
|
4
|
-
import { agentPresences, presenceWorldPosition } from '../state/presence-store';
|
|
4
|
+
import { agentPresences, presenceWorldPosition, writerColor } from '../state/presence-store';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Agent presence layer (rail-chrome-v2 phase 3): one cursor + phase chip per
|
|
@@ -49,7 +49,7 @@ export function AgentPresenceLayer() {
|
|
|
49
49
|
data-session-id={presence.sessionId}
|
|
50
50
|
style={{
|
|
51
51
|
transform: `translate(${position.x}px, ${position.y}px)`,
|
|
52
|
-
'--identity-color':
|
|
52
|
+
'--identity-color': writerColor(presence.sessionId),
|
|
53
53
|
}}
|
|
54
54
|
>
|
|
55
55
|
<div class="agent-cursor-inner" style={{ transform: `scale(${counterScale})` }}>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
|
2
2
|
import { mutatingNodeIds, sessionActive } from '../state/presence-store';
|
|
3
|
-
import {
|
|
3
|
+
import { attentionPulseNodeIds } from '../state/attention-store';
|
|
4
|
+
import { isAgentPin, NearPinMark, NodeContextMark } from './NodeContextMark';
|
|
5
|
+
import { activityLensNodeIds } from '../state/session-store';
|
|
4
6
|
import {
|
|
5
7
|
activeNodeId,
|
|
6
8
|
activeNeighborNodeIds,
|
|
@@ -34,20 +36,9 @@ import {
|
|
|
34
36
|
ungroupFromClient,
|
|
35
37
|
updateNodeFromClient,
|
|
36
38
|
} from '../state/intent-bridge';
|
|
37
|
-
import { KIND_COLOR } from './kind-colors';
|
|
38
39
|
import { reportHumanGrab, takeOverNode, yieldedNodes } from '../state/human-store';
|
|
39
40
|
import { AxStepControls } from '../nodes/AxStepControls';
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
getNodeIcon,
|
|
43
|
-
IconChevronRight,
|
|
44
|
-
IconChevronDown,
|
|
45
|
-
IconClose,
|
|
46
|
-
IconMore,
|
|
47
|
-
IconExternalLink,
|
|
48
|
-
IconExpand,
|
|
49
|
-
IconSparkle,
|
|
50
|
-
} from '../icons';
|
|
41
|
+
import { getNodeIcon, IconChevronRight, IconClose, IconMore, IconExpand, IconPin } from '../icons';
|
|
51
42
|
import { EXPANDABLE_TYPES, TYPE_LABELS } from '../types';
|
|
52
43
|
import type { CanvasNodeState } from '../types';
|
|
53
44
|
import { AUTO_FIT_TITLEBAR_HEIGHT, computeAutoFitHeight, shouldAutoFitNode } from './auto-fit';
|
|
@@ -137,8 +128,6 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
137
128
|
const isActive = activeNodeId.value === node.id;
|
|
138
129
|
const isSelected = selectedNodeIds.value.has(node.id);
|
|
139
130
|
const isContextPinned = contextPinnedNodeIds.value.has(node.id);
|
|
140
|
-
const isAttentionPrimary = attentionPrimaryNodeIds.value.has(node.id);
|
|
141
|
-
const isAttentionSecondary = !isAttentionPrimary && attentionSecondaryNodeIds.value.has(node.id);
|
|
142
131
|
const isAttentionPulse = attentionPulseNodeIds.value.has(node.id);
|
|
143
132
|
const isNeighbor = !isActive && activeNeighborNodeIds.value.has(node.id);
|
|
144
133
|
const searchSet = searchHighlightIds.value;
|
|
@@ -414,6 +403,7 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
414
403
|
height: node.collapsed ? 'auto' : `${node.size.height}px`,
|
|
415
404
|
zIndex: node.zIndex,
|
|
416
405
|
'--node-chrome-scale': chromeScale.toFixed(3),
|
|
406
|
+
'--node-port-scale': (1 / viewportScale).toFixed(3),
|
|
417
407
|
...(groupColor ? { '--group-color': groupColor } : {}),
|
|
418
408
|
};
|
|
419
409
|
// Shimmer while an attached agent is mutating this node (rail-chrome-v2
|
|
@@ -427,8 +417,6 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
427
417
|
isSearchDimmed ? 'search-dimmed' : '',
|
|
428
418
|
isSelected ? 'selected' : '',
|
|
429
419
|
isContextPinned ? 'context-pinned' : '',
|
|
430
|
-
isAttentionPrimary ? 'attention-focus-primary' : '',
|
|
431
|
-
isAttentionSecondary ? 'attention-focus-secondary' : '',
|
|
432
420
|
isAttentionPulse ? 'attention-pulse' : '',
|
|
433
421
|
isPinned ? 'pinned' : '',
|
|
434
422
|
isTrace ? 'trace-node' : '',
|
|
@@ -436,6 +424,7 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
436
424
|
isGroup ? 'group-node' : '',
|
|
437
425
|
isStrictSize ? 'strict-size' : '',
|
|
438
426
|
isAgentMutating ? 'agent-mutating' : '',
|
|
427
|
+
activityLensNodeIds.value && !activityLensNodeIds.value.has(node.id) ? 'lens-dimmed' : '',
|
|
439
428
|
]
|
|
440
429
|
.filter(Boolean)
|
|
441
430
|
.join(' ');
|
|
@@ -487,7 +476,7 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
487
476
|
<div class="group-chip-name">{title}</div>
|
|
488
477
|
<div class="group-chip-meta">
|
|
489
478
|
{groupKindDots.map((kind) => (
|
|
490
|
-
<span key={kind} class="group-kind-dot"
|
|
479
|
+
<span key={kind} class="group-kind-dot" aria-hidden="true" />
|
|
491
480
|
))}
|
|
492
481
|
<span class="group-chip-count">
|
|
493
482
|
{groupChildren.length} node{groupChildren.length === 1 ? '' : 's'}
|
|
@@ -515,13 +504,156 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
515
504
|
}}
|
|
516
505
|
onDblClick={handleDblClick}
|
|
517
506
|
>
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
<
|
|
507
|
+
<div class="node-content">
|
|
508
|
+
{isGroup && (
|
|
509
|
+
<div class="node-titlebar group-edge-row" onPointerDown={handleTitlePointerDown}>
|
|
510
|
+
<span class="group-name-pill">
|
|
511
|
+
{renaming ? (
|
|
512
|
+
<input
|
|
513
|
+
ref={renameRef}
|
|
514
|
+
class="node-title-input group-title-input"
|
|
515
|
+
value={title}
|
|
516
|
+
onBlur={(e) => commitRename((e.target as HTMLInputElement).value)}
|
|
517
|
+
onKeyDown={(e) => {
|
|
518
|
+
if (e.key === 'Enter') commitRename((e.target as HTMLInputElement).value);
|
|
519
|
+
if (e.key === 'Escape') setRenaming(false);
|
|
520
|
+
}}
|
|
521
|
+
onClick={(e) => e.stopPropagation()}
|
|
522
|
+
/>
|
|
523
|
+
) : (
|
|
524
|
+
<span class="group-name" title={title}>
|
|
525
|
+
{title}
|
|
526
|
+
</span>
|
|
527
|
+
)}
|
|
528
|
+
<span class="group-count">{groupChildren.length}</span>
|
|
529
|
+
</span>
|
|
530
|
+
<span class="group-edge-spacer" />
|
|
531
|
+
<span class="group-actions" role="toolbar" aria-label={`${title} actions`}>
|
|
532
|
+
<button
|
|
533
|
+
type="button"
|
|
534
|
+
class="group-action"
|
|
535
|
+
title="Auto-arrange children"
|
|
536
|
+
aria-label="Auto-arrange children"
|
|
537
|
+
disabled={groupChildren.length === 0}
|
|
538
|
+
onClick={(e) => {
|
|
539
|
+
e.stopPropagation();
|
|
540
|
+
void addToGroupFromClient(node.id, groupChildren, 'grid');
|
|
541
|
+
}}
|
|
542
|
+
>
|
|
543
|
+
<svg
|
|
544
|
+
width="11"
|
|
545
|
+
height="11"
|
|
546
|
+
viewBox="0 0 16 16"
|
|
547
|
+
fill="none"
|
|
548
|
+
stroke="currentColor"
|
|
549
|
+
stroke-width="1.5"
|
|
550
|
+
aria-hidden="true"
|
|
551
|
+
>
|
|
552
|
+
<rect x="1.5" y="1.5" width="5" height="5" rx="1" />
|
|
553
|
+
<rect x="9.5" y="1.5" width="5" height="5" rx="1" />
|
|
554
|
+
<rect x="1.5" y="9.5" width="5" height="5" rx="1" />
|
|
555
|
+
<rect x="9.5" y="9.5" width="5" height="5" rx="1" />
|
|
556
|
+
</svg>
|
|
557
|
+
</button>
|
|
558
|
+
<button
|
|
559
|
+
type="button"
|
|
560
|
+
class="group-action"
|
|
561
|
+
title="Collapse group"
|
|
562
|
+
aria-label="Collapse group"
|
|
563
|
+
onClick={(e) => {
|
|
564
|
+
e.stopPropagation();
|
|
565
|
+
toggleCollapsed(node.id);
|
|
566
|
+
}}
|
|
567
|
+
>
|
|
568
|
+
<svg
|
|
569
|
+
width="11"
|
|
570
|
+
height="11"
|
|
571
|
+
viewBox="0 0 16 16"
|
|
572
|
+
fill="none"
|
|
573
|
+
stroke="currentColor"
|
|
574
|
+
stroke-width="1.8"
|
|
575
|
+
stroke-linecap="round"
|
|
576
|
+
stroke-linejoin="round"
|
|
577
|
+
aria-hidden="true"
|
|
578
|
+
>
|
|
579
|
+
<path d="M3 10 L8 5 L13 10" />
|
|
580
|
+
</svg>
|
|
581
|
+
</button>
|
|
582
|
+
<button
|
|
583
|
+
type="button"
|
|
584
|
+
class="group-action"
|
|
585
|
+
title="Rename · Ungroup · Pin all to context"
|
|
586
|
+
aria-label="Group menu"
|
|
587
|
+
aria-expanded={groupMenuOpen}
|
|
588
|
+
onClick={(e) => {
|
|
589
|
+
e.stopPropagation();
|
|
590
|
+
setGroupMenuOpen((open) => !open);
|
|
591
|
+
}}
|
|
592
|
+
>
|
|
593
|
+
<IconMore size={14} />
|
|
594
|
+
</button>
|
|
595
|
+
{groupMenuOpen && (
|
|
596
|
+
<div class="group-menu" role="menu" onPointerDown={(e) => e.stopPropagation()}>
|
|
597
|
+
<button
|
|
598
|
+
type="button"
|
|
599
|
+
role="menuitem"
|
|
600
|
+
onClick={(e) => {
|
|
601
|
+
e.stopPropagation();
|
|
602
|
+
setGroupMenuOpen(false);
|
|
603
|
+
setRenaming(true);
|
|
604
|
+
requestAnimationFrame(() => renameRef.current?.focus());
|
|
605
|
+
}}
|
|
606
|
+
>
|
|
607
|
+
Rename
|
|
608
|
+
</button>
|
|
609
|
+
<button
|
|
610
|
+
type="button"
|
|
611
|
+
role="menuitem"
|
|
612
|
+
onClick={(e) => {
|
|
613
|
+
e.stopPropagation();
|
|
614
|
+
setGroupMenuOpen(false);
|
|
615
|
+
// Dissolve — the same op an agent's ungroup runs.
|
|
616
|
+
removeNode(node.id);
|
|
617
|
+
void ungroupFromClient(node.id);
|
|
618
|
+
}}
|
|
619
|
+
>
|
|
620
|
+
Ungroup
|
|
621
|
+
</button>
|
|
622
|
+
<button
|
|
623
|
+
type="button"
|
|
624
|
+
role="menuitem"
|
|
625
|
+
disabled={groupChildren.length === 0}
|
|
626
|
+
onClick={(e) => {
|
|
627
|
+
e.stopPropagation();
|
|
628
|
+
setGroupMenuOpen(false);
|
|
629
|
+
addContextPins(groupChildren);
|
|
630
|
+
}}
|
|
631
|
+
>
|
|
632
|
+
Pin all to context
|
|
633
|
+
</button>
|
|
634
|
+
</div>
|
|
635
|
+
)}
|
|
636
|
+
</span>
|
|
637
|
+
</div>
|
|
638
|
+
)}
|
|
639
|
+
{!isGroup && (
|
|
640
|
+
<div class="node-titlebar" onPointerDown={handleTitlePointerDown}>
|
|
641
|
+
<span class="node-type-icon" aria-hidden="true">
|
|
642
|
+
{(() => {
|
|
643
|
+
const NodeIcon = getNodeIcon(node.type);
|
|
644
|
+
return <NodeIcon size={Math.round(14 * chromeScale)} />;
|
|
645
|
+
})()}
|
|
646
|
+
</span>
|
|
647
|
+
{/* The kind-colored icon says what type this is (rail-chrome-v2 card
|
|
648
|
+
shell: icon · title · controls, no type badge). Only the AX status
|
|
649
|
+
chip joins it, and only when there is a status to show. */}
|
|
650
|
+
{typeof node.data.axWorkStatus === 'string' && (
|
|
651
|
+
<span class={`node-ax-status node-ax-status-${node.data.axWorkStatus}`}>{node.data.axWorkStatus}</span>
|
|
652
|
+
)}
|
|
521
653
|
{renaming ? (
|
|
522
654
|
<input
|
|
523
655
|
ref={renameRef}
|
|
524
|
-
class="node-title-input
|
|
656
|
+
class="node-title-input"
|
|
525
657
|
value={title}
|
|
526
658
|
onBlur={(e) => commitRename((e.target as HTMLInputElement).value)}
|
|
527
659
|
onKeyDown={(e) => {
|
|
@@ -531,234 +663,88 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
531
663
|
onClick={(e) => e.stopPropagation()}
|
|
532
664
|
/>
|
|
533
665
|
) : (
|
|
534
|
-
<span class="
|
|
666
|
+
<span class="node-title" title={title}>
|
|
535
667
|
{title}
|
|
536
668
|
</span>
|
|
537
669
|
)}
|
|
538
|
-
<
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
e.stopPropagation();
|
|
550
|
-
void addToGroupFromClient(node.id, groupChildren, 'grid');
|
|
551
|
-
}}
|
|
552
|
-
>
|
|
553
|
-
<svg
|
|
554
|
-
width="11"
|
|
555
|
-
height="11"
|
|
556
|
-
viewBox="0 0 16 16"
|
|
557
|
-
fill="none"
|
|
558
|
-
stroke="currentColor"
|
|
559
|
-
stroke-width="1.5"
|
|
560
|
-
aria-hidden="true"
|
|
561
|
-
>
|
|
562
|
-
<rect x="1.5" y="1.5" width="5" height="5" rx="1" />
|
|
563
|
-
<rect x="9.5" y="1.5" width="5" height="5" rx="1" />
|
|
564
|
-
<rect x="1.5" y="9.5" width="5" height="5" rx="1" />
|
|
565
|
-
<rect x="9.5" y="9.5" width="5" height="5" rx="1" />
|
|
566
|
-
</svg>
|
|
567
|
-
</button>
|
|
568
|
-
<button
|
|
569
|
-
type="button"
|
|
570
|
-
class="group-action"
|
|
571
|
-
title="Collapse group"
|
|
572
|
-
aria-label="Collapse group"
|
|
573
|
-
onClick={(e) => {
|
|
574
|
-
e.stopPropagation();
|
|
575
|
-
toggleCollapsed(node.id);
|
|
576
|
-
}}
|
|
577
|
-
>
|
|
578
|
-
<svg
|
|
579
|
-
width="11"
|
|
580
|
-
height="11"
|
|
581
|
-
viewBox="0 0 16 16"
|
|
582
|
-
fill="none"
|
|
583
|
-
stroke="currentColor"
|
|
584
|
-
stroke-width="1.8"
|
|
585
|
-
stroke-linecap="round"
|
|
586
|
-
stroke-linejoin="round"
|
|
587
|
-
aria-hidden="true"
|
|
588
|
-
>
|
|
589
|
-
<path d="M3 10 L8 5 L13 10" />
|
|
590
|
-
</svg>
|
|
591
|
-
</button>
|
|
592
|
-
<button
|
|
593
|
-
type="button"
|
|
594
|
-
class="group-action"
|
|
595
|
-
title="Rename · Ungroup · Pin all to context"
|
|
596
|
-
aria-label="Group menu"
|
|
597
|
-
aria-expanded={groupMenuOpen}
|
|
598
|
-
onClick={(e) => {
|
|
599
|
-
e.stopPropagation();
|
|
600
|
-
setGroupMenuOpen((open) => !open);
|
|
601
|
-
}}
|
|
602
|
-
>
|
|
603
|
-
<IconMore size={14} />
|
|
604
|
-
</button>
|
|
605
|
-
{groupMenuOpen && (
|
|
606
|
-
<div class="group-menu" role="menu" onPointerDown={(e) => e.stopPropagation()}>
|
|
670
|
+
<NearPinMark node={node} pinned={isContextPinned} />
|
|
671
|
+
<NodeContextMark node={node} pinned={isContextPinned} />
|
|
672
|
+
<div class="node-controls">
|
|
673
|
+
{isPinned && (
|
|
674
|
+
<span class="pin-indicator" title="Pinned">
|
|
675
|
+
⊙
|
|
676
|
+
</span>
|
|
677
|
+
)}
|
|
678
|
+
{/* Expand and the context pin always show; the rest sit behind ⋯ and ×
|
|
679
|
+
on hover (docs/design/Chrome.dc.html). */}
|
|
680
|
+
{EXPANDABLE_TYPES.has(node.type) && (
|
|
607
681
|
<button
|
|
608
682
|
type="button"
|
|
609
|
-
role="menuitem"
|
|
610
683
|
onClick={(e) => {
|
|
611
684
|
e.stopPropagation();
|
|
612
|
-
|
|
613
|
-
setRenaming(true);
|
|
614
|
-
requestAnimationFrame(() => renameRef.current?.focus());
|
|
685
|
+
expandNode(node.id);
|
|
615
686
|
}}
|
|
687
|
+
title="Expand (focus mode)"
|
|
616
688
|
>
|
|
617
|
-
|
|
689
|
+
<IconExpand size={14} />
|
|
618
690
|
</button>
|
|
619
|
-
|
|
620
|
-
type="button"
|
|
621
|
-
role="menuitem"
|
|
622
|
-
onClick={(e) => {
|
|
623
|
-
e.stopPropagation();
|
|
624
|
-
setGroupMenuOpen(false);
|
|
625
|
-
// Dissolve — the same op an agent's ungroup runs.
|
|
626
|
-
removeNode(node.id);
|
|
627
|
-
void ungroupFromClient(node.id);
|
|
628
|
-
}}
|
|
629
|
-
>
|
|
630
|
-
Ungroup
|
|
631
|
-
</button>
|
|
632
|
-
<button
|
|
633
|
-
type="button"
|
|
634
|
-
role="menuitem"
|
|
635
|
-
disabled={groupChildren.length === 0}
|
|
636
|
-
onClick={(e) => {
|
|
637
|
-
e.stopPropagation();
|
|
638
|
-
setGroupMenuOpen(false);
|
|
639
|
-
addContextPins(groupChildren);
|
|
640
|
-
}}
|
|
641
|
-
>
|
|
642
|
-
Pin all to context
|
|
643
|
-
</button>
|
|
644
|
-
</div>
|
|
645
|
-
)}
|
|
646
|
-
</span>
|
|
647
|
-
</div>
|
|
648
|
-
)}
|
|
649
|
-
{!isGroup && (
|
|
650
|
-
<div class="node-titlebar" onPointerDown={handleTitlePointerDown}>
|
|
651
|
-
<span class="node-type-icon" aria-hidden="true">
|
|
652
|
-
{(() => {
|
|
653
|
-
const NodeIcon = getNodeIcon(node.type);
|
|
654
|
-
return <NodeIcon size={Math.round(14 * chromeScale)} />;
|
|
655
|
-
})()}
|
|
656
|
-
</span>
|
|
657
|
-
{/* The kind-colored icon says what type this is (rail-chrome-v2 card
|
|
658
|
-
shell: icon · title · controls, no type badge). Only the AX status
|
|
659
|
-
chip joins it, and only when there is a status to show. */}
|
|
660
|
-
{typeof node.data.axWorkStatus === 'string' && (
|
|
661
|
-
<span class={`node-ax-status node-ax-status-${node.data.axWorkStatus}`}>{node.data.axWorkStatus}</span>
|
|
662
|
-
)}
|
|
663
|
-
{renaming ? (
|
|
664
|
-
<input
|
|
665
|
-
ref={renameRef}
|
|
666
|
-
class="node-title-input"
|
|
667
|
-
value={title}
|
|
668
|
-
onBlur={(e) => commitRename((e.target as HTMLInputElement).value)}
|
|
669
|
-
onKeyDown={(e) => {
|
|
670
|
-
if (e.key === 'Enter') commitRename((e.target as HTMLInputElement).value);
|
|
671
|
-
if (e.key === 'Escape') setRenaming(false);
|
|
672
|
-
}}
|
|
673
|
-
onClick={(e) => e.stopPropagation()}
|
|
674
|
-
/>
|
|
675
|
-
) : (
|
|
676
|
-
<span class="node-title" title={title}>
|
|
677
|
-
{title}
|
|
678
|
-
</span>
|
|
679
|
-
)}
|
|
680
|
-
<div class="node-controls">
|
|
681
|
-
{isPinned && (
|
|
682
|
-
<span class="pin-indicator" title="Pinned">
|
|
683
|
-
⊙
|
|
684
|
-
</span>
|
|
685
|
-
)}
|
|
686
|
-
<button
|
|
687
|
-
type="button"
|
|
688
|
-
class={`ctx-pin-btn${isContextPinned ? ' ctx-pin-active' : ''}`}
|
|
689
|
-
onClick={(e) => {
|
|
690
|
-
e.stopPropagation();
|
|
691
|
-
toggleContextPin(node.id);
|
|
692
|
-
}}
|
|
693
|
-
title={isContextPinned ? 'Remove from context' : 'Add to context'}
|
|
694
|
-
>
|
|
695
|
-
<IconSparkle size={14} />
|
|
696
|
-
</button>
|
|
697
|
-
{/* Open as site — full-page standalone view of this node's surface,
|
|
698
|
-
served from /api/canvas/surface/:id (same document as the canvas
|
|
699
|
-
iframe). Opens via the system browser so embedded hosts do not trap
|
|
700
|
-
it in their own webview. */}
|
|
701
|
-
{canOpenAsSite(node) && (
|
|
691
|
+
)}
|
|
702
692
|
<button
|
|
703
693
|
type="button"
|
|
694
|
+
class={`ctx-pin-btn${isContextPinned ? ' ctx-pin-active' : ''}`}
|
|
704
695
|
onClick={(e) => {
|
|
705
696
|
e.stopPropagation();
|
|
706
|
-
|
|
697
|
+
toggleContextPin(node.id);
|
|
707
698
|
}}
|
|
708
|
-
title=
|
|
699
|
+
title={isContextPinned ? 'Remove from context' : 'Add to context'}
|
|
700
|
+
aria-label={isContextPinned ? 'Remove from context' : 'Add to context'}
|
|
701
|
+
aria-pressed={isContextPinned}
|
|
709
702
|
>
|
|
710
|
-
<
|
|
703
|
+
<span class="ctx-pin-mark">
|
|
704
|
+
<IconPin />
|
|
705
|
+
{isContextPinned && isAgentPin(node.id) && <span class="ctx-pin-agent-dot" aria-hidden="true" />}
|
|
706
|
+
</span>
|
|
711
707
|
</button>
|
|
712
|
-
)}
|
|
713
|
-
{/* Expand — opens node as full-viewport overlay for focused work */}
|
|
714
|
-
{EXPANDABLE_TYPES.has(node.type) && (
|
|
715
708
|
<button
|
|
716
709
|
type="button"
|
|
710
|
+
class="node-hover-control"
|
|
717
711
|
onClick={(e) => {
|
|
718
712
|
e.stopPropagation();
|
|
719
|
-
|
|
713
|
+
onContextMenu?.(e, node.id);
|
|
720
714
|
}}
|
|
721
|
-
title="
|
|
715
|
+
title="More actions"
|
|
716
|
+
aria-label="More actions"
|
|
722
717
|
>
|
|
723
|
-
<
|
|
718
|
+
<IconMore size={14} />
|
|
724
719
|
</button>
|
|
725
|
-
|
|
726
|
-
<button
|
|
727
|
-
type="button"
|
|
728
|
-
onClick={(e) => {
|
|
729
|
-
e.stopPropagation();
|
|
730
|
-
toggleCollapsed(node.id);
|
|
731
|
-
}}
|
|
732
|
-
title={node.collapsed ? 'Expand' : 'Collapse'}
|
|
733
|
-
>
|
|
734
|
-
{node.collapsed ? <IconChevronRight size={14} /> : <IconChevronDown size={14} />}
|
|
735
|
-
</button>
|
|
736
|
-
{/* Report #64: status nodes get the same remove control as every other
|
|
720
|
+
{/* Report #64: status nodes get the same remove control as every other
|
|
737
721
|
node type (backend removal + undo/history handle status uniformly). */}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
e
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
722
|
+
<button
|
|
723
|
+
type="button"
|
|
724
|
+
class="node-hover-control"
|
|
725
|
+
onClick={(e) => {
|
|
726
|
+
e.stopPropagation();
|
|
727
|
+
void removeNodeFromClient(node.id);
|
|
728
|
+
}}
|
|
729
|
+
title="Close"
|
|
730
|
+
>
|
|
731
|
+
<IconClose size={14} />
|
|
732
|
+
</button>
|
|
733
|
+
</div>
|
|
748
734
|
</div>
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
{/* AX flow step controls — rendered here rather than in a node renderer
|
|
735
|
+
)}
|
|
736
|
+
{!node.collapsed && (
|
|
737
|
+
<div ref={bodyRef} class="node-body">
|
|
738
|
+
{children}
|
|
739
|
+
{/* AX flow step controls — rendered here rather than in a node renderer
|
|
755
740
|
because a materialized step can be ANY node type, and because the
|
|
756
741
|
body is what auto-fit measures (a footer outside it would be clipped
|
|
757
742
|
on a content-fitted node). Renders nothing unless the node carries a
|
|
758
743
|
`data.axStep` stamp. */}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
744
|
+
<AxStepControls node={node} />
|
|
745
|
+
</div>
|
|
746
|
+
)}
|
|
747
|
+
</div>
|
|
762
748
|
{!node.collapsed && (
|
|
763
749
|
<div class="node-resize-handle" onPointerDown={(e) => startResize(e, node.size.width, node.size.height)} />
|
|
764
750
|
)}
|
|
@@ -781,8 +767,10 @@ export function CanvasNode({ node, children, onContextMenu }: CanvasNodeProps) {
|
|
|
781
767
|
key={side}
|
|
782
768
|
class={`node-port node-port-${side}`}
|
|
783
769
|
onPointerDown={(e) => {
|
|
770
|
+
if (e.button !== 0) return;
|
|
784
771
|
e.stopPropagation();
|
|
785
772
|
e.preventDefault();
|
|
773
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
786
774
|
const cx = node.position.x + node.size.width / 2;
|
|
787
775
|
const cy = node.position.y + node.size.height / 2;
|
|
788
776
|
const hw = node.size.width / 2;
|
|
@@ -38,7 +38,6 @@ import {
|
|
|
38
38
|
} from '../state/canvas-store';
|
|
39
39
|
import { createEdgeFromClient, createNodeFromClient } from '../state/intent-bridge';
|
|
40
40
|
import type { AnnotationTool, CanvasAnnotation, CanvasNodeState } from '../types';
|
|
41
|
-
import { FocusFieldLayer } from './FocusFieldLayer';
|
|
42
41
|
import { importFiles } from './import-files';
|
|
43
42
|
import { IntentLayer } from './IntentLayer';
|
|
44
43
|
import { AgentPresenceLayer } from './AgentPresenceLayer';
|
|
@@ -652,12 +651,18 @@ export function CanvasViewport({
|
|
|
652
651
|
}
|
|
653
652
|
}
|
|
654
653
|
|
|
654
|
+
function handleCancel() {
|
|
655
|
+
draggingEdge.value = null;
|
|
656
|
+
}
|
|
657
|
+
|
|
655
658
|
document.addEventListener('pointermove', handleMove);
|
|
656
659
|
document.addEventListener('pointerup', handleUp);
|
|
660
|
+
document.addEventListener('pointercancel', handleCancel);
|
|
657
661
|
document.addEventListener('keydown', handleKey);
|
|
658
662
|
return () => {
|
|
659
663
|
document.removeEventListener('pointermove', handleMove);
|
|
660
664
|
document.removeEventListener('pointerup', handleUp);
|
|
665
|
+
document.removeEventListener('pointercancel', handleCancel);
|
|
661
666
|
document.removeEventListener('keydown', handleKey);
|
|
662
667
|
};
|
|
663
668
|
}, [containerRef]);
|
|
@@ -849,7 +854,6 @@ export function CanvasViewport({
|
|
|
849
854
|
left: 0,
|
|
850
855
|
}}
|
|
851
856
|
>
|
|
852
|
-
<FocusFieldLayer />
|
|
853
857
|
<ScopeFenceLayer />
|
|
854
858
|
<IntentLayer />
|
|
855
859
|
<AgentPresenceLayer />
|