pmx-canvas 0.2.1 → 0.2.3
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/CHANGELOG.md +119 -0
- package/Readme.md +2 -2
- package/dist/canvas/global.css +260 -0
- package/dist/canvas/index.js +76 -76
- package/dist/json-render/index.js +2 -2
- package/dist/types/client/canvas/IntentLayer.d.ts +1 -0
- package/dist/types/client/state/intent-bridge.d.ts +10 -0
- package/dist/types/client/state/intent-store.d.ts +25 -0
- package/dist/types/json-render/server.d.ts +1 -1
- package/dist/types/server/index.d.ts +34 -4
- package/dist/types/server/intent-registry.d.ts +45 -0
- package/dist/types/server/operations/ops/intent.d.ts +2 -0
- package/dist/types/shared/ax-intent.d.ts +58 -0
- package/docs/mcp.md +21 -2
- package/docs/screenshot.png +0 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +200 -1305
- package/skills/pmx-canvas/evals/evals.json +255 -1
- package/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/jwt.ts +17 -0
- package/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/login.ts +12 -0
- package/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/middleware.ts +13 -0
- package/skills/pmx-canvas/evals/fixtures/code-exploration/src/routes/auth.ts +13 -0
- package/skills/pmx-canvas/evals/fixtures/investigation-board/src/handlers/users.ts +27 -0
- package/skills/pmx-canvas/references/full-reference.md +1445 -0
- package/src/cli/index.ts +21 -4
- package/src/client/canvas/CanvasNode.tsx +13 -13
- package/src/client/canvas/CanvasViewport.tsx +2 -0
- package/src/client/canvas/ContextMenu.tsx +25 -19
- package/src/client/canvas/IntentLayer.tsx +278 -0
- package/src/client/nodes/ExtAppFrame.tsx +32 -23
- package/src/client/state/intent-bridge.ts +31 -0
- package/src/client/state/intent-store.ts +107 -0
- package/src/client/state/sse-bridge.ts +31 -0
- package/src/client/theme/global.css +260 -0
- package/src/json-render/charts/components.tsx +18 -4
- package/src/json-render/renderer/index.tsx +11 -2
- package/src/json-render/server.ts +1 -1
- package/src/server/index.ts +240 -158
- package/src/server/intent-registry.ts +324 -0
- package/src/server/operations/composites.ts +11 -0
- package/src/server/operations/index.ts +2 -0
- package/src/server/operations/ops/edges.ts +1 -0
- package/src/server/operations/ops/groups.ts +3 -0
- package/src/server/operations/ops/intent.ts +132 -0
- package/src/server/operations/ops/json-render.ts +3 -0
- package/src/server/operations/ops/nodes.ts +3 -0
- package/src/server/operations/ops/webview.ts +15 -4
- package/src/server/operations/registry.ts +68 -3
- package/src/server/server.ts +40 -12
- package/src/shared/ax-intent.ts +64 -0
- package/src/shared/surface.ts +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,123 @@ All notable changes to `pmx-canvas` are documented here. This project follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.2.3] - 2026-06-23
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **`canvas_webview` `start` now returns parseable JSON on failure (report #66).** A start
|
|
13
|
+
failure (e.g. the Bun.WebView startup timeout in a headless host) surfaced the bare error
|
|
14
|
+
message as the MCP tool text, so a client expecting JSON choked with
|
|
15
|
+
`Unexpected token 'T', "Timed out "... is not valid JSON`. The failure path now returns
|
|
16
|
+
`{ ok:false, error, webview }` (still flagged `isError:true`), so callers can reliably
|
|
17
|
+
distinguish a failure/timeout from valid content.
|
|
18
|
+
The composite and the legacy `canvas_webview_start` share the fix.
|
|
19
|
+
- **Expanded ext-app clicks now wait for bridge readiness.** The iframe no longer accepts input
|
|
20
|
+
while reconnecting, preventing the first interaction from being lost during a frame remount.
|
|
21
|
+
|
|
22
|
+
### Docs
|
|
23
|
+
|
|
24
|
+
- **Documented #67 (standalone graph/json-render live-resize) as a host-browser limitation.** The
|
|
25
|
+
`display=site` chart reflows correctly on a live window resize in a normal browser (added an
|
|
26
|
+
e2e regression that resizes a loaded standalone surface in both directions and asserts the SVG
|
|
27
|
+
tracks the viewport with no horizontal overflow). The report's stale-until-reload behavior was
|
|
28
|
+
observed only in the Codex single-tab in-app browser, which does not deliver live-resize events
|
|
29
|
+
to the page (both width *and* height froze) — no canvas code can supply a resize signal the host
|
|
30
|
+
never sends. Skill **Known Limitations** + the Open-as-Site reference now recommend a system
|
|
31
|
+
browser for separate full-page viewing.
|
|
32
|
+
- **Skill eval suite 15 → 17, plus fixtures and a portability fix (report skill-audit addendum).**
|
|
33
|
+
Added a Ghost Cursor eval (`canvas_intent` signal → linked-settle → veto rejection) and a
|
|
34
|
+
standalone-surface eval (`display=site` URL, ext-app-not-open-as-site, host-resize caveat).
|
|
35
|
+
Bundled real fixture source files under `evals/fixtures/` for the investigation-board and
|
|
36
|
+
code-exploration evals (which referenced files that did not exist, so a real run failed on
|
|
37
|
+
missing fixtures rather than skill quality), and relaxed the pinned-context eval to accept the
|
|
38
|
+
host-equivalent read path (`canvas_ax_state` / HTTP) like the context-pin eval.
|
|
39
|
+
|
|
40
|
+
## [0.2.2] - 2026-06-22
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- **Ghost Cursor of Intent — a pre-commit presence layer.** An agent can announce the
|
|
45
|
+
spatial move it is *about* to make, so the canvas paints a faint, dashed "ghost"
|
|
46
|
+
placeholder *before* the real mutation lands — modeled on a multiplayer cursor
|
|
47
|
+
(ephemeral presence, never canvas state). An intent describes one of five moves
|
|
48
|
+
(`create` / `move` / `connect` / `remove` / `edit`) at a spatial anchor, with an
|
|
49
|
+
optional `label`, `reason` (why the agent wants it), `confidence` (0–1, drives ghost
|
|
50
|
+
opacity), and `seq` (staged-batch ordering). The human watching the browser can
|
|
51
|
+
**veto** a forming ghost (✕ on the ghost or Esc while hovered) before it happens — a
|
|
52
|
+
veto dissolves the ghost, queues a steering message back to the agent, and *poisons
|
|
53
|
+
that intent id* so the linked mutation is rejected. When the agent runs the real
|
|
54
|
+
mutation **linked** to the intent (by passing the returned `intent.id` as
|
|
55
|
+
`intentId`), the ghost **settles** into the real node automatically. Ghosts are never
|
|
56
|
+
persisted, never snapshotted, never in `canvas_get_layout`; they auto-expire (~8 s
|
|
57
|
+
default, 60 s max) and are count-capped (12 live, oldest evicted).
|
|
58
|
+
- **New MCP tool `canvas_intent`** (composite-only): `signal` (register/replace an
|
|
59
|
+
intent → `{ ok, intent }`), `update` (patch a live intent + reset its TTL), `clear`
|
|
60
|
+
(dissolve a ghost; `vetoed:true` for a human veto, `settledNodeId` to morph it into a
|
|
61
|
+
real node). The public MCP surface grows from 83 to 84 tools (15 composites).
|
|
62
|
+
- **New optional `intentId`** on the mutation tools/ops (`canvas_node`, `canvas_edge`,
|
|
63
|
+
`canvas_group`, `canvas_render`) and the matching SDK methods — passing it links the
|
|
64
|
+
mutation to a live ghost so it settles on success and is rejected if vetoed/expired.
|
|
65
|
+
Omitting it (every prior caller) is byte-identical to before.
|
|
66
|
+
- **New HTTP routes** `POST /api/canvas/ax/intent`, `PATCH`/`DELETE
|
|
67
|
+
/api/canvas/ax/intent/:id`; **new SSE frames** `ax-intent` / `ax-intent-clear`
|
|
68
|
+
(replayed to reconnecting browsers); **new SDK methods** `signalIntent` /
|
|
69
|
+
`updateIntent` / `clearIntent`. Purely additive — no existing behavior changes.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- **Daemon status reports its workspace identity.** `pmx-canvas serve status` now returns the
|
|
74
|
+
canonical `workspace` from the live `/health` response, so agents can safely verify that a
|
|
75
|
+
responsive listener belongs to the intended project before mutating its board.
|
|
76
|
+
- **Status nodes can be removed (report #64).** Status nodes now show the standard ×
|
|
77
|
+
remove control in their title bar (and a "Close" context-menu item) like every other
|
|
78
|
+
node type, with the same undo/history behavior — they were previously the only node
|
|
79
|
+
type with no removal control, so temporary/test status nodes accumulated with no way
|
|
80
|
+
to dismiss them from the node UI.
|
|
81
|
+
- **Right-click "Pin" now pins to the agent context set (report #63).** The node
|
|
82
|
+
context menu offered the niche arrange-lock as the obvious "Pin"/"Unpin" item while
|
|
83
|
+
the primary human→agent context pin was buried under "Add to context", so clicking
|
|
84
|
+
"Pin" appeared to do nothing. The menu now lists **"Pin as context" / "Unpin from
|
|
85
|
+
context"** first (matching the SelectionBar wording; drives the context count +
|
|
86
|
+
indicator), and the arrange-lock item is renamed **"Lock position (no auto-arrange)"**
|
|
87
|
+
and now persists like other layout mutations.
|
|
88
|
+
- **Hosted MCP-app nodes no longer offer a broken "Open as site" (report #61).** A
|
|
89
|
+
hosted ext-app (e.g. Excalidraw) is a live MCP-app shell that only renders with the
|
|
90
|
+
in-canvas AppBridge host; opening it as a standalone browser tab produced
|
|
91
|
+
`MCP error -32601: Method not found`. Such apps are no longer openable as a PMX site
|
|
92
|
+
(the control is hidden and the surface route returns a clean 404) — open them
|
|
93
|
+
externally through their own app, or view them in the canvas. Bundled web-artifacts
|
|
94
|
+
and url-backed viewers still open as a site.
|
|
95
|
+
- **Expanded MCP-app nodes no longer clip text (report #62).** The expand/collapse
|
|
96
|
+
host-context update measured the iframe synchronously, before the expanded overlay had
|
|
97
|
+
laid out, so an app like Excalidraw reflowed bound text against the stale inline size
|
|
98
|
+
and clipped the start of labels. The measurement + host-context send now wait for
|
|
99
|
+
layout (double rAF, mirroring the post-ready nudge) so the app gets the real expanded
|
|
100
|
+
dimensions.
|
|
101
|
+
- **Graph / json-render "Open as site" fills the browser viewport (report #65).** The
|
|
102
|
+
standalone viewer reused the in-canvas card height, so a graph opened as a site sat in
|
|
103
|
+
a shallow band at the top of a large window. The "Open as site" surface now serves the
|
|
104
|
+
viewer in a `display=site` mode that fills the viewport (flex `100dvh`, chart height
|
|
105
|
+
measured from the full window, resize-responsive) — distinct from the in-canvas
|
|
106
|
+
content-fit grow and the expanded-overlay fill, neither of which changes.
|
|
107
|
+
|
|
108
|
+
### Docs
|
|
109
|
+
|
|
110
|
+
- **Bundled skill audit fixes (skill-audit 2026-06-22).** `skills/pmx-canvas/SKILL.md`:
|
|
111
|
+
added a mandatory **workspace-identity preflight** (read `/health`, match
|
|
112
|
+
`workspace`, treat `responsive:true`+`pidRunning:false` as a stale listener,
|
|
113
|
+
isolate with an explicit `--port`) so an agent never mutates another project's canvas
|
|
114
|
+
via a leftover port-4313 daemon; a short **Quick Operating Path** front-door; corrected
|
|
115
|
+
the **MCP composite map to the current 15** (added `canvas_app` + `canvas_webview`,
|
|
116
|
+
`canvas_query validate`, `canvas_node`-creates-html/primitive) and reclassified the
|
|
117
|
+
folded standalones as deprecated→composite (removed in v0.3); and a **Known limitations
|
|
118
|
+
& host differences** section reflecting the #61–#65 fixes plus the remaining Codex
|
|
119
|
+
sandboxed-iframe automation limit. The eval suite grew from 7 to 15 cases (workspace
|
|
120
|
+
mismatch, current composites, board extension, verified pinning, status cleanup, AX
|
|
121
|
+
control surface, AX delivery, final cleanup/validate). The top-level skill is now a
|
|
122
|
+
224-line operational guide; the complete long-form manual lives in
|
|
123
|
+
`references/full-reference.md`.
|
|
124
|
+
|
|
8
125
|
## [0.2.1] - 2026-06-17
|
|
9
126
|
|
|
10
127
|
### Fixed
|
|
@@ -2271,6 +2388,8 @@ otherwise have to discover by trial and error.
|
|
|
2271
2388
|
- Regression coverage for snapshot flat-`id` aliases on both MCP and
|
|
2272
2389
|
HTTP surfaces, plus async / top-level-`await` WebView script bodies.
|
|
2273
2390
|
|
|
2391
|
+
[0.2.3]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.2.3
|
|
2392
|
+
[0.2.2]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.2.2
|
|
2274
2393
|
[0.2.1]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.2.1
|
|
2275
2394
|
[0.2.0]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.2.0
|
|
2276
2395
|
[0.1.36]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.36
|
package/Readme.md
CHANGED
|
@@ -105,7 +105,7 @@ the DB so SQLite WAL data is checkpointed into the file.
|
|
|
105
105
|
|
|
106
106
|
### 06 / Any agent
|
|
107
107
|
|
|
108
|
-
Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (
|
|
108
|
+
Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (84 tools,
|
|
109
109
|
14 resources, change notifications), the [CLI](docs/cli.md), the
|
|
110
110
|
[HTTP API](docs/http-api.md), or the [Bun SDK](docs/sdk.md). Works with
|
|
111
111
|
Claude Code, GitHub Copilot CLI, Codex, Cursor, Windsurf, or any agent
|
|
@@ -255,7 +255,7 @@ the agent can read `canvas://skills` and pull in companion skills
|
|
|
255
255
|
the three-tier visual matrix (json-render → html → web-artifact)
|
|
256
256
|
- **[CLI reference](docs/cli.md)** — full command surface, daemon mode,
|
|
257
257
|
watch streams, WebView automation
|
|
258
|
-
- **[MCP reference](docs/mcp.md)** —
|
|
258
|
+
- **[MCP reference](docs/mcp.md)** — 84 tools, 14 resources, change
|
|
259
259
|
notifications, node-type routing
|
|
260
260
|
- **[HTTP API](docs/http-api.md)** — REST endpoints, SSE, batch operations
|
|
261
261
|
- **[AX host-adapter contract](docs/ax-host-adapter-contract.md)** — how native
|
package/dist/canvas/global.css
CHANGED
|
@@ -3484,3 +3484,263 @@ button.welcome-hint:hover {
|
|
|
3484
3484
|
.image-node-zoom-reset:hover {
|
|
3485
3485
|
background: var(--c-surface-hover);
|
|
3486
3486
|
}
|
|
3487
|
+
|
|
3488
|
+
/* ── Ghost Cursor of Intent ─────────────────────────────────────
|
|
3489
|
+
Pre-commit presence: faint placeholders for the move the agent is about to
|
|
3490
|
+
make. Lives inside the canvas world transform (positions are world coords).
|
|
3491
|
+
The layer sits above nodes so remove/edit overlays read on top of their
|
|
3492
|
+
target, but is pointer-transparent except the info card + veto control. */
|
|
3493
|
+
.intent-layer {
|
|
3494
|
+
display: contents;
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
.intent-ghost {
|
|
3498
|
+
position: absolute;
|
|
3499
|
+
pointer-events: none;
|
|
3500
|
+
z-index: 100000;
|
|
3501
|
+
transition:
|
|
3502
|
+
left 480ms ease,
|
|
3503
|
+
top 480ms ease,
|
|
3504
|
+
width 480ms ease,
|
|
3505
|
+
height 480ms ease,
|
|
3506
|
+
opacity 180ms ease;
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
/* create / move destination — a dashed ghost node */
|
|
3510
|
+
.intent-ghost-box {
|
|
3511
|
+
border: 1.5px dashed var(--c-accent);
|
|
3512
|
+
border-radius: 10px;
|
|
3513
|
+
background: var(--c-accent-10);
|
|
3514
|
+
box-shadow: 0 0 0 1px var(--c-accent-10), 0 6px 22px var(--c-accent-10);
|
|
3515
|
+
animation: intent-breathe 2.1s ease-in-out infinite;
|
|
3516
|
+
overflow: visible;
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3519
|
+
.intent-ghost-titlebar {
|
|
3520
|
+
display: flex;
|
|
3521
|
+
align-items: center;
|
|
3522
|
+
gap: 6px;
|
|
3523
|
+
padding: 8px 10px;
|
|
3524
|
+
color: var(--c-accent);
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
.intent-ghost-icon {
|
|
3528
|
+
display: inline-flex;
|
|
3529
|
+
align-items: center;
|
|
3530
|
+
color: var(--c-accent);
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
.intent-ghost-badge {
|
|
3534
|
+
font-size: 11px;
|
|
3535
|
+
font-weight: 600;
|
|
3536
|
+
letter-spacing: 0.02em;
|
|
3537
|
+
text-transform: uppercase;
|
|
3538
|
+
color: var(--c-accent);
|
|
3539
|
+
opacity: 0.85;
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
@keyframes intent-breathe {
|
|
3543
|
+
0%, 100% { box-shadow: 0 0 0 1px var(--c-accent-10), 0 6px 22px var(--c-accent-10); }
|
|
3544
|
+
50% { box-shadow: 0 0 0 1px var(--c-accent-25), 0 8px 30px var(--c-accent-25); }
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
/* remove — a red crosshatch tombstone over the target */
|
|
3548
|
+
.intent-ghost-remove {
|
|
3549
|
+
border: 1.5px dashed var(--c-danger);
|
|
3550
|
+
border-radius: 10px;
|
|
3551
|
+
background-color: color-mix(in srgb, var(--c-danger) 8%, transparent);
|
|
3552
|
+
background-image: repeating-linear-gradient(
|
|
3553
|
+
45deg,
|
|
3554
|
+
color-mix(in srgb, var(--c-danger) 22%, transparent) 0,
|
|
3555
|
+
color-mix(in srgb, var(--c-danger) 22%, transparent) 2px,
|
|
3556
|
+
transparent 2px,
|
|
3557
|
+
transparent 9px
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
/* edit — a shimmer bar over the target */
|
|
3562
|
+
.intent-ghost-edit {
|
|
3563
|
+
border: 1.5px dashed var(--c-accent);
|
|
3564
|
+
border-radius: 10px;
|
|
3565
|
+
background: var(--c-accent-10);
|
|
3566
|
+
overflow: visible;
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
.intent-edit-bar {
|
|
3570
|
+
position: absolute;
|
|
3571
|
+
top: 0;
|
|
3572
|
+
left: 0;
|
|
3573
|
+
height: 3px;
|
|
3574
|
+
width: 100%;
|
|
3575
|
+
background: linear-gradient(90deg, transparent, var(--c-accent), transparent);
|
|
3576
|
+
animation: response-stream-pulse 1.5s ease-in-out infinite;
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3579
|
+
/* connect — info card anchored at the bezier midpoint */
|
|
3580
|
+
.intent-ghost-connect {
|
|
3581
|
+
display: flex;
|
|
3582
|
+
justify-content: center;
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3585
|
+
/* the info treatment: label + confidence chip, reason, seq, veto */
|
|
3586
|
+
.intent-info {
|
|
3587
|
+
position: absolute;
|
|
3588
|
+
top: 100%;
|
|
3589
|
+
left: 0;
|
|
3590
|
+
margin-top: 6px;
|
|
3591
|
+
display: flex;
|
|
3592
|
+
flex-direction: column;
|
|
3593
|
+
gap: 4px;
|
|
3594
|
+
pointer-events: auto;
|
|
3595
|
+
max-width: 280px;
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
.intent-ghost-connect .intent-info {
|
|
3599
|
+
position: static;
|
|
3600
|
+
margin-top: 0;
|
|
3601
|
+
align-items: center;
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
.intent-chip {
|
|
3605
|
+
display: inline-flex;
|
|
3606
|
+
align-items: center;
|
|
3607
|
+
gap: 6px;
|
|
3608
|
+
align-self: flex-start;
|
|
3609
|
+
padding: 3px 6px 3px 8px;
|
|
3610
|
+
border-radius: 999px;
|
|
3611
|
+
background: color-mix(in srgb, var(--c-panel-glass) 96%, transparent);
|
|
3612
|
+
backdrop-filter: blur(10px);
|
|
3613
|
+
border: 1px solid var(--c-accent-25);
|
|
3614
|
+
box-shadow: 0 4px 14px var(--c-shadow);
|
|
3615
|
+
color: var(--c-text);
|
|
3616
|
+
font-size: 12px;
|
|
3617
|
+
line-height: 1.2;
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
.intent-seq {
|
|
3621
|
+
display: inline-flex;
|
|
3622
|
+
align-items: center;
|
|
3623
|
+
justify-content: center;
|
|
3624
|
+
min-width: 16px;
|
|
3625
|
+
height: 16px;
|
|
3626
|
+
padding: 0 4px;
|
|
3627
|
+
border-radius: 999px;
|
|
3628
|
+
background: var(--c-accent);
|
|
3629
|
+
color: var(--c-bg);
|
|
3630
|
+
font-size: 10px;
|
|
3631
|
+
font-weight: 700;
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
.intent-chip-icon {
|
|
3635
|
+
display: inline-flex;
|
|
3636
|
+
align-items: center;
|
|
3637
|
+
color: var(--c-accent);
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3640
|
+
.intent-chip-label {
|
|
3641
|
+
font-weight: 600;
|
|
3642
|
+
white-space: nowrap;
|
|
3643
|
+
overflow: hidden;
|
|
3644
|
+
text-overflow: ellipsis;
|
|
3645
|
+
max-width: 180px;
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
.intent-confidence {
|
|
3649
|
+
font-size: 10px;
|
|
3650
|
+
font-variant-numeric: tabular-nums;
|
|
3651
|
+
color: var(--c-muted);
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3654
|
+
.intent-veto {
|
|
3655
|
+
display: inline-flex;
|
|
3656
|
+
align-items: center;
|
|
3657
|
+
justify-content: center;
|
|
3658
|
+
width: 16px;
|
|
3659
|
+
height: 16px;
|
|
3660
|
+
margin-left: 2px;
|
|
3661
|
+
padding: 0;
|
|
3662
|
+
border: none;
|
|
3663
|
+
border-radius: 999px;
|
|
3664
|
+
background: transparent;
|
|
3665
|
+
color: var(--c-muted);
|
|
3666
|
+
font-size: 11px;
|
|
3667
|
+
cursor: pointer;
|
|
3668
|
+
transition: background 120ms ease, color 120ms ease;
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
.intent-veto:hover {
|
|
3672
|
+
background: color-mix(in srgb, var(--c-danger) 18%, transparent);
|
|
3673
|
+
color: var(--c-danger);
|
|
3674
|
+
}
|
|
3675
|
+
|
|
3676
|
+
.intent-reason {
|
|
3677
|
+
align-self: flex-start;
|
|
3678
|
+
padding: 3px 8px;
|
|
3679
|
+
border-radius: 7px;
|
|
3680
|
+
background: color-mix(in srgb, var(--c-panel-glass) 92%, transparent);
|
|
3681
|
+
border: 1px solid var(--c-line);
|
|
3682
|
+
color: var(--c-muted);
|
|
3683
|
+
font-size: 11px;
|
|
3684
|
+
line-height: 1.35;
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
/* connect bezier + move trail (SVG) */
|
|
3688
|
+
.intent-line-layer path {
|
|
3689
|
+
fill: none;
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
.intent-line-layer {
|
|
3693
|
+
z-index: 99999;
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
.intent-edge {
|
|
3697
|
+
stroke-width: 2;
|
|
3698
|
+
stroke-dasharray: 6 5;
|
|
3699
|
+
animation: intent-dash 0.9s linear infinite;
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3702
|
+
.intent-edge.type-flow { stroke: var(--c-accent); }
|
|
3703
|
+
.intent-edge.type-depends-on { stroke: var(--c-warn); }
|
|
3704
|
+
.intent-edge.type-relation { stroke: var(--c-muted); }
|
|
3705
|
+
.intent-edge.type-references { stroke: var(--c-dim); }
|
|
3706
|
+
|
|
3707
|
+
.intent-trail {
|
|
3708
|
+
stroke: var(--c-accent);
|
|
3709
|
+
stroke-width: 2;
|
|
3710
|
+
stroke-dasharray: 5 5;
|
|
3711
|
+
animation: intent-dash 0.9s linear infinite;
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
.intent-arrow-head {
|
|
3715
|
+
fill: var(--c-accent);
|
|
3716
|
+
}
|
|
3717
|
+
|
|
3718
|
+
@keyframes intent-dash {
|
|
3719
|
+
to { stroke-dashoffset: -22; }
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3722
|
+
/* settle — the ghost becomes real, then clears */
|
|
3723
|
+
.intent-ghost.is-settling {
|
|
3724
|
+
animation: intent-settle 480ms ease forwards;
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
@keyframes intent-settle {
|
|
3728
|
+
0% { transform: scale(1); }
|
|
3729
|
+
45% { transform: scale(1.04); border-style: solid; opacity: 1; }
|
|
3730
|
+
100% { transform: scale(1); opacity: 0; }
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3733
|
+
/* dissolve — abandoned / vetoed / expired */
|
|
3734
|
+
.intent-ghost.is-dissolving {
|
|
3735
|
+
animation: intent-dissolve 320ms ease forwards;
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3738
|
+
@keyframes intent-dissolve {
|
|
3739
|
+
to { transform: scale(0.96); opacity: 0; filter: blur(2px); }
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
/* keep ghosts calm while a node is being dragged */
|
|
3743
|
+
html.is-node-dragging .intent-ghost,
|
|
3744
|
+
html.is-node-dragging .intent-line-layer {
|
|
3745
|
+
opacity: 0.5;
|
|
3746
|
+
}
|