@elytracms/studio 0.0.69 → 0.0.70
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/dist/components/app-views/studio-config.d.ts +22 -0
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +91 -29
- package/dist/components/builder/blocks-model.js +83 -54
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +40 -12
- package/dist/components/builder/blocks-panel.js +88 -34
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.d.ts +13 -23
- package/dist/components/builder/fixture-thumbnail.js +27 -99
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +14 -11
- package/dist/components/builder/spacing-handles.js +33 -57
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/session-expired-overlay.d.ts +16 -0
- package/dist/components/session-expired-overlay.js +22 -6
- package/dist/components/session-expired-overlay.js.map +1 -1
- package/dist/components/sign-in-view.d.ts +10 -1
- package/dist/components/sign-in-view.js +14 -3
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/customization.d.ts +9 -1
- package/dist/customization.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +6 -6
- package/dist/lib/i18n/messages.de.js +6 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +5 -0
- package/dist/lib/i18n/messages.en.js +6 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +10 -0
- package/dist/lib/workspace/index.js +11 -0
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/ui/dialog.d.ts +9 -1
- package/dist/ui/dialog.js +8 -2
- package/dist/ui/dialog.js.map +1 -1
- package/package.json +2 -2
|
@@ -7,8 +7,9 @@ import type { FrameScheduler, RectTracker } from './canvas-rect-tracking.js';
|
|
|
7
7
|
* container is selected on the canvas, draggable gap handles appear between
|
|
8
8
|
* its children, axis-aware via the shared `containerFlowAxis`. Dragging snaps
|
|
9
9
|
* through the EC-161 spacing-token scale (`lib/dnd/gap-drag` — pure math, no
|
|
10
|
-
* pixel value can escape it)
|
|
11
|
-
*
|
|
10
|
+
* pixel value can escape it); the static gap tags (EC-435) stay put and read
|
|
11
|
+
* the LIVE token + re-measured px during the drag (EC-438 — no
|
|
12
|
+
* pointer-following label). The drag previews imperatively on the rendered
|
|
12
13
|
* element; releasing commits exactly ONE builder dispatch — `setProp` with the
|
|
13
14
|
* token, or `removeProp` for "no gap", the identical values the inspector's
|
|
14
15
|
* gap select writes — so one undo restores, and the graph stays the single
|
|
@@ -96,16 +97,17 @@ export interface SpacingHandlesViewProps {
|
|
|
96
97
|
spots: HandleSpotView[];
|
|
97
98
|
horizontal: boolean;
|
|
98
99
|
/**
|
|
99
|
-
* Token label for the container's
|
|
100
|
-
* plain-language vocabulary the layout inspector
|
|
101
|
-
*
|
|
100
|
+
* Token label for the container's COMMITTED gap (EC-435 AD-2) — the SAME
|
|
101
|
+
* plain-language vocabulary the layout inspector resolves (`gapStepLabel`),
|
|
102
|
+
* stamped on every hatched gap region as a static size tag while idle.
|
|
102
103
|
*/
|
|
103
104
|
gapLabel: string;
|
|
104
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* The live drag: the active handle index (tint) and the live step's token
|
|
107
|
+
* label — the static tags read it in place while dragging (EC-438).
|
|
108
|
+
*/
|
|
105
109
|
drag: {
|
|
106
110
|
index: number;
|
|
107
|
-
labelX: number;
|
|
108
|
-
labelY: number;
|
|
109
111
|
label: string;
|
|
110
112
|
} | null;
|
|
111
113
|
onPointerDown: (spot: HandleSpotView) => (e: React.PointerEvent<HTMLDivElement>) => void;
|
|
@@ -122,9 +124,10 @@ export interface SpacingHandlesViewProps {
|
|
|
122
124
|
* EC-435 AD-2: every open gap region renders a pointer-transparent hatched
|
|
123
125
|
* rectangle (diagonal accent stripes, Figma-grid style) under the handles,
|
|
124
126
|
* each with a small static token-label size tag beside its handle. During a
|
|
125
|
-
* gap drag the
|
|
126
|
-
*
|
|
127
|
-
* the gap handles remain the
|
|
127
|
+
* gap drag the tags STAY PUT and read live (EC-438 — a gap drag previews ONE
|
|
128
|
+
* container-wide gap, so every tag shows the live step's token + its own
|
|
129
|
+
* re-measured px); no label follows the pointer. The gap handles remain the
|
|
130
|
+
* only interactive element.
|
|
128
131
|
*/
|
|
129
132
|
export declare function SpacingHandlesView(props: SpacingHandlesViewProps): import("react").JSX.Element;
|
|
130
133
|
export declare function SpacingHandles({ doc, node }: {
|
|
@@ -14,8 +14,9 @@ import { createRectTracker, measureElementRect, scheduleFrame, subscribeCanvasLa
|
|
|
14
14
|
* container is selected on the canvas, draggable gap handles appear between
|
|
15
15
|
* its children, axis-aware via the shared `containerFlowAxis`. Dragging snaps
|
|
16
16
|
* through the EC-161 spacing-token scale (`lib/dnd/gap-drag` — pure math, no
|
|
17
|
-
* pixel value can escape it)
|
|
18
|
-
*
|
|
17
|
+
* pixel value can escape it); the static gap tags (EC-435) stay put and read
|
|
18
|
+
* the LIVE token + re-measured px during the drag (EC-438 — no
|
|
19
|
+
* pointer-following label). The drag previews imperatively on the rendered
|
|
19
20
|
* element; releasing commits exactly ONE builder dispatch — `setProp` with the
|
|
20
21
|
* token, or `removeProp` for "no gap", the identical values the inspector's
|
|
21
22
|
* gap select writes — so one undo restores, and the graph stays the single
|
|
@@ -168,9 +169,10 @@ const HANDLE_MAX_LENGTH = 48;
|
|
|
168
169
|
* EC-435 AD-2: every open gap region renders a pointer-transparent hatched
|
|
169
170
|
* rectangle (diagonal accent stripes, Figma-grid style) under the handles,
|
|
170
171
|
* each with a small static token-label size tag beside its handle. During a
|
|
171
|
-
* gap drag the
|
|
172
|
-
*
|
|
173
|
-
* the gap handles remain the
|
|
172
|
+
* gap drag the tags STAY PUT and read live (EC-438 — a gap drag previews ONE
|
|
173
|
+
* container-wide gap, so every tag shows the live step's token + its own
|
|
174
|
+
* re-measured px); no label follows the pointer. The gap handles remain the
|
|
175
|
+
* only interactive element.
|
|
174
176
|
*/
|
|
175
177
|
export function SpacingHandlesView(props) {
|
|
176
178
|
const { spots, horizontal, gapLabel, drag } = props;
|
|
@@ -190,29 +192,27 @@ export function SpacingHandlesView(props) {
|
|
|
190
192
|
background: 'repeating-linear-gradient(45deg, rgba(91,140,255,0.28) 0px, rgba(91,140,255,0.28) 3px, rgba(91,140,255,0.06) 3px, rgba(91,140,255,0.06) 9px)',
|
|
191
193
|
boxShadow: 'inset 0 0 0 1px rgba(91,140,255,0.3)',
|
|
192
194
|
} }, spot.index));
|
|
193
|
-
}),
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
})
|
|
215
|
-
: null, spots.map((spot) => {
|
|
195
|
+
}), spots.map((spot) => {
|
|
196
|
+
if (spot.gapLength < 1)
|
|
197
|
+
return null;
|
|
198
|
+
const length = Math.min(spot.crossLength, HANDLE_MAX_LENGTH);
|
|
199
|
+
return (_jsxs("div", { "data-ec-gap-hatch-label": spot.index, style: {
|
|
200
|
+
position: 'absolute',
|
|
201
|
+
left: horizontal ? spot.x : spot.x + length / 2 + 6,
|
|
202
|
+
top: horizontal ? spot.y + length / 2 + 6 : spot.y,
|
|
203
|
+
transform: horizontal ? 'translateX(-50%)' : 'translateY(-50%)',
|
|
204
|
+
zIndex: 45,
|
|
205
|
+
pointerEvents: 'none',
|
|
206
|
+
padding: '1px 6px',
|
|
207
|
+
borderRadius: 5,
|
|
208
|
+
fontSize: 10,
|
|
209
|
+
fontWeight: 600,
|
|
210
|
+
whiteSpace: 'nowrap',
|
|
211
|
+
color: '#fff',
|
|
212
|
+
backgroundColor: 'rgba(91,140,255,0.92)',
|
|
213
|
+
boxShadow: '0 1px 4px rgba(8,12,24,0.3)',
|
|
214
|
+
}, children: [drag ? drag.label : gapLabel, " \u00B7 ", Math.round(spot.gapLength), "px"] }, spot.index));
|
|
215
|
+
}), spots.map((spot) => {
|
|
216
216
|
const length = Math.min(spot.crossLength, HANDLE_MAX_LENGTH);
|
|
217
217
|
const active = drag?.index === spot.index;
|
|
218
218
|
return (_jsx("div", { "data-ec-gap-handle": spot.index, role: "separator", "aria-label": translate('builder.spacing.gapAfterChild', { index: spot.index + 1 }), "aria-orientation": horizontal ? 'vertical' : 'horizontal', onPointerDown: props.onPointerDown(spot), onPointerMove: props.onPointerMove, onPointerUp: props.onPointerUp, onPointerCancel: props.onPointerCancel, style: {
|
|
@@ -234,21 +234,7 @@ export function SpacingHandlesView(props) {
|
|
|
234
234
|
backgroundColor: active ? '#5b8cff' : 'rgba(91, 140, 255, 0.65)',
|
|
235
235
|
boxShadow: '0 0 0 1px rgba(8, 12, 24, 0.55)',
|
|
236
236
|
} }) }, spot.index));
|
|
237
|
-
})
|
|
238
|
-
position: 'absolute',
|
|
239
|
-
left: drag.labelX + 12,
|
|
240
|
-
top: drag.labelY + 12,
|
|
241
|
-
zIndex: 50,
|
|
242
|
-
pointerEvents: 'none',
|
|
243
|
-
padding: '2px 8px',
|
|
244
|
-
borderRadius: 6,
|
|
245
|
-
fontSize: 11,
|
|
246
|
-
fontWeight: 600,
|
|
247
|
-
whiteSpace: 'nowrap',
|
|
248
|
-
color: '#fff',
|
|
249
|
-
backgroundColor: '#5b8cff',
|
|
250
|
-
boxShadow: '0 2px 8px rgba(8, 12, 24, 0.4)',
|
|
251
|
-
}, children: drag.label })) : null] }));
|
|
237
|
+
})] }));
|
|
252
238
|
}
|
|
253
239
|
export function SpacingHandles({ doc, node }) {
|
|
254
240
|
const builder = useBuilder();
|
|
@@ -370,8 +356,6 @@ export function SpacingHandles({ doc, node }) {
|
|
|
370
356
|
startStep,
|
|
371
357
|
step: startStep,
|
|
372
358
|
startCoord: axis === 'horizontal' ? e.clientX : e.clientY,
|
|
373
|
-
labelX: spot.x,
|
|
374
|
-
labelY: spot.y,
|
|
375
359
|
};
|
|
376
360
|
dragRef.current = next;
|
|
377
361
|
setDrag(next);
|
|
@@ -380,15 +364,9 @@ export function SpacingHandles({ doc, node }) {
|
|
|
380
364
|
const active = dragRef.current;
|
|
381
365
|
if (!active)
|
|
382
366
|
return;
|
|
383
|
-
const win = doc.defaultView;
|
|
384
367
|
const coord = axis === 'horizontal' ? e.clientX : e.clientY;
|
|
385
368
|
const step = gapStepFromDrag(active.startStep, coord - active.startCoord);
|
|
386
|
-
const next = {
|
|
387
|
-
...active,
|
|
388
|
-
step,
|
|
389
|
-
labelX: e.clientX + (win?.scrollX ?? 0),
|
|
390
|
-
labelY: e.clientY + (win?.scrollY ?? 0),
|
|
391
|
-
};
|
|
369
|
+
const next = { ...active, step };
|
|
392
370
|
dragRef.current = next;
|
|
393
371
|
setDrag(next);
|
|
394
372
|
applyPreview(step);
|
|
@@ -396,10 +374,8 @@ export function SpacingHandles({ doc, node }) {
|
|
|
396
374
|
const horizontal = axis === 'horizontal';
|
|
397
375
|
return createPortal(_jsx(SpacingHandlesView, { spots: spots, horizontal: horizontal,
|
|
398
376
|
// The static size tags share ONE vocabulary with the inspector's gap
|
|
399
|
-
// select
|
|
400
|
-
//
|
|
401
|
-
gapLabel: gapStepLabel(containerGapStep(node)), drag: drag
|
|
402
|
-
? { index: drag.index, labelX: drag.labelX, labelY: drag.labelY, label: gapStepLabel(drag.step) }
|
|
403
|
-
: null, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: () => finish('commit'), onPointerCancel: () => finish('cancel') }), doc.body);
|
|
377
|
+
// select: the token label of the node's committed gap step (EC-435
|
|
378
|
+
// AD-2); during a drag they read the live step instead (EC-438).
|
|
379
|
+
gapLabel: gapStepLabel(containerGapStep(node)), drag: drag ? { index: drag.index, label: gapStepLabel(drag.step) } : null, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: () => finish('commit'), onPointerCancel: () => finish('cancel') }), doc.body);
|
|
404
380
|
}
|
|
405
381
|
//# sourceMappingURL=spacing-handles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spacing-handles.js","sourceRoot":"","sources":["../../../src/components/builder/spacing-handles.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { DEFAULT_SLOT, isLayoutContainerNode, staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { containerFlowAxis } from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { FlowAxis } from '@elytracms/studio/lib/dnd/drop-rules'\nimport {\n containerGapStep,\n gapHandleSpots,\n gapStepCss,\n gapStepFromDrag,\n} from '@elytracms/studio/lib/dnd/gap-drag'\nimport type { GapRect, GapStep } from '@elytracms/studio/lib/dnd/gap-drag'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useBuilder } from './builder-store'\nimport { GAP_LABEL_KEYS, GAP_NONE_LABEL_KEY } from './layout-inspector'\nimport { activeDrag, onDragSessionEnd, onDragSessionStart } from './drag-session'\nimport {\n createRectTracker,\n measureElementRect,\n scheduleFrame,\n subscribeCanvasLayout,\n} from './canvas-rect-tracking'\nimport type { FrameScheduler, RectTracker } from './canvas-rect-tracking'\n\n/**\n * Tokenized spacing handles (EC-163, vision §3.1 / AD-10): when a Stack/Grid\n * container is selected on the canvas, draggable gap handles appear between\n * its children, axis-aware via the shared `containerFlowAxis`. Dragging snaps\n * through the EC-161 spacing-token scale (`lib/dnd/gap-drag` — pure math, no\n * pixel value can escape it) with a live plain-language label (the layout\n * inspector's vocabulary). The drag previews imperatively on the rendered\n * element; releasing commits exactly ONE builder dispatch — `setProp` with the\n * token, or `removeProp` for \"no gap\", the identical values the inspector's\n * gap select writes — so one undo restores, and the graph stays the single\n * source of truth. Esc mid-drag cancels: preview reverts, nothing dispatches.\n */\n\n/** Plain-language label for a gap step — the inspector's vocabulary. */\nexport function gapStepLabel(step: GapStep): string {\n return step === null ? translate(GAP_NONE_LABEL_KEY) : translate(GAP_LABEL_KEYS[step])\n}\n\nexport interface HandleSpotView {\n index: number\n /** Document-coordinate position of the handle's center. */\n x: number\n y: number\n crossStart: number\n crossLength: number\n /** Leading edge of the gap region along the flow axis (EC-435 hatch). */\n gapStart: number\n /** Rendered extent of the gap along the flow axis (0 for a closed gap). */\n gapLength: number\n}\n\n/**\n * The hatched gap REGION for a spot (EC-435 AD-2): the rectangle between child\n * N's trailing edge and child N+1's leading edge along the flow axis, spanning\n * the pair's shared cross extent — the same measured child-rect basis the\n * handle spots come from (`gapHandleSpots`), in the same document coordinates.\n * Pure over the spot; a closed gap yields a zero-extent rect (nothing to paint).\n */\nexport function gapRegionRect(spot: HandleSpotView, axis: FlowAxis): GapRect {\n return axis === 'horizontal'\n ? { left: spot.gapStart, top: spot.crossStart, width: spot.gapLength, height: spot.crossLength }\n : { left: spot.crossStart, top: spot.gapStart, width: spot.crossLength, height: spot.gapLength }\n}\n\ninterface ActiveDrag {\n index: number\n startStep: GapStep\n step: GapStep\n /** Pointer start along the flow axis (client coords). */\n startCoord: number\n /** Live pointer position (document coords) for the floating label. */\n labelX: number\n labelY: number\n}\n\n/**\n * The container's rendered element (the Stack/Grid div carrying the gap style).\n * Resilient to legitimate intermediate wrappers between the editable\n * `[data-ec-node-id]` wrapper and the primitive (an error-boundary/provider DOM\n * node, a future decorator layer): the FIRST `[data-ec-primitive]` inside the\n * wrapper whose NEAREST node wrapper is THIS node — so it tolerates a wrapper\n * above the primitive but never descends into a CHILD node's subtree and grabs\n * that nested container's own `[data-ec-primitive]`. Mirrors `measurePropRect`'s\n * nearest-node scoping (EC-408). Today no such wrapper exists — `decorate` wraps\n * `<RenderErrorBoundary>` which renders its children with no DOM element, so the\n * primitive is a direct child — but the scoping makes 2(b) unregressable.\n */\nexport function containerElementFor(doc: Document, nodeId: string): HTMLElement | null {\n const wrapper = doc.querySelector(`[data-ec-node-id=\"${nodeId}\"]`)\n if (!wrapper) return null\n for (const primitive of wrapper.querySelectorAll('[data-ec-primitive]')) {\n if (primitive.closest('[data-ec-node-id]') === wrapper) return primitive as HTMLElement\n }\n return null\n}\n\n/** The container's rendered child node-wrappers — the DOM child basis. */\nfunction childWrappers(container: Element): HTMLElement[] {\n return Array.from(container.querySelectorAll(':scope > [data-ec-node-id]')) as HTMLElement[]\n}\n\ninterface Measured {\n spots: HandleSpotView[]\n /** The axis the children are *rendered* along (drag direction follows it). */\n axis: FlowAxis\n}\n\nconst EMPTY_MEASURED = (axis: FlowAxis): Measured => ({ spots: [], axis })\n\n/**\n * Measure the handle spots between the container's rendered children — the\n * SINGLE source of truth for whether handles show (spots) and where. Eligibility\n * and measurement agree on the SAME (DOM) child basis by construction: a\n * null-rendering graph child (condition-gated → no `[data-ec-node-id]` wrapper)\n * simply isn't counted, so it can neither block handles when ≥2 children render\n * nor phantom them when <2 do; kept fresh by the live tracker (EC-404) so a child\n * add/remove or resize re-runs this. The graph-level flow axis\n * (`containerFlowAxis`) is tried first; when the rendered layout disagrees — a\n * stacking Grid below the mobile breakpoint lays its \"columns\" out vertically —\n * the cross axis is measured instead, so handles always sit in the gaps the\n * editor actually sees.\n */\nexport function measureSpots(doc: Document, nodeId: string, flowAxis: FlowAxis): Measured {\n const container = containerElementFor(doc, nodeId)\n const win = doc.defaultView\n if (!container || !win) return EMPTY_MEASURED(flowAxis)\n const children = childWrappers(container)\n if (children.length < 2) return EMPTY_MEASURED(flowAxis)\n const rects = children.map((el) => measureElementRect(el, win))\n const along = gapHandleSpots(rects, flowAxis)\n if (along.length > 0) return { spots: along, axis: flowAxis }\n const cross: FlowAxis = flowAxis === 'horizontal' ? 'vertical' : 'horizontal'\n const across = gapHandleSpots(rects, cross)\n return across.length > 0 ? { spots: across, axis: cross } : EMPTY_MEASURED(flowAxis)\n}\n\n/** Structural equality of two spot lists — suppresses redundant tracker emits. */\nfunction spotsEqual(a: readonly HandleSpotView[], b: readonly HandleSpotView[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n const x = a[i]!\n const y = b[i]!\n if (\n x.index !== y.index ||\n x.x !== y.x ||\n x.y !== y.y ||\n x.crossStart !== y.crossStart ||\n x.crossLength !== y.crossLength ||\n x.gapStart !== y.gapStart ||\n x.gapLength !== y.gapLength\n ) {\n return false\n }\n }\n return true\n}\n\n/** True when two measurements are equivalent (same axis, same spots). */\nexport function measuredEqual(a: Measured, b: Measured): boolean {\n return a.axis === b.axis && spotsEqual(a.spots, b.spots)\n}\n\n/** The elements the tracker's `ResizeObserver` watches: the container + its children. */\nfunction spacingTargets(doc: Document, nodeId: string): Element[] {\n const container = containerElementFor(doc, nodeId)\n if (!container) return []\n return [container, ...childWrappers(container)]\n}\n\n/**\n * Wire a live rect tracker for the spacing handles onto the shared EC-404 core:\n * `subscribeCanvasLayout` re-measures under child resize (image/font decode),\n * child add/remove (async population, conditional render — `MutationObserver`\n * `childList`), container resize, and scroll/viewport change; `scheduleFrame`\n * coalesces on the PARENT window's frame loop with the mandatory 100 ms timeout\n * backstop (the iframe's rAF is paused offscreen / never fires under automation).\n * `dispose` tears down every observer + listener — no leaks on unmount. The\n * once-only `useLayoutEffect` measure this replaces went stale the instant any of\n * those settled after first paint (EC-406 evidence).\n */\nexport function createSpacingTracker(\n doc: Document,\n nodeId: string,\n flowAxis: FlowAxis,\n host: FrameScheduler,\n onChange: (measured: Measured) => void,\n): RectTracker {\n return createRectTracker<Measured>({\n measure: () => measureSpots(doc, nodeId, flowAxis),\n equals: measuredEqual,\n onChange,\n schedule: (flush) => scheduleFrame(host, flush),\n subscribe: (invalidate) => subscribeCanvasLayout(doc, () => spacingTargets(doc, nodeId), invalidate),\n })\n}\n\n/**\n * True when the node is a layout container the handles can attach to — a cheap,\n * NECESSARY graph precondition for the mount (the authoritative \"are there ≥2\n * gaps to space\" answer is the live DOM {@link measureSpots}). Because each graph\n * child renders at most one `[data-ec-node-id]` wrapper, rendered DOM children ⊆\n * graph children, so requiring ≥2 graph children can never wrongly hide handles\n * that a fresh DOM measure would show.\n */\nexport function hasSpacingHandles(node: ComponentNode | null): node is ComponentNode {\n return (\n node !== null &&\n isLayoutContainerNode(node) &&\n (node.slots?.[DEFAULT_SLOT]?.length ?? 0) >= 2\n )\n}\n\nconst HANDLE_THICKNESS = 14\nconst HANDLE_BAR = 4\nconst HANDLE_MAX_LENGTH = 48\n\n/** Everything the portal-free handle markup needs (chrome contract surface). */\nexport interface SpacingHandlesViewProps {\n spots: HandleSpotView[]\n horizontal: boolean\n /**\n * Token label for the container's current gap (EC-435 AD-2) — the SAME\n * plain-language vocabulary the layout inspector and the drag label resolve\n * (`gapStepLabel`), stamped on every hatched gap region as a static size tag.\n */\n gapLabel: string\n /** The live drag, for the active-handle tint and the floating label. */\n drag: { index: number; labelX: number; labelY: number; label: string } | null\n onPointerDown: (spot: HandleSpotView) => (e: React.PointerEvent<HTMLDivElement>) => void\n onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => void\n onPointerUp: () => void\n onPointerCancel: () => void\n}\n\n/**\n * The spacing handles' rendered markup, portal-free so the AD-D chrome\n * contract test can walk it with `renderToStaticMarkup`. In-frame chrome\n * contract (EC-414 AD-B): `ec-*`/`data-ec-*` plus inline styles only; the root\n * carries the `.ec-chrome` containment recipe.\n *\n * EC-435 AD-2: every open gap region renders a pointer-transparent hatched\n * rectangle (diagonal accent stripes, Figma-grid style) under the handles,\n * each with a small static token-label size tag beside its handle. During a\n * gap drag the hatches keep tracking the previewed gap while the static tags\n * yield to the floating drag label (one live readout, no double vocabulary);\n * the gap handles remain the only interactive element.\n */\nexport function SpacingHandlesView(props: SpacingHandlesViewProps) {\n const { spots, horizontal, gapLabel, drag } = props\n const axis: FlowAxis = horizontal ? 'horizontal' : 'vertical'\n return (\n <div data-ec-spacing-handles=\"\" className=\"ec-chrome\">\n {spots.map((spot) => {\n const region = gapRegionRect(spot, axis)\n if (spot.gapLength < 1) return null\n return (\n <div\n key={spot.index}\n data-ec-gap-hatch={spot.index}\n style={{\n position: 'absolute',\n left: region.left,\n top: region.top,\n width: region.width,\n height: region.height,\n zIndex: 10,\n pointerEvents: 'none',\n background:\n 'repeating-linear-gradient(45deg, rgba(91,140,255,0.28) 0px, rgba(91,140,255,0.28) 3px, rgba(91,140,255,0.06) 3px, rgba(91,140,255,0.06) 9px)',\n boxShadow: 'inset 0 0 0 1px rgba(91,140,255,0.3)',\n }}\n />\n )\n })}\n {drag === null\n ? spots.map((spot) => {\n if (spot.gapLength < 1) return null\n const length = Math.min(spot.crossLength, HANDLE_MAX_LENGTH)\n return (\n <div\n key={spot.index}\n data-ec-gap-hatch-label={spot.index}\n style={{\n position: 'absolute',\n left: horizontal ? spot.x : spot.x + length / 2 + 6,\n top: horizontal ? spot.y + length / 2 + 6 : spot.y,\n transform: horizontal ? 'translateX(-50%)' : 'translateY(-50%)',\n zIndex: 45,\n pointerEvents: 'none',\n padding: '1px 6px',\n borderRadius: 5,\n fontSize: 10,\n fontWeight: 600,\n whiteSpace: 'nowrap',\n color: '#fff',\n backgroundColor: 'rgba(91,140,255,0.92)',\n boxShadow: '0 1px 4px rgba(8,12,24,0.3)',\n }}\n >\n {gapLabel} · {Math.round(spot.gapLength)}px\n </div>\n )\n })\n : null}\n {spots.map((spot) => {\n const length = Math.min(spot.crossLength, HANDLE_MAX_LENGTH)\n const active = drag?.index === spot.index\n return (\n <div\n key={spot.index}\n data-ec-gap-handle={spot.index}\n role=\"separator\"\n aria-label={translate('builder.spacing.gapAfterChild', { index: spot.index + 1 })}\n aria-orientation={horizontal ? 'vertical' : 'horizontal'}\n onPointerDown={props.onPointerDown(spot)}\n onPointerMove={props.onPointerMove}\n onPointerUp={props.onPointerUp}\n onPointerCancel={props.onPointerCancel}\n style={{\n position: 'absolute',\n left: spot.x - (horizontal ? HANDLE_THICKNESS / 2 : length / 2),\n top: spot.y - (horizontal ? length / 2 : HANDLE_THICKNESS / 2),\n width: horizontal ? HANDLE_THICKNESS : length,\n height: horizontal ? length : HANDLE_THICKNESS,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: horizontal ? 'col-resize' : 'row-resize',\n zIndex: 40,\n touchAction: 'none',\n }}\n >\n <div\n style={{\n width: horizontal ? HANDLE_BAR : '100%',\n height: horizontal ? '100%' : HANDLE_BAR,\n borderRadius: 999,\n backgroundColor: active ? '#5b8cff' : 'rgba(91, 140, 255, 0.65)',\n boxShadow: '0 0 0 1px rgba(8, 12, 24, 0.55)',\n }}\n />\n </div>\n )\n })}\n {drag ? (\n <div\n data-ec-gap-label=\"\"\n style={{\n position: 'absolute',\n left: drag.labelX + 12,\n top: drag.labelY + 12,\n zIndex: 50,\n pointerEvents: 'none',\n padding: '2px 8px',\n borderRadius: 6,\n fontSize: 11,\n fontWeight: 600,\n whiteSpace: 'nowrap',\n color: '#fff',\n backgroundColor: '#5b8cff',\n boxShadow: '0 2px 8px rgba(8, 12, 24, 0.4)',\n }}\n >\n {drag.label}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport function SpacingHandles({ doc, node }: { doc: Document; node: ComponentNode }) {\n const builder = useBuilder()\n const { dispatch } = builder\n const flowAxis = containerFlowAxis(node)\n const [measured, setMeasured] = useState<Measured>(() => ({ spots: [], axis: flowAxis }))\n const [drag, setDrag] = useState<ActiveDrag | null>(null)\n const dragRef = useRef<ActiveDrag | null>(null)\n const trackerRef = useRef<RectTracker | null>(null)\n // The container's pre-drag inline gap, restored on cancel/commit (the graph\n // re-render owns the committed value; the preview is imperative-only).\n const inlineGapRef = useRef<string>('')\n\n // Measurement lifetime (EC-406): ride the shared rect-tracking core instead of\n // the once-only useLayoutEffect + lone resize listener that went stale. The\n // tracker re-measures under child resize (image/font decode), child add/remove\n // (async population, conditional render), container resize, and scroll/viewport\n // change — every settle the old code missed. Re-subscribed on a graph/axis/\n // viewport change so the ResizeObserver re-points at the current children.\n useLayoutEffect(() => {\n const win = doc.defaultView\n if (!win) return\n const host = (win.parent ?? win) as unknown as FrameScheduler\n const tracker = createSpacingTracker(doc, node.id, flowAxis, host, setMeasured)\n trackerRef.current = tracker\n return () => {\n tracker.dispose()\n trackerRef.current = null\n }\n }, [doc, node.id, flowAxis, builder.present, builder.viewport])\n\n // The gap-drag previews imperatively on `container.style.gap` (the tracker's\n // MutationObserver is childList-only and ignores attribute writes, by design),\n // so nudge a coalesced re-measure per step to keep every handle glued to the\n // previewed gap as the children shift. Harmless off-drag (an unchanged measure\n // emits nothing).\n useEffect(() => {\n trackerRef.current?.invalidate()\n }, [drag?.step])\n\n // Scope 4: yield the handles during an active CONTENT drag (node move/insert) —\n // the DnD before/after/into indicators own the \"where it lands\" story then. The\n // handles' OWN gap drag uses pointer capture, never `beginDrag`, so it does not\n // trip this. Inline-editing suppression is handled at the canvas mount (the\n // handles unmount while a text/rich-text session owns the node).\n const [contentDragging, setContentDragging] = useState(() => activeDrag() !== null)\n useEffect(() => {\n const offStart = onDragSessionStart(() => setContentDragging(true))\n const offEnd = onDragSessionEnd(() => setContentDragging(false))\n return () => {\n offStart()\n offEnd()\n }\n }, [])\n\n const { spots: measuredSpots, axis } = measured\n const spots = contentDragging ? [] : measuredSpots\n\n const applyPreview = useCallback(\n (step: GapStep) => {\n const container = containerElementFor(doc, node.id)\n if (container) container.style.gap = gapStepCss(step)\n },\n [doc, node.id],\n )\n\n const finish = useCallback(\n (mode: 'commit' | 'cancel') => {\n const active = dragRef.current\n if (!active) return\n dragRef.current = null\n setDrag(null)\n // Drop the imperative preview; the rendered graph value takes over.\n const container = containerElementFor(doc, node.id)\n if (container) container.style.gap = inlineGapRef.current\n if (mode === 'commit' && active.step !== active.startStep) {\n // Exactly one dispatch — one operations command, one undo step — and\n // the identical prop value the inspector's gap select produces.\n if (active.step === null) {\n dispatch({ type: 'removeProp', id: node.id, name: 'gap' })\n } else {\n dispatch({ type: 'setProp', id: node.id, name: 'gap', value: staticProp(active.step) })\n }\n }\n },\n [doc, node.id, dispatch],\n )\n\n // Esc cancels mid-drag — listen on both documents (focus may sit either side\n // of the iframe boundary while the pointer is captured).\n useEffect(() => {\n if (!drag) return\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n finish('cancel')\n }\n }\n doc.addEventListener('keydown', onKeyDown, true)\n document.addEventListener('keydown', onKeyDown, true)\n return () => {\n doc.removeEventListener('keydown', onKeyDown, true)\n document.removeEventListener('keydown', onKeyDown, true)\n }\n }, [drag, doc, finish])\n\n // Cleanup on unmount/selection change mid-drag: revert, never dispatch.\n useEffect(() => () => finish('cancel'), [finish])\n\n const onPointerDown = (spot: HandleSpotView) => (e: React.PointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return\n e.preventDefault()\n e.stopPropagation()\n const startStep = containerGapStep(node)\n const container = containerElementFor(doc, node.id)\n inlineGapRef.current = container?.style.gap ?? ''\n try {\n e.currentTarget.setPointerCapture(e.pointerId)\n } catch {\n // Synthetic pointer events (tests, automation) have no active pointer\n // to capture; move/up still arrive via the handle's own listeners.\n }\n const next: ActiveDrag = {\n index: spot.index,\n startStep,\n step: startStep,\n startCoord: axis === 'horizontal' ? e.clientX : e.clientY,\n labelX: spot.x,\n labelY: spot.y,\n }\n dragRef.current = next\n setDrag(next)\n }\n\n const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n const active = dragRef.current\n if (!active) return\n const win = doc.defaultView\n const coord = axis === 'horizontal' ? e.clientX : e.clientY\n const step = gapStepFromDrag(active.startStep, coord - active.startCoord)\n const next: ActiveDrag = {\n ...active,\n step,\n labelX: e.clientX + (win?.scrollX ?? 0),\n labelY: e.clientY + (win?.scrollY ?? 0),\n }\n dragRef.current = next\n setDrag(next)\n applyPreview(step)\n }\n\n const horizontal = axis === 'horizontal'\n\n return createPortal(\n <SpacingHandlesView\n spots={spots}\n horizontal={horizontal}\n // The static size tags share ONE vocabulary with the inspector's gap\n // select and the floating drag label: the token label of the node's\n // current gap step (EC-435 AD-2).\n gapLabel={gapStepLabel(containerGapStep(node))}\n drag={\n drag\n ? { index: drag.index, labelX: drag.labelX, labelY: drag.labelY, label: gapStepLabel(drag.step) }\n : null\n }\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={() => finish('commit')}\n onPointerCancel={() => finish('cancel')}\n />,\n doc.body,\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"spacing-handles.js","sourceRoot":"","sources":["../../../src/components/builder/spacing-handles.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\nimport { DEFAULT_SLOT, isLayoutContainerNode, staticProp } from '@elytracms/core/project-graph'\nimport type { ComponentNode } from '@elytracms/core/project-graph'\nimport { containerFlowAxis } from '@elytracms/studio/lib/dnd/drop-rules'\nimport type { FlowAxis } from '@elytracms/studio/lib/dnd/drop-rules'\nimport {\n containerGapStep,\n gapHandleSpots,\n gapStepCss,\n gapStepFromDrag,\n} from '@elytracms/studio/lib/dnd/gap-drag'\nimport type { GapRect, GapStep } from '@elytracms/studio/lib/dnd/gap-drag'\nimport { translate } from '@elytracms/studio/lib/i18n'\nimport { useBuilder } from './builder-store'\nimport { GAP_LABEL_KEYS, GAP_NONE_LABEL_KEY } from './layout-inspector'\nimport { activeDrag, onDragSessionEnd, onDragSessionStart } from './drag-session'\nimport {\n createRectTracker,\n measureElementRect,\n scheduleFrame,\n subscribeCanvasLayout,\n} from './canvas-rect-tracking'\nimport type { FrameScheduler, RectTracker } from './canvas-rect-tracking'\n\n/**\n * Tokenized spacing handles (EC-163, vision §3.1 / AD-10): when a Stack/Grid\n * container is selected on the canvas, draggable gap handles appear between\n * its children, axis-aware via the shared `containerFlowAxis`. Dragging snaps\n * through the EC-161 spacing-token scale (`lib/dnd/gap-drag` — pure math, no\n * pixel value can escape it); the static gap tags (EC-435) stay put and read\n * the LIVE token + re-measured px during the drag (EC-438 — no\n * pointer-following label). The drag previews imperatively on the rendered\n * element; releasing commits exactly ONE builder dispatch — `setProp` with the\n * token, or `removeProp` for \"no gap\", the identical values the inspector's\n * gap select writes — so one undo restores, and the graph stays the single\n * source of truth. Esc mid-drag cancels: preview reverts, nothing dispatches.\n */\n\n/** Plain-language label for a gap step — the inspector's vocabulary. */\nexport function gapStepLabel(step: GapStep): string {\n return step === null ? translate(GAP_NONE_LABEL_KEY) : translate(GAP_LABEL_KEYS[step])\n}\n\nexport interface HandleSpotView {\n index: number\n /** Document-coordinate position of the handle's center. */\n x: number\n y: number\n crossStart: number\n crossLength: number\n /** Leading edge of the gap region along the flow axis (EC-435 hatch). */\n gapStart: number\n /** Rendered extent of the gap along the flow axis (0 for a closed gap). */\n gapLength: number\n}\n\n/**\n * The hatched gap REGION for a spot (EC-435 AD-2): the rectangle between child\n * N's trailing edge and child N+1's leading edge along the flow axis, spanning\n * the pair's shared cross extent — the same measured child-rect basis the\n * handle spots come from (`gapHandleSpots`), in the same document coordinates.\n * Pure over the spot; a closed gap yields a zero-extent rect (nothing to paint).\n */\nexport function gapRegionRect(spot: HandleSpotView, axis: FlowAxis): GapRect {\n return axis === 'horizontal'\n ? { left: spot.gapStart, top: spot.crossStart, width: spot.gapLength, height: spot.crossLength }\n : { left: spot.crossStart, top: spot.gapStart, width: spot.crossLength, height: spot.gapLength }\n}\n\ninterface ActiveDrag {\n index: number\n startStep: GapStep\n step: GapStep\n /** Pointer start along the flow axis (client coords). */\n startCoord: number\n}\n\n/**\n * The container's rendered element (the Stack/Grid div carrying the gap style).\n * Resilient to legitimate intermediate wrappers between the editable\n * `[data-ec-node-id]` wrapper and the primitive (an error-boundary/provider DOM\n * node, a future decorator layer): the FIRST `[data-ec-primitive]` inside the\n * wrapper whose NEAREST node wrapper is THIS node — so it tolerates a wrapper\n * above the primitive but never descends into a CHILD node's subtree and grabs\n * that nested container's own `[data-ec-primitive]`. Mirrors `measurePropRect`'s\n * nearest-node scoping (EC-408). Today no such wrapper exists — `decorate` wraps\n * `<RenderErrorBoundary>` which renders its children with no DOM element, so the\n * primitive is a direct child — but the scoping makes 2(b) unregressable.\n */\nexport function containerElementFor(doc: Document, nodeId: string): HTMLElement | null {\n const wrapper = doc.querySelector(`[data-ec-node-id=\"${nodeId}\"]`)\n if (!wrapper) return null\n for (const primitive of wrapper.querySelectorAll('[data-ec-primitive]')) {\n if (primitive.closest('[data-ec-node-id]') === wrapper) return primitive as HTMLElement\n }\n return null\n}\n\n/** The container's rendered child node-wrappers — the DOM child basis. */\nfunction childWrappers(container: Element): HTMLElement[] {\n return Array.from(container.querySelectorAll(':scope > [data-ec-node-id]')) as HTMLElement[]\n}\n\ninterface Measured {\n spots: HandleSpotView[]\n /** The axis the children are *rendered* along (drag direction follows it). */\n axis: FlowAxis\n}\n\nconst EMPTY_MEASURED = (axis: FlowAxis): Measured => ({ spots: [], axis })\n\n/**\n * Measure the handle spots between the container's rendered children — the\n * SINGLE source of truth for whether handles show (spots) and where. Eligibility\n * and measurement agree on the SAME (DOM) child basis by construction: a\n * null-rendering graph child (condition-gated → no `[data-ec-node-id]` wrapper)\n * simply isn't counted, so it can neither block handles when ≥2 children render\n * nor phantom them when <2 do; kept fresh by the live tracker (EC-404) so a child\n * add/remove or resize re-runs this. The graph-level flow axis\n * (`containerFlowAxis`) is tried first; when the rendered layout disagrees — a\n * stacking Grid below the mobile breakpoint lays its \"columns\" out vertically —\n * the cross axis is measured instead, so handles always sit in the gaps the\n * editor actually sees.\n */\nexport function measureSpots(doc: Document, nodeId: string, flowAxis: FlowAxis): Measured {\n const container = containerElementFor(doc, nodeId)\n const win = doc.defaultView\n if (!container || !win) return EMPTY_MEASURED(flowAxis)\n const children = childWrappers(container)\n if (children.length < 2) return EMPTY_MEASURED(flowAxis)\n const rects = children.map((el) => measureElementRect(el, win))\n const along = gapHandleSpots(rects, flowAxis)\n if (along.length > 0) return { spots: along, axis: flowAxis }\n const cross: FlowAxis = flowAxis === 'horizontal' ? 'vertical' : 'horizontal'\n const across = gapHandleSpots(rects, cross)\n return across.length > 0 ? { spots: across, axis: cross } : EMPTY_MEASURED(flowAxis)\n}\n\n/** Structural equality of two spot lists — suppresses redundant tracker emits. */\nfunction spotsEqual(a: readonly HandleSpotView[], b: readonly HandleSpotView[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i += 1) {\n const x = a[i]!\n const y = b[i]!\n if (\n x.index !== y.index ||\n x.x !== y.x ||\n x.y !== y.y ||\n x.crossStart !== y.crossStart ||\n x.crossLength !== y.crossLength ||\n x.gapStart !== y.gapStart ||\n x.gapLength !== y.gapLength\n ) {\n return false\n }\n }\n return true\n}\n\n/** True when two measurements are equivalent (same axis, same spots). */\nexport function measuredEqual(a: Measured, b: Measured): boolean {\n return a.axis === b.axis && spotsEqual(a.spots, b.spots)\n}\n\n/** The elements the tracker's `ResizeObserver` watches: the container + its children. */\nfunction spacingTargets(doc: Document, nodeId: string): Element[] {\n const container = containerElementFor(doc, nodeId)\n if (!container) return []\n return [container, ...childWrappers(container)]\n}\n\n/**\n * Wire a live rect tracker for the spacing handles onto the shared EC-404 core:\n * `subscribeCanvasLayout` re-measures under child resize (image/font decode),\n * child add/remove (async population, conditional render — `MutationObserver`\n * `childList`), container resize, and scroll/viewport change; `scheduleFrame`\n * coalesces on the PARENT window's frame loop with the mandatory 100 ms timeout\n * backstop (the iframe's rAF is paused offscreen / never fires under automation).\n * `dispose` tears down every observer + listener — no leaks on unmount. The\n * once-only `useLayoutEffect` measure this replaces went stale the instant any of\n * those settled after first paint (EC-406 evidence).\n */\nexport function createSpacingTracker(\n doc: Document,\n nodeId: string,\n flowAxis: FlowAxis,\n host: FrameScheduler,\n onChange: (measured: Measured) => void,\n): RectTracker {\n return createRectTracker<Measured>({\n measure: () => measureSpots(doc, nodeId, flowAxis),\n equals: measuredEqual,\n onChange,\n schedule: (flush) => scheduleFrame(host, flush),\n subscribe: (invalidate) => subscribeCanvasLayout(doc, () => spacingTargets(doc, nodeId), invalidate),\n })\n}\n\n/**\n * True when the node is a layout container the handles can attach to — a cheap,\n * NECESSARY graph precondition for the mount (the authoritative \"are there ≥2\n * gaps to space\" answer is the live DOM {@link measureSpots}). Because each graph\n * child renders at most one `[data-ec-node-id]` wrapper, rendered DOM children ⊆\n * graph children, so requiring ≥2 graph children can never wrongly hide handles\n * that a fresh DOM measure would show.\n */\nexport function hasSpacingHandles(node: ComponentNode | null): node is ComponentNode {\n return (\n node !== null &&\n isLayoutContainerNode(node) &&\n (node.slots?.[DEFAULT_SLOT]?.length ?? 0) >= 2\n )\n}\n\nconst HANDLE_THICKNESS = 14\nconst HANDLE_BAR = 4\nconst HANDLE_MAX_LENGTH = 48\n\n/** Everything the portal-free handle markup needs (chrome contract surface). */\nexport interface SpacingHandlesViewProps {\n spots: HandleSpotView[]\n horizontal: boolean\n /**\n * Token label for the container's COMMITTED gap (EC-435 AD-2) — the SAME\n * plain-language vocabulary the layout inspector resolves (`gapStepLabel`),\n * stamped on every hatched gap region as a static size tag while idle.\n */\n gapLabel: string\n /**\n * The live drag: the active handle index (tint) and the live step's token\n * label — the static tags read it in place while dragging (EC-438).\n */\n drag: { index: number; label: string } | null\n onPointerDown: (spot: HandleSpotView) => (e: React.PointerEvent<HTMLDivElement>) => void\n onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => void\n onPointerUp: () => void\n onPointerCancel: () => void\n}\n\n/**\n * The spacing handles' rendered markup, portal-free so the AD-D chrome\n * contract test can walk it with `renderToStaticMarkup`. In-frame chrome\n * contract (EC-414 AD-B): `ec-*`/`data-ec-*` plus inline styles only; the root\n * carries the `.ec-chrome` containment recipe.\n *\n * EC-435 AD-2: every open gap region renders a pointer-transparent hatched\n * rectangle (diagonal accent stripes, Figma-grid style) under the handles,\n * each with a small static token-label size tag beside its handle. During a\n * gap drag the tags STAY PUT and read live (EC-438 — a gap drag previews ONE\n * container-wide gap, so every tag shows the live step's token + its own\n * re-measured px); no label follows the pointer. The gap handles remain the\n * only interactive element.\n */\nexport function SpacingHandlesView(props: SpacingHandlesViewProps) {\n const { spots, horizontal, gapLabel, drag } = props\n const axis: FlowAxis = horizontal ? 'horizontal' : 'vertical'\n return (\n <div data-ec-spacing-handles=\"\" className=\"ec-chrome\">\n {spots.map((spot) => {\n const region = gapRegionRect(spot, axis)\n if (spot.gapLength < 1) return null\n return (\n <div\n key={spot.index}\n data-ec-gap-hatch={spot.index}\n style={{\n position: 'absolute',\n left: region.left,\n top: region.top,\n width: region.width,\n height: region.height,\n zIndex: 10,\n pointerEvents: 'none',\n background:\n 'repeating-linear-gradient(45deg, rgba(91,140,255,0.28) 0px, rgba(91,140,255,0.28) 3px, rgba(91,140,255,0.06) 3px, rgba(91,140,255,0.06) 9px)',\n boxShadow: 'inset 0 0 0 1px rgba(91,140,255,0.3)',\n }}\n />\n )\n })}\n {spots.map((spot) => {\n if (spot.gapLength < 1) return null\n const length = Math.min(spot.crossLength, HANDLE_MAX_LENGTH)\n return (\n <div\n key={spot.index}\n data-ec-gap-hatch-label={spot.index}\n style={{\n position: 'absolute',\n left: horizontal ? spot.x : spot.x + length / 2 + 6,\n top: horizontal ? spot.y + length / 2 + 6 : spot.y,\n transform: horizontal ? 'translateX(-50%)' : 'translateY(-50%)',\n zIndex: 45,\n pointerEvents: 'none',\n padding: '1px 6px',\n borderRadius: 5,\n fontSize: 10,\n fontWeight: 600,\n whiteSpace: 'nowrap',\n color: '#fff',\n backgroundColor: 'rgba(91,140,255,0.92)',\n boxShadow: '0 1px 4px rgba(8,12,24,0.3)',\n }}\n >\n {drag ? drag.label : gapLabel} · {Math.round(spot.gapLength)}px\n </div>\n )\n })}\n {spots.map((spot) => {\n const length = Math.min(spot.crossLength, HANDLE_MAX_LENGTH)\n const active = drag?.index === spot.index\n return (\n <div\n key={spot.index}\n data-ec-gap-handle={spot.index}\n role=\"separator\"\n aria-label={translate('builder.spacing.gapAfterChild', { index: spot.index + 1 })}\n aria-orientation={horizontal ? 'vertical' : 'horizontal'}\n onPointerDown={props.onPointerDown(spot)}\n onPointerMove={props.onPointerMove}\n onPointerUp={props.onPointerUp}\n onPointerCancel={props.onPointerCancel}\n style={{\n position: 'absolute',\n left: spot.x - (horizontal ? HANDLE_THICKNESS / 2 : length / 2),\n top: spot.y - (horizontal ? length / 2 : HANDLE_THICKNESS / 2),\n width: horizontal ? HANDLE_THICKNESS : length,\n height: horizontal ? length : HANDLE_THICKNESS,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: horizontal ? 'col-resize' : 'row-resize',\n zIndex: 40,\n touchAction: 'none',\n }}\n >\n <div\n style={{\n width: horizontal ? HANDLE_BAR : '100%',\n height: horizontal ? '100%' : HANDLE_BAR,\n borderRadius: 999,\n backgroundColor: active ? '#5b8cff' : 'rgba(91, 140, 255, 0.65)',\n boxShadow: '0 0 0 1px rgba(8, 12, 24, 0.55)',\n }}\n />\n </div>\n )\n })}\n </div>\n )\n}\n\nexport function SpacingHandles({ doc, node }: { doc: Document; node: ComponentNode }) {\n const builder = useBuilder()\n const { dispatch } = builder\n const flowAxis = containerFlowAxis(node)\n const [measured, setMeasured] = useState<Measured>(() => ({ spots: [], axis: flowAxis }))\n const [drag, setDrag] = useState<ActiveDrag | null>(null)\n const dragRef = useRef<ActiveDrag | null>(null)\n const trackerRef = useRef<RectTracker | null>(null)\n // The container's pre-drag inline gap, restored on cancel/commit (the graph\n // re-render owns the committed value; the preview is imperative-only).\n const inlineGapRef = useRef<string>('')\n\n // Measurement lifetime (EC-406): ride the shared rect-tracking core instead of\n // the once-only useLayoutEffect + lone resize listener that went stale. The\n // tracker re-measures under child resize (image/font decode), child add/remove\n // (async population, conditional render), container resize, and scroll/viewport\n // change — every settle the old code missed. Re-subscribed on a graph/axis/\n // viewport change so the ResizeObserver re-points at the current children.\n useLayoutEffect(() => {\n const win = doc.defaultView\n if (!win) return\n const host = (win.parent ?? win) as unknown as FrameScheduler\n const tracker = createSpacingTracker(doc, node.id, flowAxis, host, setMeasured)\n trackerRef.current = tracker\n return () => {\n tracker.dispose()\n trackerRef.current = null\n }\n }, [doc, node.id, flowAxis, builder.present, builder.viewport])\n\n // The gap-drag previews imperatively on `container.style.gap` (the tracker's\n // MutationObserver is childList-only and ignores attribute writes, by design),\n // so nudge a coalesced re-measure per step to keep every handle glued to the\n // previewed gap as the children shift. Harmless off-drag (an unchanged measure\n // emits nothing).\n useEffect(() => {\n trackerRef.current?.invalidate()\n }, [drag?.step])\n\n // Scope 4: yield the handles during an active CONTENT drag (node move/insert) —\n // the DnD before/after/into indicators own the \"where it lands\" story then. The\n // handles' OWN gap drag uses pointer capture, never `beginDrag`, so it does not\n // trip this. Inline-editing suppression is handled at the canvas mount (the\n // handles unmount while a text/rich-text session owns the node).\n const [contentDragging, setContentDragging] = useState(() => activeDrag() !== null)\n useEffect(() => {\n const offStart = onDragSessionStart(() => setContentDragging(true))\n const offEnd = onDragSessionEnd(() => setContentDragging(false))\n return () => {\n offStart()\n offEnd()\n }\n }, [])\n\n const { spots: measuredSpots, axis } = measured\n const spots = contentDragging ? [] : measuredSpots\n\n const applyPreview = useCallback(\n (step: GapStep) => {\n const container = containerElementFor(doc, node.id)\n if (container) container.style.gap = gapStepCss(step)\n },\n [doc, node.id],\n )\n\n const finish = useCallback(\n (mode: 'commit' | 'cancel') => {\n const active = dragRef.current\n if (!active) return\n dragRef.current = null\n setDrag(null)\n // Drop the imperative preview; the rendered graph value takes over.\n const container = containerElementFor(doc, node.id)\n if (container) container.style.gap = inlineGapRef.current\n if (mode === 'commit' && active.step !== active.startStep) {\n // Exactly one dispatch — one operations command, one undo step — and\n // the identical prop value the inspector's gap select produces.\n if (active.step === null) {\n dispatch({ type: 'removeProp', id: node.id, name: 'gap' })\n } else {\n dispatch({ type: 'setProp', id: node.id, name: 'gap', value: staticProp(active.step) })\n }\n }\n },\n [doc, node.id, dispatch],\n )\n\n // Esc cancels mid-drag — listen on both documents (focus may sit either side\n // of the iframe boundary while the pointer is captured).\n useEffect(() => {\n if (!drag) return\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n e.stopPropagation()\n finish('cancel')\n }\n }\n doc.addEventListener('keydown', onKeyDown, true)\n document.addEventListener('keydown', onKeyDown, true)\n return () => {\n doc.removeEventListener('keydown', onKeyDown, true)\n document.removeEventListener('keydown', onKeyDown, true)\n }\n }, [drag, doc, finish])\n\n // Cleanup on unmount/selection change mid-drag: revert, never dispatch.\n useEffect(() => () => finish('cancel'), [finish])\n\n const onPointerDown = (spot: HandleSpotView) => (e: React.PointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return\n e.preventDefault()\n e.stopPropagation()\n const startStep = containerGapStep(node)\n const container = containerElementFor(doc, node.id)\n inlineGapRef.current = container?.style.gap ?? ''\n try {\n e.currentTarget.setPointerCapture(e.pointerId)\n } catch {\n // Synthetic pointer events (tests, automation) have no active pointer\n // to capture; move/up still arrive via the handle's own listeners.\n }\n const next: ActiveDrag = {\n index: spot.index,\n startStep,\n step: startStep,\n startCoord: axis === 'horizontal' ? e.clientX : e.clientY,\n }\n dragRef.current = next\n setDrag(next)\n }\n\n const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {\n const active = dragRef.current\n if (!active) return\n const coord = axis === 'horizontal' ? e.clientX : e.clientY\n const step = gapStepFromDrag(active.startStep, coord - active.startCoord)\n const next: ActiveDrag = { ...active, step }\n dragRef.current = next\n setDrag(next)\n applyPreview(step)\n }\n\n const horizontal = axis === 'horizontal'\n\n return createPortal(\n <SpacingHandlesView\n spots={spots}\n horizontal={horizontal}\n // The static size tags share ONE vocabulary with the inspector's gap\n // select: the token label of the node's committed gap step (EC-435\n // AD-2); during a drag they read the live step instead (EC-438).\n gapLabel={gapStepLabel(containerGapStep(node))}\n drag={drag ? { index: drag.index, label: gapStepLabel(drag.step) } : null}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={() => finish('commit')}\n onPointerCancel={() => finish('cancel')}\n />,\n doc.body,\n )\n}\n"]}
|
|
@@ -17,7 +17,23 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Stacking: `DialogBlockingContent` (EC-392 discipline) — topmost modal layer,
|
|
19
19
|
* focus-trapped, Escape/outside interaction consumed, studio beneath inert.
|
|
20
|
+
*
|
|
21
|
+
* Presentation (EC-437): the sign-in card mounts BARE (no full-page opaque
|
|
22
|
+
* panel) over the stage family's translucent blurred backdrop, so the editor
|
|
23
|
+
* SEES their intact document sitting behind the prompt — showing the "your
|
|
24
|
+
* changes stay right here" promise instead of only telling it.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* The re-auth backdrop (EC-437) — the stage family's visual language
|
|
28
|
+
* (EC-431 AD-A, `surfaceGeometry`), borrowed as CLASSES only, never the
|
|
29
|
+
* `ExpandableSurface` component: the stage is lightly dismissible, re-auth is
|
|
30
|
+
* contractually non-dismissible (`DialogBlockingContent` stays the shell).
|
|
31
|
+
* One blur step above the stage's `backdrop-blur-sm`: the whole dense studio
|
|
32
|
+
* sits behind a small centered card here (not behind a near-full panel), and
|
|
33
|
+
* the stronger step keeps the card unmistakably focal while the studio stays
|
|
34
|
+
* clearly recognizable — judged live against `-sm` with screenshots.
|
|
20
35
|
*/
|
|
36
|
+
export declare const SESSION_EXPIRED_BACKDROP_CLASS = "bg-background/70 backdrop-blur-md";
|
|
21
37
|
export declare function SessionExpiredOverlay(): import("react").JSX.Element | null;
|
|
22
38
|
/**
|
|
23
39
|
* Why the sign-in screen appeared in place (EC-403): the session-expired copy
|
|
@@ -25,19 +25,35 @@ import { SignInScreen, activeProjectName } from './sign-in-view.js';
|
|
|
25
25
|
*
|
|
26
26
|
* Stacking: `DialogBlockingContent` (EC-392 discipline) — topmost modal layer,
|
|
27
27
|
* focus-trapped, Escape/outside interaction consumed, studio beneath inert.
|
|
28
|
+
*
|
|
29
|
+
* Presentation (EC-437): the sign-in card mounts BARE (no full-page opaque
|
|
30
|
+
* panel) over the stage family's translucent blurred backdrop, so the editor
|
|
31
|
+
* SEES their intact document sitting behind the prompt — showing the "your
|
|
32
|
+
* changes stay right here" promise instead of only telling it.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* The re-auth backdrop (EC-437) — the stage family's visual language
|
|
36
|
+
* (EC-431 AD-A, `surfaceGeometry`), borrowed as CLASSES only, never the
|
|
37
|
+
* `ExpandableSurface` component: the stage is lightly dismissible, re-auth is
|
|
38
|
+
* contractually non-dismissible (`DialogBlockingContent` stays the shell).
|
|
39
|
+
* One blur step above the stage's `backdrop-blur-sm`: the whole dense studio
|
|
40
|
+
* sits behind a small centered card here (not behind a near-full panel), and
|
|
41
|
+
* the stronger step keeps the card unmistakably focal while the studio stays
|
|
42
|
+
* clearly recognizable — judged live against `-sm` with screenshots.
|
|
28
43
|
*/
|
|
44
|
+
export const SESSION_EXPIRED_BACKDROP_CLASS = 'bg-background/70 backdrop-blur-md';
|
|
29
45
|
export function SessionExpiredOverlay() {
|
|
30
46
|
const auth = useAuth();
|
|
31
47
|
const t = useT();
|
|
32
48
|
const cache = useOperationCache();
|
|
33
49
|
if (!auth.sessionExpired)
|
|
34
50
|
return null;
|
|
35
|
-
return (_jsx(Dialog, { open: true, children: _jsxs(DialogBlockingContent, { "aria-describedby": undefined, children: [_jsx(DialogTitle, { className: "sr-only", children: t('error.sessionExpiredTitle') }), _jsx(SignInScreen, { notice: _jsx(SessionExpiredNotice, {}), projectName: activeProjectName(), showModeToggle: false, devCredentials: authMode() === 'playground' ? LOCAL_DEV_CREDENTIALS : null, authenticate: (_mode, email, password) => auth.signIn(email, password), onAuthenticated: () =>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
return (_jsx(Dialog, { open: true, children: _jsxs(DialogBlockingContent, { "aria-describedby": undefined, overlayClassName: SESSION_EXPIRED_BACKDROP_CLASS, children: [_jsx(DialogTitle, { className: "sr-only", children: t('error.sessionExpiredTitle') }), _jsx("div", { className: "grid min-h-full place-items-center p-6", children: _jsx(SignInScreen, { layout: "bare", notice: _jsx(SessionExpiredNotice, {}), projectName: activeProjectName(), showModeToggle: false, devCredentials: authMode() === 'playground' ? LOCAL_DEV_CREDENTIALS : null, authenticate: (_mode, email, password) => auth.signIn(email, password), onAuthenticated: () =>
|
|
52
|
+
// The session was dead for an unknown span: reads may have failed
|
|
53
|
+
// (retry: false) or gone stale. One wholesale invalidation lets
|
|
54
|
+
// every active query refetch under the fresh session — resume,
|
|
55
|
+
// not reload. (The empty key prefix matches all queries.)
|
|
56
|
+
cache.invalidate([]) }) })] }) }));
|
|
41
57
|
}
|
|
42
58
|
/**
|
|
43
59
|
* Why the sign-in screen appeared in place (EC-403): the session-expired copy
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-expired-overlay.js","sourceRoot":"","sources":["../../src/components/session-expired-overlay.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEhE
|
|
1
|
+
{"version":3,"file":"session-expired-overlay.js","sourceRoot":"","sources":["../../src/components/session-expired-overlay.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAA;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,mCAAmC,CAAA;AAEjF,MAAM,UAAU,qBAAqB;IACnC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;IACtB,MAAM,CAAC,GAAG,IAAI,EAAE,CAAA;IAChB,MAAM,KAAK,GAAG,iBAAiB,EAAE,CAAA;IAEjC,IAAI,CAAC,IAAI,CAAC,cAAc;QAAE,OAAO,IAAI,CAAA;IAErC,OAAO,CACL,KAAC,MAAM,IAAC,IAAI,kBACV,MAAC,qBAAqB,wBACF,SAAS,EAC3B,gBAAgB,EAAE,8BAA8B,aAEhD,KAAC,WAAW,IAAC,SAAS,EAAC,SAAS,YAAE,CAAC,CAAC,2BAA2B,CAAC,GAAe,EAI/E,cAAK,SAAS,EAAC,wCAAwC,YACrD,KAAC,YAAY,IACX,MAAM,EAAC,MAAM,EACb,MAAM,EAAE,KAAC,oBAAoB,KAAG,EAChC,WAAW,EAAE,iBAAiB,EAAE,EAChC,cAAc,EAAE,KAAK,EACrB,cAAc,EAAE,QAAQ,EAAE,KAAK,YAAY,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,EAC1E,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,EACtE,eAAe,EAAE,GAAG,EAAE;wBACpB,kEAAkE;wBAClE,gEAAgE;wBAChE,+DAA+D;wBAC/D,0DAA0D;wBAC1D,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,GAEtB,GACE,IACgB,GACjB,CACV,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAA;IAChB,OAAO,CACL,cACE,IAAI,EAAC,OAAO,EACZ,SAAS,EAAC,qEAAqE,YAE/E,MAAC,GAAG,IAAC,GAAG,EAAC,GAAG,EAAC,KAAK,EAAC,OAAO,aACxB,KAAC,WAAW,IAAC,SAAS,EAAC,yCAAyC,GAAG,EACnE,MAAC,KAAK,IAAC,GAAG,EAAC,GAAG,EAAC,SAAS,EAAC,gBAAgB,aACvC,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,8BAA8B,YACrD,CAAC,CAAC,2BAA2B,CAAC,GAC1B,EACP,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,YACzB,CAAC,CAAC,sBAAsB,CAAC,GACrB,IACD,IACJ,GACF,CACP,CAAA;AACH,CAAC","sourcesContent":["import { CircleAlert } from 'lucide-react'\nimport { LOCAL_DEV_CREDENTIALS, authMode } from '@elytracms/studio/lib/auth'\nimport { useT } from '@elytracms/studio/lib/i18n'\nimport { useOperationCache } from '@elytracms/studio/lib/query'\nimport { Dialog, DialogBlockingContent, DialogTitle, Row, Stack, Text } from '@elytracms/studio/ui'\nimport { useAuth } from './auth-context'\nimport { SignInScreen, activeProjectName } from './sign-in-view'\n\n/**\n * The blocking in-place re-auth overlay (EC-403). Mounted once in the studio\n * root (inside `StudioProvider`, over every route) and shown while the auth\n * store reports `sessionExpired`: the backend session died mid-flight, so all\n * editing is blocked behind a non-dismissible modal that mounts the SAME\n * `SignInScreen` composition the `/sign-in` route uses — **without any\n * navigation**. The React tree beneath stays alive, so every unsaved editor\n * state survives; a successful sign-in applies the fresh session (clearing the\n * flag in the auth store) and editing resumes exactly where it stopped.\n * Pending failed writes keep their normal retry paths (a `persistError`\n * clears on the next successful save).\n *\n * Re-auth is strictly sign-in (no create-account toggle — a brand-new account\n * cannot resume an existing editing session). On success the studio query\n * cache is invalidated wholesale so reads that failed or staled while the\n * session was dead recover without a reload.\n *\n * Stacking: `DialogBlockingContent` (EC-392 discipline) — topmost modal layer,\n * focus-trapped, Escape/outside interaction consumed, studio beneath inert.\n *\n * Presentation (EC-437): the sign-in card mounts BARE (no full-page opaque\n * panel) over the stage family's translucent blurred backdrop, so the editor\n * SEES their intact document sitting behind the prompt — showing the \"your\n * changes stay right here\" promise instead of only telling it.\n */\n\n/**\n * The re-auth backdrop (EC-437) — the stage family's visual language\n * (EC-431 AD-A, `surfaceGeometry`), borrowed as CLASSES only, never the\n * `ExpandableSurface` component: the stage is lightly dismissible, re-auth is\n * contractually non-dismissible (`DialogBlockingContent` stays the shell).\n * One blur step above the stage's `backdrop-blur-sm`: the whole dense studio\n * sits behind a small centered card here (not behind a near-full panel), and\n * the stronger step keeps the card unmistakably focal while the studio stays\n * clearly recognizable — judged live against `-sm` with screenshots.\n */\nexport const SESSION_EXPIRED_BACKDROP_CLASS = 'bg-background/70 backdrop-blur-md'\n\nexport function SessionExpiredOverlay() {\n const auth = useAuth()\n const t = useT()\n const cache = useOperationCache()\n\n if (!auth.sessionExpired) return null\n\n return (\n <Dialog open>\n <DialogBlockingContent\n aria-describedby={undefined}\n overlayClassName={SESSION_EXPIRED_BACKDROP_CLASS}\n >\n <DialogTitle className=\"sr-only\">{t('error.sessionExpiredTitle')}</DialogTitle>\n {/* min-h-full (not h-full): when the card outgrows a short viewport,\n the wrapper grows with it and the blocking content scrolls —\n centered when it fits, never clipped when it doesn't. */}\n <div className=\"grid min-h-full place-items-center p-6\">\n <SignInScreen\n layout=\"bare\"\n notice={<SessionExpiredNotice />}\n projectName={activeProjectName()}\n showModeToggle={false}\n devCredentials={authMode() === 'playground' ? LOCAL_DEV_CREDENTIALS : null}\n authenticate={(_mode, email, password) => auth.signIn(email, password)}\n onAuthenticated={() =>\n // The session was dead for an unknown span: reads may have failed\n // (retry: false) or gone stale. One wholesale invalidation lets\n // every active query refetch under the fresh session — resume,\n // not reload. (The empty key prefix matches all queries.)\n cache.invalidate([])\n }\n />\n </div>\n </DialogBlockingContent>\n </Dialog>\n )\n}\n\n/**\n * Why the sign-in screen appeared in place (EC-403): the session-expired copy\n * rendered above the form via the `SignInScreen` notice slot. Pure — exported\n * for the provider-free static-render smoke test.\n */\nexport function SessionExpiredNotice() {\n const t = useT()\n return (\n <div\n role=\"alert\"\n className=\"rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2\"\n >\n <Row gap=\"2\" align=\"start\">\n <CircleAlert className=\"mt-0.5 size-4 shrink-0 text-destructive\" />\n <Stack gap=\"1\" className=\"min-w-0 flex-1\">\n <Text size=\"sm\" className=\"font-medium text-destructive\">\n {t('error.sessionExpiredTitle')}\n </Text>\n <Text size=\"sm\" tone=\"muted\">\n {t('error.sessionExpired')}\n </Text>\n </Stack>\n </Row>\n </div>\n )\n}\n"]}
|
|
@@ -38,6 +38,15 @@ interface SignInScreenProps {
|
|
|
38
38
|
* normal `/sign-in` route passes none.
|
|
39
39
|
*/
|
|
40
40
|
notice?: ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* Presentation wrapper (EC-437). `'page'` (default) renders the
|
|
43
|
+
* full-viewport opaque panel — the `/sign-in` route's shipped layout,
|
|
44
|
+
* byte-identical. `'bare'` renders only the centered card column so a host
|
|
45
|
+
* surface composes its own backdrop: the session-expired re-auth overlay
|
|
46
|
+
* mounts it over a translucent blurred backdrop, keeping the intact studio
|
|
47
|
+
* visible beneath the prompt.
|
|
48
|
+
*/
|
|
49
|
+
layout?: 'page' | 'bare';
|
|
41
50
|
brandMark?: ReactNode;
|
|
42
51
|
productName?: string;
|
|
43
52
|
/** Attempt auth for the chosen mode. The container maps this to signIn/signUp. */
|
|
@@ -49,5 +58,5 @@ interface SignInScreenProps {
|
|
|
49
58
|
* Provider-free presentational sign-in screen. Owns only local form state and
|
|
50
59
|
* calls injected callbacks — no context hooks — so it static-renders in tests.
|
|
51
60
|
*/
|
|
52
|
-
export declare function SignInScreen({ projectName, showModeToggle, devCredentials, notice, brandMark, productName, authenticate, onAuthenticated, }: SignInScreenProps): import("react").JSX.Element;
|
|
61
|
+
export declare function SignInScreen({ projectName, showModeToggle, devCredentials, notice, layout, brandMark, productName, authenticate, onAuthenticated, }: SignInScreenProps): import("react").JSX.Element;
|
|
53
62
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { useNavigate, useSearch } from '@tanstack/react-router';
|
|
4
4
|
import { Button, Card, CardContent, Field, Heading, Input, Stack, Text } from '@elytracms/studio/ui';
|
|
@@ -38,7 +38,7 @@ export function SignInView({ brandMark, productName }) {
|
|
|
38
38
|
* Provider-free presentational sign-in screen. Owns only local form state and
|
|
39
39
|
* calls injected callbacks — no context hooks — so it static-renders in tests.
|
|
40
40
|
*/
|
|
41
|
-
export function SignInScreen({ projectName, showModeToggle, devCredentials, notice, brandMark, productName = 'Elytra', authenticate, onAuthenticated, }) {
|
|
41
|
+
export function SignInScreen({ projectName, showModeToggle, devCredentials, notice, layout = 'page', brandMark, productName = 'Elytra', authenticate, onAuthenticated, }) {
|
|
42
42
|
const t = useT();
|
|
43
43
|
const [email, setEmail] = useState('');
|
|
44
44
|
const [password, setPassword] = useState('');
|
|
@@ -66,7 +66,7 @@ export function SignInScreen({ projectName, showModeToggle, devCredentials, noti
|
|
|
66
66
|
}
|
|
67
67
|
const submitting = phase === 'submitting';
|
|
68
68
|
const creating = mode === 'signup';
|
|
69
|
-
return (_jsx(
|
|
69
|
+
return (_jsx(ScreenShell, { layout: layout, children: _jsxs(Stack, { gap: "4", className: "w-full max-w-sm", children: [notice, _jsxs(Stack, { gap: "1", align: "center", children: [brandMark ?? _jsx("span", { className: "size-8 rounded-lg bg-primary shadow-sm", "aria-hidden": true }), _jsx(Heading, { size: "h2", children: creating ? t('auth.signIn.headingCreate') : t('auth.signIn.heading', { productName }) }), _jsx(Text, { size: "sm", tone: "muted", children: projectName
|
|
70
70
|
? t(creating ? 'auth.signIn.subtext.register' : 'auth.signIn.subtext.continue', {
|
|
71
71
|
project: projectName,
|
|
72
72
|
})
|
|
@@ -81,6 +81,17 @@ export function SignInScreen({ projectName, showModeToggle, devCredentials, noti
|
|
|
81
81
|
setError(null);
|
|
82
82
|
}, children: creating ? t('auth.signIn.toggle.signIn') : t('auth.signIn.toggle.createAccount') })] })) : null, devCredentials ? _jsx(DevCredentialsHint, { entries: devCredentials }) : null] }) }));
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* The screen's presentation shell (EC-437). `'page'` is the `/sign-in`
|
|
86
|
+
* route's shipped full-viewport opaque panel, byte-identical; `'bare'`
|
|
87
|
+
* renders the card column alone — the session-expired re-auth overlay owns
|
|
88
|
+
* backdrop + centering there, so the studio stays visible beneath the prompt.
|
|
89
|
+
*/
|
|
90
|
+
function ScreenShell({ layout, children }) {
|
|
91
|
+
if (layout === 'bare')
|
|
92
|
+
return _jsx(_Fragment, { children: children });
|
|
93
|
+
return _jsx("div", { className: "grid min-h-dvh place-items-center bg-background p-6", children: children });
|
|
94
|
+
}
|
|
84
95
|
/** Seeded dev accounts for the explicit playground mode — fixtures, not secrets. */
|
|
85
96
|
function DevCredentialsHint({ entries }) {
|
|
86
97
|
const t = useT();
|