pmx-canvas 0.4.7 → 0.5.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.
Files changed (212) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +154 -5
  2. package/.github/extensions/pmx-canvas/steering-delivery.mjs +79 -0
  3. package/CHANGELOG.md +156 -0
  4. package/Readme.md +72 -23
  5. package/dist/canvas/global.css +3069 -796
  6. package/dist/canvas/index.js +104 -104
  7. package/dist/json-render/index.css +1 -1
  8. package/dist/types/cli/agent.d.ts +1 -0
  9. package/dist/types/cli/commands/pump.d.ts +9 -0
  10. package/dist/types/client/App.d.ts +1 -1
  11. package/dist/types/client/canvas/AgentPresenceLayer.d.ts +11 -0
  12. package/dist/types/client/canvas/BarHint.d.ts +22 -0
  13. package/dist/types/client/canvas/CanvasNode.d.ts +5 -0
  14. package/dist/types/client/canvas/CanvasViewport.d.ts +4 -4
  15. package/dist/types/client/canvas/CommandBar.d.ts +10 -0
  16. package/dist/types/client/canvas/ConnectionBanner.d.ts +13 -0
  17. package/dist/types/client/canvas/ContextMenu.d.ts +6 -0
  18. package/dist/types/client/canvas/EdgeLayer.d.ts +2 -1
  19. package/dist/types/client/canvas/EmptyState.d.ts +11 -0
  20. package/dist/types/client/canvas/ExternalWriters.d.ts +5 -0
  21. package/dist/types/client/canvas/HumanPresenceLayer.d.ts +8 -0
  22. package/dist/types/client/canvas/Minimap.d.ts +12 -1
  23. package/dist/types/client/canvas/ScopeFenceLayer.d.ts +8 -0
  24. package/dist/types/client/canvas/SelectionBar.d.ts +6 -0
  25. package/dist/types/client/canvas/SessionPanel.d.ts +1 -0
  26. package/dist/types/client/canvas/SessionReceipt.d.ts +17 -0
  27. package/dist/types/client/canvas/TextPrompt.d.ts +25 -0
  28. package/dist/types/client/canvas/ToolRail.d.ts +21 -0
  29. package/dist/types/client/canvas/TopBar.d.ts +7 -0
  30. package/dist/types/client/canvas/canvas-area.d.ts +25 -0
  31. package/dist/types/client/canvas/create-in-view.d.ts +18 -0
  32. package/dist/types/client/canvas/import-files.d.ts +8 -0
  33. package/dist/types/client/canvas/kind-colors.d.ts +3 -0
  34. package/dist/types/client/canvas/use-focus-trap.d.ts +14 -0
  35. package/dist/types/client/canvas/use-now.d.ts +6 -0
  36. package/dist/types/client/icons.d.ts +11 -4
  37. package/dist/types/client/nodes/ExtAppFrame.d.ts +32 -0
  38. package/dist/types/client/nodes/GroupNode.d.ts +6 -1
  39. package/dist/types/client/nodes/MarkdownNode.d.ts +2 -0
  40. package/dist/types/client/nodes/inline-editor-commands.d.ts +3 -4
  41. package/dist/types/client/state/canvas-store.d.ts +49 -5
  42. package/dist/types/client/state/human-store.d.ts +26 -0
  43. package/dist/types/client/state/iframe-mode.d.ts +1 -0
  44. package/dist/types/client/state/intent-bridge.d.ts +37 -5
  45. package/dist/types/client/state/node-factory.d.ts +0 -1
  46. package/dist/types/client/state/presence-store.d.ts +78 -0
  47. package/dist/types/client/state/session-store.d.ts +176 -0
  48. package/dist/types/client/types.d.ts +2 -1
  49. package/dist/types/client/utils/platform.d.ts +2 -0
  50. package/dist/types/server/agent-presence.d.ts +192 -0
  51. package/dist/types/server/ax-gate-ttl.d.ts +6 -0
  52. package/dist/types/server/ax-interaction.d.ts +4 -10
  53. package/dist/types/server/ax-state-manager.d.ts +21 -2
  54. package/dist/types/server/ax-state.d.ts +49 -3
  55. package/dist/types/server/ax-wait.d.ts +13 -0
  56. package/dist/types/server/canvas-db.d.ts +23 -1
  57. package/dist/types/server/canvas-operations.d.ts +1 -0
  58. package/dist/types/server/canvas-state.d.ts +27 -13
  59. package/dist/types/server/canvas-validation.d.ts +0 -14
  60. package/dist/types/server/html-surface.d.ts +1 -1
  61. package/dist/types/server/human-presence.d.ts +40 -0
  62. package/dist/types/server/index.d.ts +29 -3
  63. package/dist/types/server/intent-registry.d.ts +7 -0
  64. package/dist/types/server/mutation-history.d.ts +15 -2
  65. package/dist/types/server/operations/invoker.d.ts +13 -2
  66. package/dist/types/server/operations/ops/ax-presence.d.ts +2 -0
  67. package/dist/types/server/operations/ops/human-presence.d.ts +3 -0
  68. package/dist/types/server/operations/registry.d.ts +11 -0
  69. package/dist/types/server/operations/types.d.ts +1 -1
  70. package/dist/types/server/scope-fence.d.ts +62 -0
  71. package/dist/types/server/server.d.ts +0 -12
  72. package/dist/types/shared/agent-presence.d.ts +130 -0
  73. package/dist/types/shared/approval-gates.d.ts +19 -0
  74. package/dist/types/shared/auto-arrange.d.ts +0 -1
  75. package/dist/types/shared/ax-kinds.d.ts +11 -0
  76. package/dist/types/shared/human-presence.d.ts +31 -0
  77. package/dist/types/shared/iframe-probe.d.ts +8 -0
  78. package/dist/types/shared/scope-fence.d.ts +21 -0
  79. package/docs/RELEASE.md +54 -0
  80. package/docs/ax-host-adapter-contract.md +30 -14
  81. package/docs/cli.md +37 -1
  82. package/docs/environment.md +3 -0
  83. package/docs/http-api.md +222 -5
  84. package/docs/mcp.md +6 -5
  85. package/docs/node-types.md +3 -1
  86. package/docs/plans/plan-009-tech-debt-backlog.md +13 -0
  87. package/docs/screenshot.png +0 -0
  88. package/docs/screenshots/multi-agent-live.png +0 -0
  89. package/docs/screenshots/workbench-live-dark.png +0 -0
  90. package/docs/screenshots/workbench-live-light.png +0 -0
  91. package/docs/sdk.md +1 -1
  92. package/package.json +1 -1
  93. package/skills/pmx-canvas/SKILL.md +109 -18
  94. package/skills/pmx-canvas/references/codex-app-adapter.md +28 -0
  95. package/skills/pmx-canvas/references/full-reference.md +55 -16
  96. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +41 -24
  97. package/skills/pmx-canvas-orchestration/SKILL.md +56 -2
  98. package/skills/pmx-canvas-testing/SKILL.md +57 -0
  99. package/src/cli/agent.ts +1 -0
  100. package/src/cli/commands/copilot.ts +15 -8
  101. package/src/cli/commands/nodes.ts +1 -19
  102. package/src/cli/commands/pump.ts +252 -0
  103. package/src/cli/index.ts +1 -5
  104. package/src/cli/shared.ts +1 -1
  105. package/src/client/App.tsx +209 -585
  106. package/src/client/canvas/AgentPresenceLayer.tsx +74 -0
  107. package/src/client/canvas/AttentionHistory.tsx +0 -10
  108. package/src/client/canvas/BarHint.tsx +49 -0
  109. package/src/client/canvas/CanvasNode.tsx +488 -136
  110. package/src/client/canvas/CanvasViewport.tsx +190 -94
  111. package/src/client/canvas/CommandBar.tsx +140 -0
  112. package/src/client/canvas/CommandPalette.tsx +133 -58
  113. package/src/client/canvas/ConnectionBanner.tsx +65 -0
  114. package/src/client/canvas/ContextMenu.tsx +88 -27
  115. package/src/client/canvas/ContextPinBar.tsx +2 -2
  116. package/src/client/canvas/EdgeLayer.tsx +121 -38
  117. package/src/client/canvas/EmptyState.tsx +93 -0
  118. package/src/client/canvas/ExpandedNodeOverlay.tsx +64 -118
  119. package/src/client/canvas/ExternalWriters.tsx +308 -0
  120. package/src/client/canvas/FocusFieldLayer.tsx +12 -1
  121. package/src/client/canvas/HumanPresenceLayer.tsx +45 -0
  122. package/src/client/canvas/IntentLayer.tsx +1 -1
  123. package/src/client/canvas/Minimap.tsx +117 -190
  124. package/src/client/canvas/ScopeFenceLayer.tsx +38 -0
  125. package/src/client/canvas/SelectionBar.tsx +175 -23
  126. package/src/client/canvas/SessionPanel.tsx +481 -0
  127. package/src/client/canvas/SessionReceipt.tsx +141 -0
  128. package/src/client/canvas/ShortcutOverlay.tsx +21 -6
  129. package/src/client/canvas/SnapshotPanel.tsx +166 -80
  130. package/src/client/canvas/TextPrompt.tsx +128 -0
  131. package/src/client/canvas/ToolRail.tsx +447 -0
  132. package/src/client/canvas/TopBar.tsx +308 -0
  133. package/src/client/canvas/auto-fit.ts +1 -5
  134. package/src/client/canvas/canvas-area.ts +41 -0
  135. package/src/client/canvas/create-in-view.ts +48 -0
  136. package/src/client/canvas/import-files.ts +64 -0
  137. package/src/client/canvas/kind-colors.ts +22 -0
  138. package/src/client/canvas/snap-guides.ts +1 -1
  139. package/src/client/canvas/use-focus-trap.ts +54 -0
  140. package/src/client/canvas/use-now.ts +17 -0
  141. package/src/client/canvas/use-pan-zoom.ts +13 -2
  142. package/src/client/icons.tsx +57 -18
  143. package/src/client/nodes/ExtAppFrame.tsx +79 -2
  144. package/src/client/nodes/GroupNode.tsx +9 -28
  145. package/src/client/nodes/InlineFormatBar.tsx +4 -3
  146. package/src/client/nodes/InlineMarkdownEditor.tsx +27 -0
  147. package/src/client/nodes/MarkdownNode.tsx +59 -24
  148. package/src/client/nodes/MdFormatBar.tsx +2 -4
  149. package/src/client/nodes/inline-editor-commands.ts +17 -6
  150. package/src/client/nodes/use-surface-frame.ts +3 -1
  151. package/src/client/state/attention-bridge.ts +3 -0
  152. package/src/client/state/canvas-store.ts +273 -84
  153. package/src/client/state/human-store.ts +175 -0
  154. package/src/client/state/iframe-mode.ts +33 -9
  155. package/src/client/state/intent-bridge.ts +75 -13
  156. package/src/client/state/node-factory.ts +2 -8
  157. package/src/client/state/presence-store.ts +168 -0
  158. package/src/client/state/session-store.ts +419 -0
  159. package/src/client/state/sse-bridge.ts +93 -30
  160. package/src/client/theme/global.css +3069 -796
  161. package/src/client/types.ts +3 -1
  162. package/src/client/utils/platform.ts +6 -1
  163. package/src/mcp/canvas-access.ts +2 -2
  164. package/src/server/agent-presence.ts +755 -0
  165. package/src/server/ax-context.ts +45 -0
  166. package/src/server/ax-gate-ttl.ts +61 -0
  167. package/src/server/ax-interaction.ts +29 -1
  168. package/src/server/ax-state-manager.ts +78 -6
  169. package/src/server/ax-state.ts +82 -15
  170. package/src/server/ax-wait.ts +33 -0
  171. package/src/server/canvas-db.ts +237 -61
  172. package/src/server/canvas-operations.ts +17 -8
  173. package/src/server/canvas-state.ts +131 -58
  174. package/src/server/canvas-validation.ts +3 -53
  175. package/src/server/demo-state.json +2 -70
  176. package/src/server/html-surface.ts +11 -2
  177. package/src/server/human-presence.ts +160 -0
  178. package/src/server/index.ts +107 -4
  179. package/src/server/intent-registry.ts +13 -0
  180. package/src/server/mutation-history.ts +47 -2
  181. package/src/server/operations/composites.ts +6 -3
  182. package/src/server/operations/http.ts +10 -1
  183. package/src/server/operations/index.ts +4 -0
  184. package/src/server/operations/invoker.ts +33 -6
  185. package/src/server/operations/ops/ax-presence.ts +62 -0
  186. package/src/server/operations/ops/ax-read.ts +8 -0
  187. package/src/server/operations/ops/ax-state.ts +106 -1
  188. package/src/server/operations/ops/ax-timeline.ts +54 -5
  189. package/src/server/operations/ops/ax-work.ts +46 -0
  190. package/src/server/operations/ops/batch.ts +3 -1
  191. package/src/server/operations/ops/edges.ts +88 -2
  192. package/src/server/operations/ops/groups.ts +4 -3
  193. package/src/server/operations/ops/human-presence.ts +35 -0
  194. package/src/server/operations/ops/json-render.ts +11 -1
  195. package/src/server/operations/ops/nodes.ts +65 -16
  196. package/src/server/operations/ops/query.ts +1 -0
  197. package/src/server/operations/ops/snapshots.ts +16 -0
  198. package/src/server/operations/registry.ts +196 -1
  199. package/src/server/operations/types.ts +1 -1
  200. package/src/server/scope-fence.ts +207 -0
  201. package/src/server/server.ts +177 -79
  202. package/src/server/trace-manager.ts +0 -2
  203. package/src/server/web-artifacts.ts +0 -1
  204. package/src/shared/agent-presence.ts +162 -0
  205. package/src/shared/approval-gates.ts +35 -0
  206. package/src/shared/auto-arrange.ts +121 -13
  207. package/src/shared/ax-kinds.ts +23 -0
  208. package/src/shared/human-presence.ts +30 -0
  209. package/src/shared/iframe-probe.ts +8 -0
  210. package/src/shared/scope-fence.ts +31 -0
  211. package/dist/types/client/canvas/DockedNode.d.ts +0 -4
  212. package/src/client/canvas/DockedNode.tsx +0 -165
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Session receipt (rail-chrome-v2 phase 5, design item 2): a dismissible card
3
+ * at the canvas region's top-right after a session ends — what the session did
4
+ * (items / done / vetoed), the pre-session snapshot (taken at attach, so View
5
+ * diff shows the session's changes and a restore undoes them), and History
6
+ * (the snapshots panel). Client-side state, cleared on dismiss.
7
+ */
8
+ export interface DiffSummary {
9
+ added: number;
10
+ removed: number;
11
+ modified: number;
12
+ }
13
+ /** The wire shape is SnapshotDiffResult (addedNodes/removedNodes/modifiedNodes/addedEdges/removedEdges). */
14
+ export declare function summarizeDiff(diff: unknown): DiffSummary | null;
15
+ export declare function SessionReceipt({ onOpenSnapshots }: {
16
+ onOpenSnapshots: () => void;
17
+ }): import("preact/src").JSX.Element | null;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * In-canvas replacement for `window.prompt`, which embedded browser panes
3
+ * silently no-op (a Shift+F that "does nothing") and no test can drive. One
4
+ * request at a time; a new ask cancels the previous one.
5
+ */
6
+ interface TextPromptRequest {
7
+ title: string;
8
+ placeholder: string;
9
+ /** Prefilled (and pre-selected) value — edit flows start from the current text. */
10
+ initial: string;
11
+ /** Submitting an empty field resolves '' instead of null — "clear the value" flows. */
12
+ allowEmpty: boolean;
13
+ confirm: string;
14
+ /** Bumped per ask so consecutive requests get a FRESH input element. */
15
+ seq: number;
16
+ resolve: (value: string | null) => void;
17
+ }
18
+ export declare const textPromptRequest: import("@preact/signals-core").Signal<TextPromptRequest | null>;
19
+ export declare function askText(title: string, placeholder: string, opts?: {
20
+ initial?: string;
21
+ allowEmpty?: boolean;
22
+ confirm?: string;
23
+ }): Promise<string | null>;
24
+ export declare function TextPrompt(): import("preact/src").JSX.Element | null;
25
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { AnnotationTool } from '../types';
2
+ /** Prompt-driven creates for node kinds that need a source (url / path). */
3
+ export declare function promptedCreate(kind: 'image' | 'file' | 'webpage'): void;
4
+ /**
5
+ * The persistent 52px left tool rail (rail-chrome-v2 phase 1): brand → tools →
6
+ * node palette → utilities. Every button shows a hover/focus tooltip with its
7
+ * shortcut (`RailButton`) — the rail is the shortcut discovery surface.
8
+ */
9
+ export declare function ToolRail({ minimapVisible, onToggleMinimap, snapshotOpen, onToggleSnapshot, snapshotBtnRef, onOpenPalette, onOpenShortcuts, annotationTool, onSetAnnotationTool, }: {
10
+ minimapVisible: boolean;
11
+ onToggleMinimap: () => void;
12
+ snapshotOpen: boolean;
13
+ onToggleSnapshot: () => void;
14
+ snapshotBtnRef: {
15
+ current: HTMLButtonElement | null;
16
+ };
17
+ onOpenPalette: () => void;
18
+ onOpenShortcuts: () => void;
19
+ annotationTool: AnnotationTool;
20
+ onSetAnnotationTool: (tool: AnnotationTool) => void;
21
+ }): import("preact/src").JSX.Element;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The slim 44px top bar (rail-chrome-v2 phase 1): connection state, board
3
+ * identity on the left, view controls on the right. `overflow:hidden` +
4
+ * `min-width:0` discipline throughout — the title ellipsizes, meta collapses
5
+ * below 1180px, and nothing ever wraps to a second row.
6
+ */
7
+ export declare function TopBar(): import("preact").JSX.Element;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The canvas region's box in screen coordinates. With the rail + top-bar shell
3
+ * the canvas no longer spans the window, so every "centre of the view" and
4
+ * "how big is the view" computation must use this region — anchoring zoom at
5
+ * `window.innerWidth / 2` would drift the board toward the rail on every step,
6
+ * and reporting the window to the server makes an agent `fit` compute a scale
7
+ * that hides nodes under the chrome.
8
+ *
9
+ * The shell registers its canvas-region element on mount; everything else asks
10
+ * for the rect. The window fallback keeps unit tests and any chromeless embed
11
+ * working unchanged.
12
+ */
13
+ export interface CanvasAreaRect {
14
+ left: number;
15
+ top: number;
16
+ width: number;
17
+ height: number;
18
+ }
19
+ export declare function registerCanvasArea(el: HTMLElement | null): void;
20
+ export declare function canvasArea(): CanvasAreaRect;
21
+ /** Screen-space centre of the canvas region — the anchor for centred zoom/focus. */
22
+ export declare function canvasAreaCenter(): {
23
+ x: number;
24
+ y: number;
25
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * A human create from the rail, keyboard, palette, or empty state lands
3
+ * centred in the CURRENT viewport — where the human is looking — instead of
4
+ * at the server's board-relative auto-placement, which on a busy board can
5
+ * be far off-screen (the rail's Group landed out of view). Explicit x/y win.
6
+ */
7
+ export declare function createNodeInView(opts: {
8
+ type: string;
9
+ title?: string;
10
+ content?: string;
11
+ x?: number;
12
+ y?: number;
13
+ width?: number;
14
+ height?: number;
15
+ }): Promise<{
16
+ ok: boolean;
17
+ id?: string;
18
+ }>;
@@ -0,0 +1,8 @@
1
+ export declare function nodeTypeFromFilename(name: string): 'image' | 'markdown' | 'file';
2
+ /**
3
+ * Turn local files into nodes laid out in a grid around a world point — the
4
+ * one implementation behind the viewport's drop zone and the empty state's
5
+ * file picker. Images become image nodes (data URI), markdown becomes
6
+ * markdown, everything else a file node with the text inlined.
7
+ */
8
+ export declare function importFiles(files: File[], baseWx: number, baseWy: number): Promise<void>;
@@ -0,0 +1,3 @@
1
+ import type { CanvasNodeState } from '../types';
2
+ /** Kind color per node type, as CSS tokens so every theme keeps its palette. */
3
+ export declare const KIND_COLOR: Record<CanvasNodeState['type'], string>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Focus trap for modal overlays (rail-chrome-v2 item 18): while `active`, Tab
3
+ * cycles inside `ref`, the first focusable takes focus on open unless
4
+ * `initial` names one, and the element focused before the overlay opened gets
5
+ * focus back on close.
6
+ */
7
+ export declare function useFocusTrap(ref: {
8
+ current: HTMLElement | null;
9
+ }, active: boolean, options?: {
10
+ initial?: {
11
+ current: HTMLElement | null;
12
+ };
13
+ restoreTo?: () => HTMLElement | null;
14
+ }): void;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * A ticking "now" for countdowns and relative ages. `intervalMs` 0 stops the
3
+ * ticker (the value still reads as the mount time) — pass 0 while the surface
4
+ * that needs it is hidden so an idle page runs no timers.
5
+ */
6
+ export declare function useNow(intervalMs?: number): number;
@@ -5,8 +5,6 @@ interface IconProps {
5
5
  }
6
6
  /** Expand-arrows — fit all nodes */
7
7
  export declare function IconFitAll(p: IconProps): JSX.Element;
8
- /** Framed crosshair — reset view 1:1 */
9
- export declare function IconResetView(p: IconProps): JSX.Element;
10
8
  /** Magnifier with + */
11
9
  export declare function IconZoomIn(p: IconProps): JSX.Element;
12
10
  /** Magnifier with - */
@@ -35,13 +33,22 @@ export declare function IconClearTrace(p: IconProps): JSX.Element;
35
33
  export declare function IconSearch(p: IconProps): JSX.Element;
36
34
  /** Keyboard — shortcuts */
37
35
  export declare function IconShortcuts(p: IconProps): JSX.Element;
38
- /** Half-lit circle — theme toggle (generic). */
39
- export declare function IconTheme(p: IconProps): JSX.Element;
36
+ /** Speech bubble — steer the agent (command bar composer). */
37
+ export declare function IconSteer(p: IconProps): JSX.Element;
40
38
  /** Framed pin — context pinning. */
41
39
  export declare function IconPin(p: IconProps): JSX.Element;
42
40
  /** Focus Field — PMX Canvas brand mark (concentric rounded squares → lit core). */
43
41
  export declare function IconLogo({ size, class: className }: IconProps): JSX.Element;
44
42
  /** Framed document with lines — markdown */
43
+ export declare function IconCursorTool({ size }: {
44
+ size?: number;
45
+ }): JSX.Element;
46
+ export declare function IconHandTool({ size }: {
47
+ size?: number;
48
+ }): JSX.Element;
49
+ export declare function IconNodeHtml({ size }: {
50
+ size?: number;
51
+ }): JSX.Element;
45
52
  export declare function IconNodeMarkdown(p: IconProps): JSX.Element;
46
53
  /** Framed prompt with chevron and reply tail — prompt */
47
54
  export declare function IconNodePrompt(p: IconProps): JSX.Element;
@@ -18,6 +18,38 @@ interface ExtAppHostDimensionsTarget {
18
18
  * everywhere we can test (Chrome / Codex / Playwright).
19
19
  */
20
20
  export declare function isWebKitOnlyHost(userAgent: string): boolean;
21
+ /**
22
+ * Finding N (0.4.7 report): the GitHub Copilot panel's WKWebView reports
23
+ * `document.visibilityState === 'hidden'` CONTINUOUSLY — the workbench document
24
+ * never transitions to 'visible' even with the panel open and on-screen. Two
25
+ * things break as a result: the `visibilitychange` re-arm below can never fire
26
+ * (there is no transition to listen for), and `paint-ok` becomes a false green —
27
+ * the app document happily answers the double-rAF paint probe while the
28
+ * compositor still shows black. The instrumented trail from that session carries
29
+ * `visibility: "hidden"` on essentially every recovery event.
30
+ *
31
+ * So: when the document CLAIMS hidden but the frame is demonstrably on-screen,
32
+ * do not trust the claim — spend one more recovery round as if visible. A host
33
+ * that ever reports 'visible' has a working signal and is left to the
34
+ * visibilitychange path, which keeps a genuinely backgrounded panel quiet.
35
+ */
36
+ export declare function shouldAssumeVisibleRearm(options: {
37
+ userAgent: string;
38
+ visibilityState: string;
39
+ rect: {
40
+ top: number;
41
+ left: number;
42
+ bottom: number;
43
+ right: number;
44
+ width: number;
45
+ height: number;
46
+ };
47
+ viewport: {
48
+ width: number;
49
+ height: number;
50
+ };
51
+ alreadyRearmed: boolean;
52
+ }): boolean;
21
53
  export declare const WEBKIT_REMOUNT_SETTLE_MS = 1000;
22
54
  export interface WebkitRemountTask {
23
55
  /** Perform the remount. Return false if the node no longer needs it (skips the boot wait). */
@@ -2,5 +2,10 @@ import type { CanvasNodeState } from '../types';
2
2
  interface GroupNodeProps {
3
3
  node: CanvasNodeState;
4
4
  }
5
- export declare function GroupNode({ node }: GroupNodeProps): import("preact/src").JSX.Element;
5
+ /**
6
+ * Group frame body (rail-chrome-v2 groups v2): the children live in the world
7
+ * layer and the name/count/actions sit on the frame edge, so the body is just
8
+ * the wash — plus a hint while the group is empty.
9
+ */
10
+ export declare function GroupNode({ node }: GroupNodeProps): import("preact/src").JSX.Element | null;
6
11
  export {};
@@ -1,5 +1,7 @@
1
1
  import type { JSX } from 'preact';
2
2
  import type { CanvasNodeState } from '../types';
3
+ /** Flip the index-th GFM task marker in the source (document order matches the rendered boxes). */
4
+ export declare function toggleTaskMarker(markdown: string, index: number): string;
3
5
  export declare function MarkdownNode({ node, expanded }: {
4
6
  node: CanvasNodeState;
5
7
  expanded?: boolean;
@@ -1,9 +1,8 @@
1
- /** Shared contentEditable command helpers used by both the inline editor
2
- * and its floating toolbar. Kept in a module that neither component
3
- * depends on transitively so we don't get a circular import. */
4
1
  /** Prompt for a URL and insert it as a link on the current selection.
5
2
  * Rejects `javascript:` and `data:` schemes so a link can't execute script
6
- * when clicked. */
3
+ * when clicked. Uses the in-canvas prompt (`window.prompt` is a silent no-op
4
+ * in embedded panes); the selection is saved before the dialog takes focus
5
+ * and restored before inserting. */
7
6
  export declare function promptAndInsertLink(): void;
8
7
  /** Wrap the current non-empty selection in an inline `<code>` element and
9
8
  * place the caret immediately after the new element. No-op on collapsed
@@ -4,8 +4,17 @@ export declare const nodes: import("@preact/signals-core").Signal<Map<string, Ca
4
4
  export declare const edges: import("@preact/signals-core").Signal<Map<string, CanvasEdge>>;
5
5
  export declare const annotations: import("@preact/signals-core").Signal<Map<string, CanvasAnnotation>>;
6
6
  export declare const activeNodeId: import("@preact/signals-core").Signal<string | null>;
7
+ /** Click-selected edge — Delete removes it, Escape/background click clears it. */
8
+ export declare const selectedEdgeId: import("@preact/signals-core").Signal<string | null>;
7
9
  export declare const connectionStatus: import("@preact/signals-core").Signal<ConnectionStatus>;
8
10
  export declare const workbenchConnectionEpoch: import("@preact/signals-core").Signal<number>;
11
+ /**
12
+ * Degraded-connection detail (rail-chrome-v2 phase 7, design item 14):
13
+ * the reconnect attempt and the delay before the next try while the stream is
14
+ * down. Both reset to 0 when a transport is back.
15
+ */
16
+ export declare const reconnectAttempt: import("@preact/signals-core").Signal<number>;
17
+ export declare const reconnectDelay: import("@preact/signals-core").Signal<number>;
9
18
  export declare const sessionId: import("@preact/signals-core").Signal<string>;
10
19
  export declare const traceEnabled: import("@preact/signals-core").Signal<boolean>;
11
20
  export declare const canvasTheme: import("@preact/signals-core").Signal<string>;
@@ -22,21 +31,60 @@ export declare const draggingEdge: import("@preact/signals-core").Signal<{
22
31
  fromY: number;
23
32
  cursorX: number;
24
33
  cursorY: number;
34
+ /** Node under the cursor that would receive the edge on release (item 15). */
35
+ targetId?: string | null;
36
+ /** L was pressed during the drag: ask for a label on release. */
37
+ withLabel?: boolean;
25
38
  } | null>;
26
39
  export declare const searchHighlightIds: import("@preact/signals-core").Signal<Set<string> | null>;
27
40
  export declare const selectedNodeIds: import("@preact/signals-core").Signal<Set<string>>;
28
41
  export declare const contextPinnedNodeIds: import("@preact/signals-core").Signal<Set<string>>;
42
+ /** `connect` (rail-chrome-v2 item 15): drag from anywhere on a node to draw an edge. */
43
+ export type CanvasTool = 'select' | 'pan' | 'connect';
44
+ export declare const canvasTool: import("@preact/signals-core").Signal<CanvasTool>;
45
+ /** Held-Space temporary pan — same semantics as the pan tool while held. */
46
+ export declare const spacePanHeld: import("@preact/signals-core").Signal<boolean>;
47
+ export declare function isPanModeActive(): boolean;
29
48
  export declare function getNeighborNodeIds(nodeId: string | null, edgeMap: Map<string, CanvasEdge>): Set<string>;
30
49
  export declare const activeNeighborNodeIds: import("@preact/signals-core").ReadonlySignal<Set<string>>;
31
50
  export declare function toggleSelected(id: string): void;
32
51
  export declare function selectNodes(ids: string[]): void;
33
52
  export declare function clearSelection(): void;
53
+ /**
54
+ * Membership feedback while a node is being dragged: the group it would join
55
+ * on release (`add`), or the parent it would leave (`remove`). Membership
56
+ * changes ONLY on release while this is set — never silently by geometry.
57
+ * Esc during the drag clears it and keeps it cleared for that drag.
58
+ */
59
+ export declare const dragDropTarget: import("@preact/signals-core").Signal<{
60
+ nodeId: string;
61
+ groupId: string;
62
+ mode: "add" | "remove";
63
+ } | null>;
64
+ export declare function suppressDropForDrag(): void;
65
+ export declare function endDropTracking(): void;
66
+ /**
67
+ * Called on every drag move of `nodeId`: updates the drop target and grows a
68
+ * fit-mode parent frame live so a child dragged against it never clips
69
+ * (the server re-fits on persist; shrinking happens there).
70
+ */
71
+ export declare function trackDragMembership(nodeId: string): void;
72
+ /** Children of collapsed groups are hidden; edges to them point at the chip. */
73
+ export declare const hiddenByCollapsedGroup: import("@preact/signals-core").ReadonlySignal<Map<string, string>>;
74
+ /** The node that stands in for `id` on the canvas: itself, or its collapsed group's chip. */
75
+ export declare function visibleNodeFor(id: string): CanvasNodeState | undefined;
76
+ /** Collapsed groups render as a chip; children keep their positions for restore. */
77
+ export declare function groupsOfSelection(): string[];
78
+ export declare function alignSelection(edge: 'left' | 'top'): void;
79
+ /** Even horizontal gaps between the selected nodes, first and last staying put. */
80
+ export declare function distributeSelection(): void;
81
+ /** Grid the selection in reading order from its own top-left corner. */
82
+ export declare function arrangeSelection(gap?: number): void;
34
83
  export declare function getSelectedNodes(): CanvasNodeState[];
35
84
  export declare function toggleContextPin(id: string): void;
36
85
  export declare function addContextPins(ids: string[]): void;
37
86
  export declare function clearContextPins(): void;
38
87
  export declare function replaceContextPinsFromServer(ids: string[]): void;
39
- export declare function getContextPinnedNodes(): CanvasNodeState[];
40
88
  export declare function addNode(node: CanvasNodeState): void;
41
89
  export declare function updateNode(id: string, patch: Partial<CanvasNodeState>): void;
42
90
  export declare function updateNodeData(id: string, dataPatch: Record<string, unknown>): void;
@@ -65,10 +113,6 @@ export declare function resizeNode(id: string, size: {
65
113
  }): void;
66
114
  export declare function bringToFront(id: string): void;
67
115
  export declare function toggleCollapsed(id: string): void;
68
- export declare function collapseDockedContextNodes(): void;
69
- export declare const hasOpenDockedContextPanel: import("@preact/signals-core").ReadonlySignal<boolean>;
70
- export declare function dockNode(id: string, position: 'left' | 'right'): void;
71
- export declare function undockNode(id: string): void;
72
116
  export declare function setViewport(v: Partial<ViewportState>): void;
73
117
  export declare function replaceViewport(next: ViewportState): void;
74
118
  export declare function commitViewport(next: ViewportState): void;
@@ -0,0 +1,26 @@
1
+ import { type HumanPresence, type HumanPresenceSnapshot } from '../../shared/human-presence.js';
2
+ export declare const humanClientId: string;
3
+ export declare const humanName: import("@preact/signals-core").Signal<string>;
4
+ export declare const humans: import("@preact/signals-core").Signal<HumanPresence[]>;
5
+ export declare const otherHumans: import("@preact/signals-core").ReadonlySignal<HumanPresence[]>;
6
+ /** Nodes a human took over mid-edit → that human's name, shown as the yield pill for a moment. */
7
+ export declare const yieldedNodes: import("@preact/signals-core").Signal<Map<string, string>>;
8
+ export declare function applyHumanSnapshot(snapshot: Partial<HumanPresenceSnapshot> | null | undefined): void;
9
+ /** Report the pointer in world coordinates (throttled); null when it leaves the canvas. */
10
+ export declare function reportHumanCursor(cursor: {
11
+ x: number;
12
+ y: number;
13
+ } | null): void;
14
+ /** Hold / release a node (drag, rename): the edit lock agents must respect. */
15
+ export declare function reportHumanGrab(nodeId: string | null): void;
16
+ export declare function markYielded(nodeId: string, name: string): void;
17
+ /**
18
+ * User wins (item 6): the human grabbed a node the agent is mid-edit on. Every
19
+ * explicit intent targeting it is vetoed through the normal veto path (the
20
+ * agent hears it as steering), a Yield entry lands in the timeline, and the
21
+ * node wears a "took over — agent yielded" pill for a moment.
22
+ */
23
+ export declare function takeOverNode(nodeId: string, title: string): void;
24
+ /** Announce this tab and keep it alive; renews a held grab. Call once per connection. */
25
+ export declare function startHumanPresence(): () => void;
26
+ export declare function resetHumanPresence(): void;
@@ -21,6 +21,7 @@ export declare function probeSrcIframes(timeoutMs?: number): Promise<boolean>;
21
21
  export declare function resolveIframeMode(opts?: {
22
22
  embedded?: boolean;
23
23
  ampOrb?: boolean;
24
+ probeTimeoutMs?: number;
24
25
  }): Promise<IframeMode>;
25
26
  /** Test hook: clear the memoized probe so each test starts unresolved. */
26
27
  export declare function resetIframeModeForTests(): void;
@@ -1,3 +1,16 @@
1
+ import type { AgentPresenceSnapshot } from '../../shared/agent-presence.js';
2
+ /**
3
+ * Per-tab identity: an AGENT driving the workbench UI from its own pane opens
4
+ * `/workbench?agent=<key>` and every write from that tab is booked as that
5
+ * agent — before this, UI-driven agent tests carried the human marker and the
6
+ * human was told "you rejected a gate" they never saw (0.5.0 readiness).
7
+ */
8
+ export declare const tabAgentKey: string | null;
9
+ export declare function requestJson<T>(action: string, url: string, fallback: T, init?: RequestInit): Promise<T>;
10
+ export declare function requestOk(action: string, url: string, init?: RequestInit): Promise<{
11
+ ok: boolean;
12
+ }>;
13
+ export declare function requestBestEffort(action: string, url: string, init?: RequestInit): Promise<void>;
1
14
  /** Dispatch user intents from the canvas to the server (for TUI consumption). */
2
15
  export declare function sendIntent(type: string, payload?: Record<string, unknown>): Promise<{
3
16
  ok: boolean;
@@ -68,7 +81,6 @@ export declare function pushCanvasUpdate(updates: Array<{
68
81
  height: number;
69
82
  };
70
83
  collapsed?: boolean;
71
- dockPosition?: 'left' | 'right' | null;
72
84
  }>, options?: {
73
85
  recordHistory?: boolean;
74
86
  }): Promise<void>;
@@ -102,10 +114,11 @@ export declare function updateNodeFromClient(id: string, patch: {
102
114
  };
103
115
  collapsed?: boolean;
104
116
  pinned?: boolean;
105
- dockPosition?: 'left' | 'right' | null;
106
117
  title?: string;
107
118
  content?: string;
108
119
  data?: Record<string, unknown>;
120
+ /** Groups: replace the membership list (positions preserved). */
121
+ children?: string[];
109
122
  }): Promise<{
110
123
  ok: boolean;
111
124
  id?: string;
@@ -138,6 +151,8 @@ export interface AxInteractionResponse {
138
151
  }
139
152
  /** Fetch the compact AX state snapshot pushed into AX-enabled surfaces. */
140
153
  export declare function fetchAxSurfaceState(): Promise<unknown>;
154
+ /** Connect-time agent-presence read; live updates arrive as `agent-presence` SSE frames. */
155
+ export declare function fetchAgentPresence(): Promise<Partial<AgentPresenceSnapshot> | null>;
141
156
  /** Ask the server to open a node's surface in the system browser. */
142
157
  export declare function openNodeInSystemBrowserRequest(nodeId: string, url?: string): Promise<{
143
158
  ok: boolean;
@@ -177,11 +192,19 @@ export declare function createGroupFromClient(opts: {
177
192
  ok: boolean;
178
193
  id?: string;
179
194
  }>;
180
- /** Add nodes to an existing group. */
181
- export declare function addToGroupFromClient(groupId: string, childIds: string[]): Promise<{
195
+ /** Add nodes to an existing group; `childLayout` packs the children (the header's auto-arrange). */
196
+ export declare function addToGroupFromClient(groupId: string, childIds: string[], childLayout?: 'grid' | 'column' | 'flow'): Promise<{
182
197
  ok: boolean;
183
198
  }>;
184
- /** Ungroup all children from a group. */
199
+ /** Replace a group's membership (positions preserved) — how a single child leaves. */
200
+ export declare function setGroupChildrenFromClient(groupId: string, children: string[]): Promise<{
201
+ ok: boolean;
202
+ }>;
203
+ /**
204
+ * Ungroup = dissolve (children released, frame removed). The SAME server op
205
+ * an agent's `canvas_group { action: "ungroup" }` runs, so both sides get the
206
+ * identical board and one undo step.
207
+ */
185
208
  export declare function ungroupFromClient(groupId: string): Promise<{
186
209
  ok: boolean;
187
210
  }>;
@@ -203,6 +226,15 @@ export declare function restoreSnapshot(id: string): Promise<{
203
226
  export declare function deleteSnapshot(id: string): Promise<{
204
227
  ok: boolean;
205
228
  }>;
229
+ /** Patch an edge in place (label/type/style/animated) via the server. */
230
+ export declare function updateEdgeFromClient(edgeId: string, patch: {
231
+ label?: string;
232
+ type?: string;
233
+ style?: string;
234
+ animated?: boolean;
235
+ }): Promise<{
236
+ ok: boolean;
237
+ }>;
206
238
  /** Remove a canvas edge via the server. */
207
239
  export declare function removeEdgeFromClient(edgeId: string): Promise<{
208
240
  ok: boolean;
@@ -25,5 +25,4 @@ export declare function makeNodeState(id: string, type: CanvasNodeState['type'],
25
25
  width: number;
26
26
  height: number;
27
27
  };
28
- dockPosition?: 'left' | 'right' | null;
29
28
  }): CanvasNodeState;
@@ -0,0 +1,78 @@
1
+ import type { PmxAxIntent } from '../../shared/ax-intent.js';
2
+ import { type AgentActivityEntry, type AgentPresence, type AgentPresenceSnapshot, type ContextBudget } from '../../shared/agent-presence.js';
3
+ /**
4
+ * Agent presence (rail-chrome-v2 phase 2). Fed by the server's `agent-presence`
5
+ * SSE snapshot (every change, including TTL expiry) and the connect-time read,
6
+ * so this store never runs its own expiry ticker.
7
+ *
8
+ * `sessionActive` is the ONE selector every agent surface reads — the session
9
+ * panel, command bar, presence layer, and top-bar chip all mount on it.
10
+ */
11
+ export declare const agentPresences: import("@preact/signals-core").Signal<AgentPresence[]>;
12
+ export declare const contextBudget: import("@preact/signals-core").Signal<ContextBudget>;
13
+ /** Recent agent writes, newest first — the External Steering activity feed. */
14
+ export declare const agentActivity: import("@preact/signals-core").Signal<AgentActivityEntry[]>;
15
+ /** External Steering chrome (phase 6): the feed popover and the writers sheet. */
16
+ export declare const activityFeedOpen: import("@preact/signals-core").Signal<boolean>;
17
+ export declare const writersSheetOpen: import("@preact/signals-core").Signal<boolean>;
18
+ /** Feed filter: a writer's sessionId, or null for all. */
19
+ export declare const activityFilter: import("@preact/signals-core").Signal<string | null>;
20
+ export declare function writerColor(sessionId: string): string;
21
+ /** Avatar initial: first letter of the label, upper-cased. */
22
+ export declare function writerInitial(label: string): string;
23
+ /** Compact relative age for feed rows and the writers sheet: now · 12s · 4m · 2h. */
24
+ export declare function relativeAge(iso: string, now?: number): string;
25
+ /** Master gate for every agent surface: an attached session exists. */
26
+ export declare const sessionActive: import("@preact/signals-core").ReadonlySignal<boolean>;
27
+ /** Live writers with no attached session — the External Steering mode. */
28
+ export declare const externalWriterPresences: import("@preact/signals-core").ReadonlySignal<AgentPresence[]>;
29
+ /**
30
+ * Connected agents the human can ADDRESS from the composer: every live
31
+ * presence (attached sessions first). `label` is the display name; `value` is
32
+ * the CONSUMER key the steer must be targeted at — the identity the agent
33
+ * claims deliveries with. For an adapter session that is its source key
34
+ * (Copilot attaches as source "copilot" labelled "GitHub Copilot"); for an
35
+ * adopted human-started session the sessionId is "browser", so the writer's
36
+ * label IS its consumer key. Addressing the display label instead would
37
+ * produce steering nobody can ever claim. The un-adopted placeholder is
38
+ * excluded: no consumer will ever claim as "Agent session".
39
+ */
40
+ export declare const steerableAgents: import("@preact/signals-core").ReadonlySignal<{
41
+ value: string;
42
+ label: string;
43
+ attached: boolean;
44
+ steerable: boolean;
45
+ pendingSteers: number;
46
+ polling: boolean;
47
+ }[]>;
48
+ /** The attached session's presence (first if several hosts attached). */
49
+ /** Every attached session, in presence order — the top bar shows one chip each. */
50
+ export declare const attachedSessions: import("@preact/signals-core").ReadonlySignal<AgentPresence[]>;
51
+ export declare const activeSession: import("@preact/signals-core").ReadonlySignal<AgentPresence | null>;
52
+ /**
53
+ * Nodes an agent is mutating RIGHT NOW — drives the shimmer. Derived from the
54
+ * intent store's in-flight ghosts (move / edit / remove target an existing
55
+ * node) rather than a parallel source; gated on `sessionActive` at the use
56
+ * site so the quiet board never shimmers.
57
+ */
58
+ export declare function mutatingNodeIdsFrom(intentList: Iterable<Pick<PmxAxIntent, 'kind' | 'nodeId'>>): Set<string>;
59
+ export declare const mutatingNodeIds: import("@preact/signals-core").ReadonlySignal<Set<string>>;
60
+ /**
61
+ * Where a presence's cursor sits in WORLD coordinates: an explicit cursor
62
+ * wins; otherwise the node the agent last touched (anchored near its title
63
+ * bar's right end, like a collaborator hovering the node); otherwise null.
64
+ */
65
+ export declare function presenceWorldPosition(presence: Pick<AgentPresence, 'cursor' | 'focusNodeId'>, nodeById: (id: string) => {
66
+ position: {
67
+ x: number;
68
+ y: number;
69
+ };
70
+ size: {
71
+ width: number;
72
+ };
73
+ } | undefined): {
74
+ x: number;
75
+ y: number;
76
+ } | null;
77
+ export declare function applyPresenceSnapshot(snapshot: Partial<AgentPresenceSnapshot> | null | undefined): void;
78
+ export declare function resetPresence(): void;