@3driseai/3drise-viewer 0.1.41 → 0.1.42
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/ui/UiLayers.d.ts +17 -1
- package/dist/ui/UiLayers.d.ts.map +1 -1
- package/dist/ui/UiLayers.js +76 -3
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -1
- package/package.json +1 -1
package/dist/ui/UiLayers.d.ts
CHANGED
|
@@ -10,11 +10,27 @@ export interface UiLayerRuntime {
|
|
|
10
10
|
}
|
|
11
11
|
/** A rendered layer by key, id, or name-less id. Undefined while it is hidden or not mounted. */
|
|
12
12
|
export declare function getUiLayerRuntime(ref: string): UiLayerRuntime | undefined;
|
|
13
|
+
/** An element of a layer, by child-index path from the layer's root (the sanitized HTML's top level). */
|
|
14
|
+
export interface UiElementRef {
|
|
15
|
+
layerId: string;
|
|
16
|
+
path: number[];
|
|
17
|
+
}
|
|
18
|
+
export interface UiEditMode {
|
|
19
|
+
selected?: UiElementRef | null;
|
|
20
|
+
/** Called with the clicked element (null path: empty space in the layer). */
|
|
21
|
+
onSelect(ref: UiElementRef): void;
|
|
22
|
+
}
|
|
23
|
+
/** Child-index path of `el` under `body`. */
|
|
24
|
+
export declare function uiElementPath(body: Element, el: Element): number[];
|
|
25
|
+
/** The element at `path` under `body`, or null. */
|
|
26
|
+
export declare function uiElementAt(body: Element, path: number[]): Element | null;
|
|
13
27
|
export interface UiLayersProps {
|
|
14
28
|
layers: UiLayer[] | undefined;
|
|
15
29
|
scene: UiSceneApi;
|
|
16
30
|
/** Extra CSS appended to every layer after its own (ProjectLoader's `uiStyles`). */
|
|
17
31
|
extraCss?: string;
|
|
32
|
+
/** Studio only: clicks select elements instead of running them. */
|
|
33
|
+
edit?: UiEditMode;
|
|
18
34
|
}
|
|
19
|
-
export default function UiLayers({ layers, scene, extraCss }: UiLayersProps): import("react").JSX.Element;
|
|
35
|
+
export default function UiLayers({ layers, scene, extraCss, edit }: UiLayersProps): import("react").JSX.Element;
|
|
20
36
|
//# sourceMappingURL=UiLayers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UiLayers.d.ts","sourceRoot":"","sources":["../../src/ui/UiLayers.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAgD,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAOlG,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,IAAI,YAAY,EAAE,CAAC;IAC1B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,IAAI,IAAI,CAAC;CACnB;AAGD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAGzE;
|
|
1
|
+
{"version":3,"file":"UiLayers.d.ts","sourceRoot":"","sources":["../../src/ui/UiLayers.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAgD,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAOlG,MAAM,WAAW,cAAc;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,IAAI,YAAY,EAAE,CAAC;IAC1B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,IAAI,IAAI,CAAC;CACnB;AAGD,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAGzE;AAMD,yGAAyG;AACzG,MAAM,WAAW,YAAY;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE;AAEjE,MAAM,WAAW,UAAU;IACvB,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC;CACrC;AAUD,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,GAAG,MAAM,EAAE,CAUlE;AAED,mDAAmD;AACnD,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,GAAG,IAAI,CAIzE;AAqED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,IAAI,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,aAAa,+BAYhF"}
|
package/dist/ui/UiLayers.js
CHANGED
|
@@ -23,9 +23,42 @@ export function getUiLayerRuntime(ref) {
|
|
|
23
23
|
return r;
|
|
24
24
|
return undefined;
|
|
25
25
|
}
|
|
26
|
+
const EDIT_CSS = `
|
|
27
|
+
.rise-layer * { pointer-events: auto !important; cursor: default !important; }
|
|
28
|
+
.rise-layer *:hover { outline: 1px dashed rgba(255,255,255,.55); outline-offset: 1px; }
|
|
29
|
+
[data-rise-selected] { outline: 2px solid #ffffff !important; outline-offset: 2px; box-shadow: 0 0 0 4px rgba(0,0,0,.45) !important; }
|
|
30
|
+
`;
|
|
31
|
+
/** Events a layer must not see while editing — its binder would run commands or write values. */
|
|
32
|
+
const SWALLOW = ['click', 'dblclick', 'pointerdown', 'pointerup', 'mousedown', 'pointerenter', 'pointerleave', 'keydown', 'input', 'change'];
|
|
33
|
+
/** Child-index path of `el` under `body`. */
|
|
34
|
+
export function uiElementPath(body, el) {
|
|
35
|
+
const path = [];
|
|
36
|
+
let cur = el;
|
|
37
|
+
while (cur && cur !== body) {
|
|
38
|
+
const parent = cur.parentElement;
|
|
39
|
+
if (!parent)
|
|
40
|
+
return [];
|
|
41
|
+
path.unshift(Array.prototype.indexOf.call(parent.children, cur));
|
|
42
|
+
cur = parent;
|
|
43
|
+
}
|
|
44
|
+
return cur === body ? path : [];
|
|
45
|
+
}
|
|
46
|
+
/** The element at `path` under `body`, or null. */
|
|
47
|
+
export function uiElementAt(body, path) {
|
|
48
|
+
let cur = body;
|
|
49
|
+
for (const i of path) {
|
|
50
|
+
cur = cur?.children[i] ?? null;
|
|
51
|
+
if (!cur)
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return cur;
|
|
55
|
+
}
|
|
26
56
|
// ---------------------------------------------------------------------------
|
|
27
|
-
function UiLayerHost({ layer, scene, extraCss }) {
|
|
57
|
+
function UiLayerHost({ layer, scene, extraCss, edit }) {
|
|
28
58
|
const ref = useRef(null);
|
|
59
|
+
const onSelect = useRef(edit?.onSelect);
|
|
60
|
+
onSelect.current = edit?.onSelect;
|
|
61
|
+
const editing = !!edit;
|
|
29
62
|
useEffect(() => {
|
|
30
63
|
const host = ref.current;
|
|
31
64
|
if (!host)
|
|
@@ -45,13 +78,53 @@ function UiLayerHost({ layer, scene, extraCss }) {
|
|
|
45
78
|
root.replaceChildren();
|
|
46
79
|
};
|
|
47
80
|
}, [layer.id, layer.key, layer.html, layer.css, extraCss, scene]);
|
|
81
|
+
// Edit mode: swallow every event on the way down (capture on the shadow root runs
|
|
82
|
+
// before any element's listener) and report clicks as a selection.
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
const root = ref.current?.shadowRoot;
|
|
85
|
+
if (!root || !editing)
|
|
86
|
+
return;
|
|
87
|
+
const style = document.createElement('style');
|
|
88
|
+
style.textContent = EDIT_CSS;
|
|
89
|
+
root.append(style);
|
|
90
|
+
const body = root.querySelector('.rise-layer');
|
|
91
|
+
const swallow = (e) => {
|
|
92
|
+
// Script presses (scene.ui.press, the panel's Test) are untrusted: they run as normal.
|
|
93
|
+
if (!e.isTrusted)
|
|
94
|
+
return;
|
|
95
|
+
e.stopPropagation();
|
|
96
|
+
if (e.type !== 'keydown' && e.cancelable)
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
if (e.type !== 'click' || !body)
|
|
99
|
+
return;
|
|
100
|
+
const target = e.composedPath().find((n) => n instanceof Element && body.contains(n) && n !== body);
|
|
101
|
+
onSelect.current?.({ layerId: layer.id, path: target ? uiElementPath(body, target) : [] });
|
|
102
|
+
};
|
|
103
|
+
for (const t of SWALLOW)
|
|
104
|
+
root.addEventListener(t, swallow, true);
|
|
105
|
+
return () => {
|
|
106
|
+
for (const t of SWALLOW)
|
|
107
|
+
root.removeEventListener(t, swallow, true);
|
|
108
|
+
style.remove();
|
|
109
|
+
};
|
|
110
|
+
}, [editing, layer.id, layer.html, layer.css, extraCss, scene]);
|
|
111
|
+
// The selected element's outline lives on the live DOM only — never in the saved HTML.
|
|
112
|
+
const selPath = edit?.selected?.layerId === layer.id ? edit.selected.path.join('/') : null;
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
const body = ref.current?.shadowRoot?.querySelector('.rise-layer');
|
|
115
|
+
if (!body || selPath === null || !selPath)
|
|
116
|
+
return;
|
|
117
|
+
const el = uiElementAt(body, selPath.split('/').map(Number));
|
|
118
|
+
el?.setAttribute('data-rise-selected', '');
|
|
119
|
+
return () => el?.removeAttribute('data-rise-selected');
|
|
120
|
+
}, [selPath, layer.html, layer.css, extraCss, scene]);
|
|
48
121
|
return _jsx("div", { ref: ref, className: UI_HOST_CLASS, "data-ui-layer": layer.key, style: { position: 'absolute', inset: 0, pointerEvents: 'none' } });
|
|
49
122
|
}
|
|
50
|
-
export default function UiLayers({ layers, scene, extraCss }) {
|
|
123
|
+
export default function UiLayers({ layers, scene, extraCss, edit }) {
|
|
51
124
|
const visible = (layers ?? []).filter(l => l.visible !== false);
|
|
52
125
|
if (!visible.length)
|
|
53
126
|
return null;
|
|
54
127
|
return (_jsx("div", { className: "rise-ui-overlay",
|
|
55
128
|
// translateZ(0): a `position: fixed` inside a layer is then relative to this overlay, not the page.
|
|
56
|
-
style: { position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'hidden', transform: 'translateZ(0)', zIndex: 10 }, children: visible.map(l => _jsx(UiLayerHost, { layer: l, scene: scene, extraCss: extraCss }, l.id)) }));
|
|
129
|
+
style: { position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'hidden', transform: 'translateZ(0)', zIndex: 10 }, children: visible.map(l => _jsx(UiLayerHost, { layer: l, scene: scene, extraCss: extraCss, edit: edit }, l.id)) }));
|
|
57
130
|
}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { default as UiLayers, getUiLayerRuntime } from './UiLayers';
|
|
2
|
-
export type { UiLayersProps, UiLayerRuntime } from './UiLayers';
|
|
1
|
+
export { default as UiLayers, getUiLayerRuntime, uiElementPath, uiElementAt } from './UiLayers';
|
|
2
|
+
export type { UiLayersProps, UiLayerRuntime, UiEditMode, UiElementRef } from './UiLayers';
|
|
3
3
|
export { default as UiAnchorUpdater } from './UiAnchorUpdater';
|
|
4
4
|
export { bindUiLayer } from './uiBinder';
|
|
5
5
|
export type { BindOptions, BoundControl, UiBinderHandle, UiSceneApi } from './uiBinder';
|
package/dist/ui/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAChG,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/ui/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// UI layers: agent-written HTML/CSS that controls the scene (see claude/ui-module.md)
|
|
2
|
-
export { default as UiLayers, getUiLayerRuntime } from './UiLayers.js';
|
|
2
|
+
export { default as UiLayers, getUiLayerRuntime, uiElementPath, uiElementAt } from './UiLayers.js';
|
|
3
3
|
export { default as UiAnchorUpdater } from './UiAnchorUpdater.js';
|
|
4
4
|
export { bindUiLayer } from './uiBinder.js';
|
|
5
5
|
export { uiAnchors } from './uiAnchors.js';
|