pmx-canvas 0.2.1 → 0.2.2
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 +86 -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 +197 -1305
- package/skills/pmx-canvas/evals/evals.json +199 -0
- package/skills/pmx-canvas/references/full-reference.md +1441 -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 +31 -22
- 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/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
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
import { fetchAxSurfaceState } from './intent-bridge';
|
|
29
29
|
import { invalidateTokenCache } from '../theme/tokens';
|
|
30
30
|
import { resetAttentionBridge, syncAttentionFromSse } from './attention-bridge';
|
|
31
|
+
import { dissolveIntent, resetIntents, settleIntent, upsertIntent } from './intent-store';
|
|
32
|
+
import type { PmxAxIntent } from '../../shared/ax-intent.js';
|
|
31
33
|
|
|
32
34
|
let eventSource: EventSource | null = null;
|
|
33
35
|
let savedLayout: Map<string, Partial<CanvasNodeState>> | null = null;
|
|
@@ -941,6 +943,32 @@ function handleAxStateChanged(): void {
|
|
|
941
943
|
}, 150);
|
|
942
944
|
}
|
|
943
945
|
|
|
946
|
+
// ── Ghost Cursor of Intent ────────────────────────────────────
|
|
947
|
+
function handleAxIntent(data: Record<string, unknown>): void {
|
|
948
|
+
const intent = data.intent as PmxAxIntent | undefined;
|
|
949
|
+
// Require a numeric `expiresAt`: the client-side TTL prune backstop
|
|
950
|
+
// (intent-store) compares `expiresAt <= now`, so a frame missing it would never
|
|
951
|
+
// be pruned if its `clear` frame were dropped. The server always sets it, so this
|
|
952
|
+
// only rejects a malformed frame — keeping the backstop's guarantee real.
|
|
953
|
+
if (
|
|
954
|
+
!intent
|
|
955
|
+
|| typeof intent.id !== 'string'
|
|
956
|
+
|| typeof intent.kind !== 'string'
|
|
957
|
+
|| typeof intent.expiresAt !== 'number'
|
|
958
|
+
) return;
|
|
959
|
+
upsertIntent(intent);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function handleAxIntentClear(data: Record<string, unknown>): void {
|
|
963
|
+
const id = typeof data.id === 'string' ? data.id : '';
|
|
964
|
+
if (!id) return;
|
|
965
|
+
if (data.settled === true) {
|
|
966
|
+
settleIntent(id, typeof data.nodeId === 'string' ? data.nodeId : undefined);
|
|
967
|
+
} else {
|
|
968
|
+
dissolveIntent(id);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
944
972
|
// ── SSE connection ────────────────────────────────────────────
|
|
945
973
|
/** @internal — exported for testing */
|
|
946
974
|
export const EVENT_HANDLERS: Record<string, (data: Record<string, unknown>) => void> = {
|
|
@@ -976,6 +1004,8 @@ export const EVENT_HANDLERS: Record<string, (data: Record<string, unknown>) => v
|
|
|
976
1004
|
'canvas-response-complete': handleCanvasResponseComplete,
|
|
977
1005
|
'ax-state-changed': handleAxStateChanged,
|
|
978
1006
|
'ax-event-created': handleAxStateChanged,
|
|
1007
|
+
'ax-intent': handleAxIntent,
|
|
1008
|
+
'ax-intent-clear': handleAxIntentClear,
|
|
979
1009
|
};
|
|
980
1010
|
|
|
981
1011
|
export function connectSSE(): () => void {
|
|
@@ -983,6 +1013,7 @@ export function connectSSE(): () => void {
|
|
|
983
1013
|
ensureStatusNode();
|
|
984
1014
|
hasInitialServerLayout.value = false;
|
|
985
1015
|
resetAttentionBridge();
|
|
1016
|
+
resetIntents();
|
|
986
1017
|
if (reconnectTimer) {
|
|
987
1018
|
clearTimeout(reconnectTimer);
|
|
988
1019
|
reconnectTimer = null;
|
|
@@ -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
|
+
}
|
|
@@ -118,6 +118,11 @@ export function useChartFrameHeight(explicitHeight: number | null | undefined, f
|
|
|
118
118
|
const [autoHeight, setAutoHeight] = useState(fallbackHeight);
|
|
119
119
|
const [autoWidth, setAutoWidth] = useState(0);
|
|
120
120
|
|
|
121
|
+
// Standalone "Open as site" tab (#65): fill the full browser viewport — there is no
|
|
122
|
+
// card chrome below the chart, so drop the ~44px reserve and use a larger floor.
|
|
123
|
+
const isSite = typeof window !== 'undefined'
|
|
124
|
+
&& (window as { __PMX_CANVAS_JSON_RENDER_DISPLAY__?: string }).__PMX_CANVAS_JSON_RENDER_DISPLAY__ === 'site';
|
|
125
|
+
|
|
121
126
|
useEffect(() => {
|
|
122
127
|
const frame = frameRef.current;
|
|
123
128
|
if (!frame) return;
|
|
@@ -136,7 +141,11 @@ export function useChartFrameHeight(explicitHeight: number | null | undefined, f
|
|
|
136
141
|
// across node sizes). rect.top already accounts for everything above. With
|
|
137
142
|
// too small a reserve a filled chart spills ~17px past the viewport and the
|
|
138
143
|
// iframe document shows a needless scrollbar.
|
|
139
|
-
|
|
144
|
+
// Keep the ~44px reserve in BOTH modes — it covers the chart frame's own
|
|
145
|
+
// non-plot chrome (title + .pmx-chart padding), which exists in site mode too.
|
|
146
|
+
// Dropping it pushed the frame past the viewport and reintroduced a scrollbar.
|
|
147
|
+
// Site mode differs only in the floor (300 vs 220) and the fill selection below.
|
|
148
|
+
const available = Math.max(isSite ? 300 : 220, Math.round(window.innerHeight - rect.top - 44));
|
|
140
149
|
const nextWidth = Math.round(rect.width);
|
|
141
150
|
// Dead-band: ignore sub-threshold churn so a stray re-measure (e.g. a
|
|
142
151
|
// scrollbar toggling) can't ping-pong state and repaint.
|
|
@@ -162,9 +171,14 @@ export function useChartFrameHeight(explicitHeight: number | null | undefined, f
|
|
|
162
171
|
// content-fit (strictSize / user-resized nodes), it fills the frame down as before.
|
|
163
172
|
const fitContent = typeof window !== 'undefined'
|
|
164
173
|
&& (window as { __PMX_CANVAS_FIT_CONTENT__?: boolean }).__PMX_CANVAS_FIT_CONTENT__ === true;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
174
|
+
// Site mode (#65): fill the viewport (autoHeight), ignoring an explicit/configured
|
|
175
|
+
// chart height that would otherwise cap it to a shallow card. Content-fit is off in
|
|
176
|
+
// site mode (the server skips it), so site never takes the intrinsic-height branch.
|
|
177
|
+
const height = isSite
|
|
178
|
+
? autoHeight
|
|
179
|
+
: fitContent
|
|
180
|
+
? (typeof explicitHeight === 'number' ? explicitHeight : fallbackHeight)
|
|
181
|
+
: (typeof explicitHeight === 'number' ? Math.min(explicitHeight, autoHeight) : autoHeight);
|
|
168
182
|
return {
|
|
169
183
|
frameRef,
|
|
170
184
|
height,
|
|
@@ -180,8 +180,15 @@ function App() {
|
|
|
180
180
|
? { ...(spec.state ?? {}), ax: axState }
|
|
181
181
|
: spec.state ?? undefined;
|
|
182
182
|
|
|
183
|
+
// Standalone "Open as site" tab (#65): fill the browser viewport instead of the
|
|
184
|
+
// in-canvas card height. The chart child flex-grows; useChartFrameHeight measures
|
|
185
|
+
// the full viewport in this mode. Embedded/expanded keep the padded min-height box.
|
|
186
|
+
const isSite = window.__PMX_CANVAS_JSON_RENDER_DISPLAY__ === 'site';
|
|
187
|
+
const containerStyle = isSite
|
|
188
|
+
? { display: 'flex', flexDirection: 'column' as const, height: '100dvh', minHeight: '100dvh', padding: 0, boxSizing: 'border-box' as const }
|
|
189
|
+
: { minHeight: '100vh', padding: 16, boxSizing: 'border-box' as const };
|
|
183
190
|
return (
|
|
184
|
-
<div style={
|
|
191
|
+
<div style={containerStyle}>
|
|
185
192
|
<JSONUIProvider
|
|
186
193
|
registry={registry}
|
|
187
194
|
initialState={initialState}
|
|
@@ -189,7 +196,9 @@ function App() {
|
|
|
189
196
|
handlers={buildAxHandlers()}
|
|
190
197
|
>
|
|
191
198
|
<AxStateSync />
|
|
192
|
-
<
|
|
199
|
+
<div style={isSite ? { flex: 1, minHeight: 0 } : undefined}>
|
|
200
|
+
<Renderer spec={spec} registry={registry} loading={false} />
|
|
201
|
+
</div>
|
|
193
202
|
{window.__PMX_CANVAS_JSON_RENDER_DEVTOOLS__ ? (
|
|
194
203
|
<JsonRenderDevtools position="right" />
|
|
195
204
|
) : null}
|
|
@@ -940,7 +940,7 @@ export async function buildJsonRenderViewerHtml(options: {
|
|
|
940
940
|
title: string;
|
|
941
941
|
spec: JsonRenderSpec;
|
|
942
942
|
theme?: 'dark' | 'light' | 'high-contrast';
|
|
943
|
-
display?: 'expanded';
|
|
943
|
+
display?: 'expanded' | 'site';
|
|
944
944
|
devtools?: boolean;
|
|
945
945
|
nodeId?: string;
|
|
946
946
|
axToken?: string;
|