@fps-games/editor 0.1.0 → 0.1.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/local-editor-harness.d.ts +70 -26
- package/dist/local-editor-harness.d.ts.map +1 -1
- package/dist/local-editor-harness.js +455 -21
- package/dist/local-editor-harness.js.map +1 -1
- package/node_modules/@fps-games/editor-babylon/package.json +4 -4
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.d.ts +15 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js +163 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-context-menu.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts +28 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js +164 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-actions.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts +20 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js +483 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-controller.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts +56 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js +216 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-tree.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts +13 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js +138 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-hierarchy-view.js.map +1 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.d.ts +3 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.js +29 -5
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-input-router.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts +10 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js +554 -93
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-panels.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts +2 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js +7 -3
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-primitives.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js +2 -0
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-shortcuts.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts +148 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui-types.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts +11 -2
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js +206 -173
- package/node_modules/@fps-games/editor-browser/dist/local-editor-ui.js.map +1 -1
- package/node_modules/@fps-games/editor-browser/package.json +1 -1
- package/node_modules/@fps-games/editor-core/dist/index.d.ts +2 -0
- package/node_modules/@fps-games/editor-core/dist/index.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/index.js +2 -0
- package/node_modules/@fps-games/editor-core/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/inspector.d.ts +138 -0
- package/node_modules/@fps-games/editor-core/dist/inspector.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/inspector.js +298 -0
- package/node_modules/@fps-games/editor-core/dist/inspector.js.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts +28 -0
- package/node_modules/@fps-games/editor-core/dist/scene-graph.d.ts.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/scene-graph.js +143 -2
- package/node_modules/@fps-games/editor-core/dist/scene-graph.js.map +1 -1
- package/node_modules/@fps-games/editor-core/dist/transform-math.d.ts +30 -0
- package/node_modules/@fps-games/editor-core/dist/transform-math.d.ts.map +1 -0
- package/node_modules/@fps-games/editor-core/dist/transform-math.js +339 -0
- package/node_modules/@fps-games/editor-core/dist/transform-math.js.map +1 -0
- package/node_modules/@fps-games/editor-core/package.json +2 -2
- package/node_modules/@fps-games/editor-forge-play/dist/index.js.map +1 -1
- package/node_modules/@fps-games/editor-forge-play/package.json +2 -2
- package/node_modules/@fps-games/editor-protocol/package.json +1 -1
- package/package.json +11 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createEditorSession, validateSceneGraphDelete, validateSceneGraphDrop, validateSceneGraphRename, } from '@fps-games/editor-core';
|
|
1
|
+
import { createEditorSession, createInspectorRegistry, createInspectorEditPayload, mergeInspectorSections, serializedMultiObjectToInspectorObject, serializedObjectToInspectorObject, validateSceneGraphDelete, validateSceneGraphDrop, validateSceneGraphGroupSelection, validateSceneGraphMove, validateSceneGraphRename, } from '@fps-games/editor-core';
|
|
2
2
|
import { createLocalEditorBrowserUi, } from '@fps-games/editor-browser';
|
|
3
3
|
import { createBabylonEditorProjection, createBabylonEditorWorld, createBabylonProjectionSelectionController, createBabylonSceneViewCameraController, createBabylonSceneViewInputController, createBabylonTransformGizmoController, focusEditorViewportSelection, } from '@fps-games/editor-babylon';
|
|
4
4
|
export function createLocalEditorHarness(options) {
|
|
@@ -24,11 +24,15 @@ export function createLocalEditorHarness(options) {
|
|
|
24
24
|
transformConstraint: 'axis',
|
|
25
25
|
resizeHandler: null,
|
|
26
26
|
status: 'Game running',
|
|
27
|
+
statusTone: 'default',
|
|
28
|
+
statusToneStatus: 'Game running',
|
|
29
|
+
statusDetails: '',
|
|
27
30
|
summary: '',
|
|
28
31
|
};
|
|
29
32
|
let harness;
|
|
30
33
|
const ui = createLocalEditorBrowserUi({
|
|
31
34
|
root,
|
|
35
|
+
inspector: options.inspector,
|
|
32
36
|
callbacks: {
|
|
33
37
|
onEnterEditor: () => {
|
|
34
38
|
void runExclusive(state, harness.render, () => harness.enterEditor());
|
|
@@ -74,12 +78,26 @@ export function createLocalEditorHarness(options) {
|
|
|
74
78
|
if (dropSceneGraphNode(state, options, intent))
|
|
75
79
|
harness.render();
|
|
76
80
|
},
|
|
81
|
+
onSceneGraphMove: (intent) => {
|
|
82
|
+
if (moveSceneGraphNodes(state, options, intent))
|
|
83
|
+
harness.render();
|
|
84
|
+
},
|
|
85
|
+
onSceneGraphGroupSelection: (intent) => {
|
|
86
|
+
if (groupSceneGraphSelection(state, options, intent))
|
|
87
|
+
harness.render();
|
|
88
|
+
},
|
|
89
|
+
onContextAction: (action) => {
|
|
90
|
+
if (handleContextAction(state, options, action))
|
|
91
|
+
harness.render();
|
|
92
|
+
},
|
|
77
93
|
onAssetFilterChange: (value) => {
|
|
78
94
|
state.assetFilter = value;
|
|
79
95
|
harness.render();
|
|
80
96
|
},
|
|
81
97
|
onPropertyInput: (input) => {
|
|
82
|
-
|
|
98
|
+
const previousStatus = state.status;
|
|
99
|
+
const patched = patchSerializedProperty(state, options, input);
|
|
100
|
+
if (patched || state.status !== previousStatus)
|
|
83
101
|
harness.render();
|
|
84
102
|
},
|
|
85
103
|
onTransformToolChange: (tool) => {
|
|
@@ -154,7 +172,11 @@ export function createLocalEditorHarness(options) {
|
|
|
154
172
|
expectedRevision: source.ref.revision,
|
|
155
173
|
});
|
|
156
174
|
if (!hostResult.ok || !hostResult.document) {
|
|
157
|
-
|
|
175
|
+
const failureStatus = summarizeLocalEditorAuthoringFailure(hostResult);
|
|
176
|
+
state.status = failureStatus.status;
|
|
177
|
+
state.statusTone = 'error';
|
|
178
|
+
state.statusToneStatus = state.status;
|
|
179
|
+
state.statusDetails = failureStatus.details;
|
|
158
180
|
state.summary = summarizeDocument(options, document, source);
|
|
159
181
|
return false;
|
|
160
182
|
}
|
|
@@ -188,6 +210,9 @@ export function createLocalEditorHarness(options) {
|
|
|
188
210
|
}
|
|
189
211
|
state.summary = result.summary ?? summarizeDocument(options, preparedDocument, savedSource ?? null);
|
|
190
212
|
state.status = 'Scene saved';
|
|
213
|
+
state.statusTone = 'success';
|
|
214
|
+
state.statusToneStatus = state.status;
|
|
215
|
+
state.statusDetails = '';
|
|
191
216
|
return true;
|
|
192
217
|
},
|
|
193
218
|
async saveAndRunGame() {
|
|
@@ -203,6 +228,9 @@ export function createLocalEditorHarness(options) {
|
|
|
203
228
|
state.session = null;
|
|
204
229
|
state.source = null;
|
|
205
230
|
state.status = 'Reloading game';
|
|
231
|
+
state.statusTone = 'default';
|
|
232
|
+
state.statusToneStatus = state.status;
|
|
233
|
+
state.statusDetails = '';
|
|
206
234
|
await options.persistenceAdapter.runGame();
|
|
207
235
|
},
|
|
208
236
|
dispose() {
|
|
@@ -214,6 +242,32 @@ export function createLocalEditorHarness(options) {
|
|
|
214
242
|
harness.render();
|
|
215
243
|
return harness;
|
|
216
244
|
}
|
|
245
|
+
export function mergeLocalEditorHarnessInspectorComponentSections(input) {
|
|
246
|
+
const components = input.components;
|
|
247
|
+
if (!components)
|
|
248
|
+
return input.inspectorObject;
|
|
249
|
+
const context = {
|
|
250
|
+
...input.inspectorObject.selection,
|
|
251
|
+
...input.context,
|
|
252
|
+
targetIds: input.context?.targetIds ?? input.inspectorObject.targetIds,
|
|
253
|
+
activeId: input.context?.activeId ?? input.inspectorObject.activeId,
|
|
254
|
+
document: input.context?.document ?? input.inspectorObject.document ?? input.inspectorObject.selection.document,
|
|
255
|
+
};
|
|
256
|
+
const componentSections = getInspectorComponentSections({
|
|
257
|
+
components,
|
|
258
|
+
context,
|
|
259
|
+
componentConflict: input.componentConflict,
|
|
260
|
+
propertyConflict: input.propertyConflict,
|
|
261
|
+
});
|
|
262
|
+
if (componentSections.length === 0)
|
|
263
|
+
return input.inspectorObject;
|
|
264
|
+
return {
|
|
265
|
+
...input.inspectorObject,
|
|
266
|
+
sections: mergeInspectorSections(input.inspectorObject.sections, componentSections, {
|
|
267
|
+
propertyConflict: input.propertyConflict,
|
|
268
|
+
}),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
217
271
|
async function createEditorWorld(state, options, render) {
|
|
218
272
|
disposeEditorWorld(state);
|
|
219
273
|
const canvas = options.worldAdapter.getCanvas();
|
|
@@ -406,6 +460,9 @@ async function runExclusive(state, render, action) {
|
|
|
406
460
|
}
|
|
407
461
|
catch (error) {
|
|
408
462
|
state.status = error instanceof Error ? error.message : String(error);
|
|
463
|
+
state.statusTone = 'error';
|
|
464
|
+
state.statusToneStatus = state.status;
|
|
465
|
+
state.statusDetails = state.status;
|
|
409
466
|
console.error('[LocalEditorHarness] action failed', error);
|
|
410
467
|
}
|
|
411
468
|
finally {
|
|
@@ -440,6 +497,38 @@ function selectItem(state, options, input) {
|
|
|
440
497
|
};
|
|
441
498
|
return dispatchSelectionCommand(state, options, command);
|
|
442
499
|
}
|
|
500
|
+
function handleContextAction(state, options, action) {
|
|
501
|
+
if (action.region !== 'hierarchy')
|
|
502
|
+
return false;
|
|
503
|
+
if (action.action === 'focus') {
|
|
504
|
+
const activeId = action.activeId ?? action.targetIds[action.targetIds.length - 1] ?? null;
|
|
505
|
+
const selectionChanged = activeId && !state.session?.getState().selection.selectedIds.includes(activeId)
|
|
506
|
+
? dispatchSelectionCommand(state, options, {
|
|
507
|
+
type: 'selection.replace',
|
|
508
|
+
selectedIds: [activeId],
|
|
509
|
+
activeId,
|
|
510
|
+
label: 'Select Context Target',
|
|
511
|
+
})
|
|
512
|
+
: false;
|
|
513
|
+
return focusSelectedProjection(state) || selectionChanged;
|
|
514
|
+
}
|
|
515
|
+
if (action.action === 'rename')
|
|
516
|
+
return false;
|
|
517
|
+
if (action.action === 'create-group') {
|
|
518
|
+
return createSceneGraphGroup(state, options, {
|
|
519
|
+
parentId: action.parentId ?? null,
|
|
520
|
+
activeId: action.activeId ?? null,
|
|
521
|
+
name: 'Group',
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
if (action.action === 'delete') {
|
|
525
|
+
return deleteSceneGraphNodes(state, options, {
|
|
526
|
+
ids: action.targetIds,
|
|
527
|
+
activeId: action.activeId ?? null,
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
443
532
|
function dispatchSelectionCommand(state, options, command) {
|
|
444
533
|
if (state.mode !== 'editor')
|
|
445
534
|
return false;
|
|
@@ -601,6 +690,99 @@ function dropSceneGraphNode(state, options, intent) {
|
|
|
601
690
|
state.status = patch.label ?? `Reparented ${intent.draggedId}`;
|
|
602
691
|
return true;
|
|
603
692
|
}
|
|
693
|
+
function moveSceneGraphNodes(state, options, intent) {
|
|
694
|
+
const document = state.session?.getState().workingDocument;
|
|
695
|
+
if (state.mode !== 'editor' || !state.session || !document)
|
|
696
|
+
return false;
|
|
697
|
+
cancelActiveOperation(state);
|
|
698
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
699
|
+
const coreValidation = validateSceneGraphMove(hierarchy, intent);
|
|
700
|
+
if (!coreValidation.ok) {
|
|
701
|
+
state.status = `Move rejected: ${coreValidation.reason ?? 'invalid scene graph move'}`;
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
const projectValidation = options.documentAdapter.validateSceneGraphMove?.(document, intent);
|
|
705
|
+
if (projectValidation && !projectValidation.ok) {
|
|
706
|
+
state.status = `Move rejected: ${projectValidation.reason ?? 'project validation failed'}`;
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
if (!options.documentAdapter.createSceneGraphMovePatch && intent.placement === 'inside' && intent.ids.length === 1 && intent.targetId) {
|
|
710
|
+
return dropSceneGraphNode(state, options, {
|
|
711
|
+
draggedId: intent.ids[0],
|
|
712
|
+
targetId: intent.targetId,
|
|
713
|
+
placement: 'inside',
|
|
714
|
+
preserveWorldTransform: intent.preserveWorldTransform,
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
const patch = options.documentAdapter.createSceneGraphMovePatch?.(document, intent);
|
|
718
|
+
if (!patch) {
|
|
719
|
+
state.status = 'Move rejected';
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
const result = state.session.dispatch({
|
|
723
|
+
type: 'document.patch',
|
|
724
|
+
label: patch.label ?? `Move ${intent.ids.length} node(s)`,
|
|
725
|
+
patch: patch.patch,
|
|
726
|
+
targetId: intent.ids[0] ?? undefined,
|
|
727
|
+
});
|
|
728
|
+
if (!result.documentChanged) {
|
|
729
|
+
state.status = 'Move unchanged';
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
const selection = sanitizeSelection(state, options, result.workingDocument, result.selection) ?? result.selection;
|
|
733
|
+
rebuildProjectionFromDocument(state, options, result.workingDocument, selection);
|
|
734
|
+
state.summary = summarizeDocument(options, result.workingDocument, state.session.getSource());
|
|
735
|
+
state.status = patch.label ?? `Moved ${patch.changedIds?.length ?? intent.ids.length} node(s)`;
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
function groupSceneGraphSelection(state, options, intent) {
|
|
739
|
+
const document = state.session?.getState().workingDocument;
|
|
740
|
+
if (state.mode !== 'editor' || !state.session || !document)
|
|
741
|
+
return false;
|
|
742
|
+
cancelActiveOperation(state);
|
|
743
|
+
const hierarchy = options.documentAdapter.getHierarchyItems(document);
|
|
744
|
+
const coreValidation = validateSceneGraphGroupSelection(hierarchy, intent);
|
|
745
|
+
if (!coreValidation.ok) {
|
|
746
|
+
state.status = `Group selection rejected: ${coreValidation.reason ?? 'invalid scene graph group selection'}`;
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
const projectValidation = options.documentAdapter.validateSceneGraphGroupSelection?.(document, intent);
|
|
750
|
+
if (projectValidation && !projectValidation.ok) {
|
|
751
|
+
state.status = `Group selection rejected: ${projectValidation.reason ?? 'project validation failed'}`;
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
const patch = options.documentAdapter.createSceneGraphGroupSelectionPatch?.(document, intent);
|
|
755
|
+
if (!patch) {
|
|
756
|
+
state.status = 'Group selection rejected';
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
const result = state.session.dispatch({
|
|
760
|
+
type: 'document.patch',
|
|
761
|
+
label: patch.label ?? 'Group Selection',
|
|
762
|
+
patch: patch.patch,
|
|
763
|
+
targetId: patch.createdId,
|
|
764
|
+
});
|
|
765
|
+
if (!result.documentChanged) {
|
|
766
|
+
state.status = 'Group selection unchanged';
|
|
767
|
+
return true;
|
|
768
|
+
}
|
|
769
|
+
let selection = result.selection;
|
|
770
|
+
if (patch.createdId && isNodeSelectableInDocument(options, result.workingDocument, patch.createdId)) {
|
|
771
|
+
selection = state.session.dispatch({
|
|
772
|
+
type: 'selection.replace',
|
|
773
|
+
selectedIds: [patch.createdId],
|
|
774
|
+
activeId: patch.createdId,
|
|
775
|
+
label: 'Select Created Group',
|
|
776
|
+
}).selection;
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
selection = sanitizeSelection(state, options, result.workingDocument, selection) ?? selection;
|
|
780
|
+
}
|
|
781
|
+
rebuildProjectionFromDocument(state, options, result.workingDocument, selection);
|
|
782
|
+
state.summary = summarizeDocument(options, result.workingDocument, state.session.getSource());
|
|
783
|
+
state.status = patch.label ?? `Grouped ${patch.changedIds?.length ?? intent.ids.length} node(s)`;
|
|
784
|
+
return true;
|
|
785
|
+
}
|
|
604
786
|
function sanitizeSelection(state, options, document, selection) {
|
|
605
787
|
const selectedIds = selection.selectedIds.filter(id => isNodeSelectableInDocument(options, document, id));
|
|
606
788
|
const activeId = selection.activeId && selectedIds.includes(selection.activeId)
|
|
@@ -668,19 +850,30 @@ function patchSerializedProperty(state, options, input) {
|
|
|
668
850
|
return false;
|
|
669
851
|
const document = state.session.getState().workingDocument;
|
|
670
852
|
const targetIds = input.targetIds && input.targetIds.length > 0 ? input.targetIds : [input.targetId];
|
|
853
|
+
const transaction = createInspectorEditTransaction(state, options, document, input, targetIds);
|
|
854
|
+
if (!transaction.ok) {
|
|
855
|
+
state.status = transaction.message;
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
const payload = transaction.payload;
|
|
671
859
|
if (targetIds.length > 1) {
|
|
672
860
|
const patch = options.documentAdapter.createSerializedMultiPropertyPatch?.({
|
|
673
861
|
document,
|
|
674
862
|
targetIds,
|
|
675
863
|
activeId: state.session.getState().selection.activeId,
|
|
676
|
-
path:
|
|
677
|
-
value:
|
|
864
|
+
path: payload.path,
|
|
865
|
+
value: payload.value,
|
|
866
|
+
control: payload.control,
|
|
867
|
+
valueType: payload.valueType,
|
|
868
|
+
commitMode: payload.commitMode,
|
|
869
|
+
persistence: payload.persistence,
|
|
870
|
+
source: payload.source,
|
|
678
871
|
});
|
|
679
872
|
if (!patch)
|
|
680
873
|
return false;
|
|
681
874
|
const result = state.session.dispatch({
|
|
682
875
|
type: 'document.patch',
|
|
683
|
-
label: patch.label ?? `Patch ${
|
|
876
|
+
label: patch.label ?? `Patch ${payload.path} on ${targetIds.length} objects`,
|
|
684
877
|
patch: patch.patch,
|
|
685
878
|
targetId: state.session.getState().selection.activeId ?? undefined,
|
|
686
879
|
});
|
|
@@ -688,30 +881,96 @@ function patchSerializedProperty(state, options, input) {
|
|
|
688
881
|
return false;
|
|
689
882
|
const changedIds = patch.changedIds ?? targetIds;
|
|
690
883
|
const workingDocument = result.workingDocument;
|
|
691
|
-
|
|
884
|
+
if (patch.reprojectIds?.length)
|
|
885
|
+
reprojectProjectionForChangedIds(state, options, workingDocument, patch.reprojectIds);
|
|
886
|
+
else
|
|
887
|
+
syncProjectionForChangedIds(state, options, workingDocument, changedIds);
|
|
692
888
|
state.summary = summarizeDocument(options, workingDocument, state.session.getSource());
|
|
693
|
-
state.status = patch.label ?? `Patch ${
|
|
889
|
+
state.status = patch.label ?? `Patch ${payload.path} on ${targetIds.length} objects`;
|
|
694
890
|
return true;
|
|
695
891
|
}
|
|
696
892
|
const patch = options.documentAdapter.createSerializedPropertyPatch({
|
|
697
|
-
...input,
|
|
698
893
|
document,
|
|
894
|
+
targetId: payload.targetId,
|
|
895
|
+
targetIds: payload.targetIds,
|
|
896
|
+
path: payload.path,
|
|
897
|
+
value: payload.value,
|
|
898
|
+
control: payload.control,
|
|
899
|
+
valueType: payload.valueType,
|
|
900
|
+
commitMode: payload.commitMode,
|
|
901
|
+
persistence: payload.persistence,
|
|
902
|
+
source: payload.source,
|
|
699
903
|
});
|
|
700
904
|
if (!patch)
|
|
701
905
|
return false;
|
|
702
906
|
const result = state.session.dispatch({
|
|
703
907
|
type: 'document.patch',
|
|
704
|
-
label: patch.label ?? `Patch ${
|
|
908
|
+
label: patch.label ?? `Patch ${payload.path}`,
|
|
705
909
|
patch: patch.patch,
|
|
706
910
|
});
|
|
707
|
-
if (patch.
|
|
911
|
+
if (patch.reprojectIds?.length)
|
|
912
|
+
reprojectProjectionForChangedIds(state, options, result.workingDocument, patch.reprojectIds);
|
|
913
|
+
else if (patch.changedIds)
|
|
708
914
|
syncProjectionForChangedIds(state, options, result.workingDocument, patch.changedIds);
|
|
709
915
|
else
|
|
710
|
-
syncProjectionForDispatchResult(state, options, result, patch.changedId ??
|
|
916
|
+
syncProjectionForDispatchResult(state, options, result, patch.changedId ?? payload.targetId);
|
|
711
917
|
state.summary = summarizeDocument(options, result.workingDocument, state.session.getSource());
|
|
712
|
-
state.status = patch.label ?? `Patched ${
|
|
918
|
+
state.status = patch.label ?? `Patched ${payload.path}`;
|
|
713
919
|
return true;
|
|
714
920
|
}
|
|
921
|
+
function createInspectorEditTransaction(state, options, document, input, targetIds) {
|
|
922
|
+
const property = findInspectorPropertyForEdit(state, options, document, input, targetIds);
|
|
923
|
+
if (!property) {
|
|
924
|
+
return { ok: false, message: `Inspector property not found: ${input.path}.` };
|
|
925
|
+
}
|
|
926
|
+
const result = createInspectorEditPayload(property, {
|
|
927
|
+
targetId: input.targetId,
|
|
928
|
+
targetIds: input.targetIds,
|
|
929
|
+
path: input.path,
|
|
930
|
+
value: input.value,
|
|
931
|
+
control: input.control,
|
|
932
|
+
valueType: input.valueType,
|
|
933
|
+
commitMode: input.commitMode,
|
|
934
|
+
persistence: input.persistence,
|
|
935
|
+
source: input.source,
|
|
936
|
+
});
|
|
937
|
+
if (!result.ok)
|
|
938
|
+
return { ok: false, message: result.message };
|
|
939
|
+
return { ok: true, payload: result.value };
|
|
940
|
+
}
|
|
941
|
+
function findInspectorPropertyForEdit(state, options, document, input, targetIds) {
|
|
942
|
+
const inspector = createInspectorObjectForEdit(state, options, document, input, targetIds);
|
|
943
|
+
if (!inspector)
|
|
944
|
+
return null;
|
|
945
|
+
return findInspectorPropertyByPath(inspector, input.path);
|
|
946
|
+
}
|
|
947
|
+
function createInspectorObjectForEdit(state, options, document, input, targetIds) {
|
|
948
|
+
if (targetIds.length > 1) {
|
|
949
|
+
const activeId = state.session?.getState().selection.activeId ?? input.targetId ?? null;
|
|
950
|
+
const inspector = options.documentAdapter.getInspectorMultiObject?.(document, targetIds, activeId) ?? null;
|
|
951
|
+
if (inspector)
|
|
952
|
+
return withInspectorComponentSections(state, options, document, inspector);
|
|
953
|
+
const serializedMultiObject = options.documentAdapter.getSerializedMultiObject?.(document, targetIds, activeId) ?? null;
|
|
954
|
+
return serializedMultiObject
|
|
955
|
+
? withInspectorComponentSections(state, options, document, serializedMultiObjectToInspectorObject(serializedMultiObject, document))
|
|
956
|
+
: null;
|
|
957
|
+
}
|
|
958
|
+
const inspector = options.documentAdapter.getInspectorObject?.(document, input.targetId) ?? null;
|
|
959
|
+
if (inspector)
|
|
960
|
+
return withInspectorComponentSections(state, options, document, inspector);
|
|
961
|
+
const serializedObject = options.documentAdapter.getSerializedObject(document, input.targetId);
|
|
962
|
+
return serializedObject
|
|
963
|
+
? withInspectorComponentSections(state, options, document, serializedObjectToInspectorObject(serializedObject, document))
|
|
964
|
+
: null;
|
|
965
|
+
}
|
|
966
|
+
function findInspectorPropertyByPath(inspector, path) {
|
|
967
|
+
for (const section of inspector.sections) {
|
|
968
|
+
const property = section.properties.find(candidate => candidate.path === path);
|
|
969
|
+
if (property)
|
|
970
|
+
return property;
|
|
971
|
+
}
|
|
972
|
+
return null;
|
|
973
|
+
}
|
|
715
974
|
function commitGizmoTransform(state, options, event) {
|
|
716
975
|
if (state.mode !== 'editor' || !state.session)
|
|
717
976
|
return false;
|
|
@@ -865,15 +1124,45 @@ function syncProjectionForChangedIds(state, options, document, changedIds) {
|
|
|
865
1124
|
const selection = state.session?.getState().selection ?? { selectedIds: [], activeId: null };
|
|
866
1125
|
syncSelectionToProjection(state, selection);
|
|
867
1126
|
}
|
|
1127
|
+
function reprojectProjectionForChangedIds(state, options, document, changedIds) {
|
|
1128
|
+
for (const changedId of changedIds) {
|
|
1129
|
+
const projectedNode = options.documentAdapter.getProjectionNode(document, changedId);
|
|
1130
|
+
if (projectedNode)
|
|
1131
|
+
state.projection?.projectNode(projectedNode);
|
|
1132
|
+
}
|
|
1133
|
+
const selection = state.session?.getState().selection ?? { selectedIds: [], activeId: null };
|
|
1134
|
+
syncSelectionToProjection(state, selection);
|
|
1135
|
+
state.gizmo?.refreshSelection();
|
|
1136
|
+
}
|
|
868
1137
|
function createUiState(state, options) {
|
|
869
1138
|
const sessionState = state.session?.getState();
|
|
870
1139
|
const document = sessionState?.workingDocument ?? null;
|
|
871
1140
|
const selectedIds = sessionState?.selection.selectedIds ?? [];
|
|
872
1141
|
const activeId = sessionState?.selection.activeId ?? null;
|
|
1142
|
+
const serializedObject = document && activeId && selectedIds.length === 1
|
|
1143
|
+
? options.documentAdapter.getSerializedObject(document, activeId)
|
|
1144
|
+
: null;
|
|
1145
|
+
const serializedMultiObject = document && selectedIds.length > 1
|
|
1146
|
+
? options.documentAdapter.getSerializedMultiObject?.(document, selectedIds, activeId) ?? null
|
|
1147
|
+
: null;
|
|
1148
|
+
const inspectorObjectBase = document && activeId && selectedIds.length === 1
|
|
1149
|
+
? options.documentAdapter.getInspectorObject?.(document, activeId) ?? (serializedObject ? serializedObjectToInspectorObject(serializedObject, document) : null)
|
|
1150
|
+
: null;
|
|
1151
|
+
const inspectorMultiObjectBase = document && selectedIds.length > 1
|
|
1152
|
+
? options.documentAdapter.getInspectorMultiObject?.(document, selectedIds, activeId) ?? (serializedMultiObject ? serializedMultiObjectToInspectorObject(serializedMultiObject, document) : null)
|
|
1153
|
+
: null;
|
|
1154
|
+
const inspectorObject = document && inspectorObjectBase
|
|
1155
|
+
? withRuntimeInspectorSections(state, options, document, inspectorObjectBase)
|
|
1156
|
+
: inspectorObjectBase;
|
|
1157
|
+
const inspectorMultiObject = document && inspectorMultiObjectBase
|
|
1158
|
+
? withRuntimeInspectorSections(state, options, document, inspectorMultiObjectBase)
|
|
1159
|
+
: inspectorMultiObjectBase;
|
|
873
1160
|
return {
|
|
874
1161
|
mode: state.mode,
|
|
875
1162
|
busy: state.busy,
|
|
876
1163
|
status: state.status,
|
|
1164
|
+
statusTone: state.statusToneStatus === state.status ? state.statusTone : 'default',
|
|
1165
|
+
statusDetails: state.statusToneStatus === state.status ? state.statusDetails : '',
|
|
877
1166
|
summary: state.summary,
|
|
878
1167
|
assetFilter: state.assetFilter,
|
|
879
1168
|
assets: state.assets
|
|
@@ -887,12 +1176,10 @@ function createUiState(state, options) {
|
|
|
887
1176
|
count: selectedIds.length,
|
|
888
1177
|
activeId,
|
|
889
1178
|
},
|
|
890
|
-
serializedObject
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
? options.documentAdapter.getSerializedMultiObject?.(document, selectedIds, activeId) ?? null
|
|
895
|
-
: null,
|
|
1179
|
+
serializedObject,
|
|
1180
|
+
serializedMultiObject,
|
|
1181
|
+
inspectorObject,
|
|
1182
|
+
inspectorMultiObject,
|
|
896
1183
|
boxSelection: state.boxSelection,
|
|
897
1184
|
transformTool: {
|
|
898
1185
|
activeTool: state.gizmo?.getState().tool ?? state.transformTool,
|
|
@@ -912,12 +1199,159 @@ function createUiState(state, options) {
|
|
|
912
1199
|
: null,
|
|
913
1200
|
};
|
|
914
1201
|
}
|
|
1202
|
+
function withRuntimeInspectorSections(state, options, document, inspectorObject) {
|
|
1203
|
+
const baseInspectorObject = withInspectorComponentSections(state, options, document, inspectorObject);
|
|
1204
|
+
const activeId = inspectorObject.activeId;
|
|
1205
|
+
const projectionNode = activeId ? options.documentAdapter.getProjectionNode(document, activeId) : null;
|
|
1206
|
+
const projectedRoot = activeId ? state.projection?.getProjectedNode(activeId)?.root ?? null : null;
|
|
1207
|
+
const context = {
|
|
1208
|
+
document,
|
|
1209
|
+
targetIds: baseInspectorObject.targetIds,
|
|
1210
|
+
activeId,
|
|
1211
|
+
inspectorObject: baseInspectorObject,
|
|
1212
|
+
projectionNode,
|
|
1213
|
+
projectedRoot,
|
|
1214
|
+
};
|
|
1215
|
+
const runtimeSections = [
|
|
1216
|
+
...createDefaultRuntimeInspectorSections(context),
|
|
1217
|
+
...(options.documentAdapter.getRuntimeInspectorSections?.(context) ?? []),
|
|
1218
|
+
];
|
|
1219
|
+
const sections = mergeInspectorSections(baseInspectorObject.sections, runtimeSections, {
|
|
1220
|
+
propertyConflict: options.inspector?.propertyConflict,
|
|
1221
|
+
});
|
|
1222
|
+
return {
|
|
1223
|
+
...baseInspectorObject,
|
|
1224
|
+
sections,
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
function withInspectorComponentSections(state, options, document, inspectorObject) {
|
|
1228
|
+
const context = createHarnessInspectorSelectionContext(state, document, inspectorObject);
|
|
1229
|
+
return mergeLocalEditorHarnessInspectorComponentSections({
|
|
1230
|
+
inspectorObject,
|
|
1231
|
+
components: options.inspector?.components,
|
|
1232
|
+
context,
|
|
1233
|
+
componentConflict: options.inspector?.componentConflict,
|
|
1234
|
+
propertyConflict: options.inspector?.propertyConflict,
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
function getInspectorComponentSections(input) {
|
|
1238
|
+
if (isInspectorRegistry(input.components))
|
|
1239
|
+
return input.components.getSections(input.context);
|
|
1240
|
+
const registry = createInspectorRegistry({
|
|
1241
|
+
onConflict: input.componentConflict,
|
|
1242
|
+
propertyConflict: input.propertyConflict,
|
|
1243
|
+
});
|
|
1244
|
+
for (const component of input.components)
|
|
1245
|
+
registry.register(component);
|
|
1246
|
+
return registry.getSections(input.context);
|
|
1247
|
+
}
|
|
1248
|
+
function createHarnessInspectorSelectionContext(state, document, inspectorObject) {
|
|
1249
|
+
const activeId = inspectorObject.activeId;
|
|
1250
|
+
const projectedRoot = activeId ? state.projection?.getProjectedNode(activeId)?.root ?? null : null;
|
|
1251
|
+
return {
|
|
1252
|
+
...inspectorObject.selection,
|
|
1253
|
+
targetIds: inspectorObject.targetIds,
|
|
1254
|
+
activeId,
|
|
1255
|
+
document,
|
|
1256
|
+
runtimeTarget: inspectorObject.selection.runtimeTarget ?? projectedRoot ?? undefined,
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
function isInspectorRegistry(components) {
|
|
1260
|
+
return !Array.isArray(components)
|
|
1261
|
+
&& typeof components.getSections === 'function';
|
|
1262
|
+
}
|
|
1263
|
+
function createDefaultRuntimeInspectorSections(context) {
|
|
1264
|
+
if (context.targetIds.length !== 1)
|
|
1265
|
+
return [];
|
|
1266
|
+
const root = context.projectedRoot;
|
|
1267
|
+
const projectionNode = context.projectionNode;
|
|
1268
|
+
if (!root && !projectionNode)
|
|
1269
|
+
return [];
|
|
1270
|
+
const properties = [];
|
|
1271
|
+
if (projectionNode) {
|
|
1272
|
+
properties.push(createRuntimeInspectorProperty('runtime.projection.nodeId', 'Projected ID', projectionNode.id, properties.length));
|
|
1273
|
+
const assetSource = projectionNode.asset?.sourceId ?? projectionNode.asset?.id ?? '';
|
|
1274
|
+
if (assetSource)
|
|
1275
|
+
properties.push(createRuntimeInspectorProperty('runtime.projection.assetSource', 'Asset Source', assetSource, properties.length));
|
|
1276
|
+
}
|
|
1277
|
+
const runtimeClass = readRuntimeClassName(root);
|
|
1278
|
+
if (runtimeClass)
|
|
1279
|
+
properties.push(createRuntimeInspectorProperty('runtime.root.className', 'Runtime Class', runtimeClass, properties.length));
|
|
1280
|
+
const runtimeName = readRuntimeStringProperty(root, 'name');
|
|
1281
|
+
if (runtimeName)
|
|
1282
|
+
properties.push(createRuntimeInspectorProperty('runtime.root.name', 'Runtime Name', runtimeName, properties.length));
|
|
1283
|
+
const childCount = readRuntimeChildCount(root);
|
|
1284
|
+
if (childCount != null)
|
|
1285
|
+
properties.push(createRuntimeInspectorProperty('runtime.root.children', 'Runtime Children', childCount, properties.length));
|
|
1286
|
+
if (properties.length === 0)
|
|
1287
|
+
return [];
|
|
1288
|
+
return [{
|
|
1289
|
+
id: 'runtimeDiagnostics',
|
|
1290
|
+
title: 'Runtime Diagnostics',
|
|
1291
|
+
order: 900,
|
|
1292
|
+
placement: 'body',
|
|
1293
|
+
persistence: 'runtime',
|
|
1294
|
+
runtimeOnly: true,
|
|
1295
|
+
properties,
|
|
1296
|
+
}];
|
|
1297
|
+
}
|
|
1298
|
+
function createRuntimeInspectorProperty(path, label, value, order) {
|
|
1299
|
+
return {
|
|
1300
|
+
path,
|
|
1301
|
+
label,
|
|
1302
|
+
valueType: typeof value === 'number' ? 'number' : typeof value === 'boolean' ? 'boolean' : typeof value === 'object' ? 'object' : 'string',
|
|
1303
|
+
control: 'readonly',
|
|
1304
|
+
value,
|
|
1305
|
+
readOnly: true,
|
|
1306
|
+
persistence: 'runtime',
|
|
1307
|
+
commitMode: 'blur',
|
|
1308
|
+
order,
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
function readRuntimeClassName(value) {
|
|
1312
|
+
const record = isObjectRecord(value) ? value : null;
|
|
1313
|
+
const getter = record?.getClassName;
|
|
1314
|
+
if (typeof getter === 'function') {
|
|
1315
|
+
const className = getter.call(value);
|
|
1316
|
+
if (typeof className === 'string' && className.trim())
|
|
1317
|
+
return className;
|
|
1318
|
+
}
|
|
1319
|
+
const constructorName = record?.constructor && typeof record.constructor === 'function'
|
|
1320
|
+
? record.constructor.name
|
|
1321
|
+
: '';
|
|
1322
|
+
return constructorName && constructorName !== 'Object' ? constructorName : null;
|
|
1323
|
+
}
|
|
1324
|
+
function readRuntimeStringProperty(value, key) {
|
|
1325
|
+
if (!isObjectRecord(value))
|
|
1326
|
+
return null;
|
|
1327
|
+
const raw = value[key];
|
|
1328
|
+
return typeof raw === 'string' && raw.trim() ? raw : null;
|
|
1329
|
+
}
|
|
1330
|
+
function readRuntimeChildCount(value) {
|
|
1331
|
+
if (!isObjectRecord(value))
|
|
1332
|
+
return null;
|
|
1333
|
+
const children = value.getChildren;
|
|
1334
|
+
if (typeof children === 'function') {
|
|
1335
|
+
const result = children.call(value);
|
|
1336
|
+
return Array.isArray(result) ? result.length : null;
|
|
1337
|
+
}
|
|
1338
|
+
return null;
|
|
1339
|
+
}
|
|
1340
|
+
function isObjectRecord(value) {
|
|
1341
|
+
return !!value && typeof value === 'object';
|
|
1342
|
+
}
|
|
915
1343
|
function summarizeDocument(options, document, _source) {
|
|
916
1344
|
return options.documentAdapter.summarize?.(document) ?? '';
|
|
917
1345
|
}
|
|
918
|
-
function
|
|
1346
|
+
export function summarizeLocalEditorAuthoringFailure(result) {
|
|
919
1347
|
const diagnostic = result.diagnostics.find(item => item.severity === 'error') ?? result.diagnostics[0];
|
|
920
|
-
|
|
1348
|
+
const status = diagnostic?.message ?? result.reason ?? 'Authoring source commit failed';
|
|
1349
|
+
const diagnostics = summarizeDiagnostics(result.diagnostics);
|
|
1350
|
+
const details = [
|
|
1351
|
+
result.reason ? `Reason: ${result.reason}` : '',
|
|
1352
|
+
diagnostics ? `Diagnostics: ${diagnostics}` : '',
|
|
1353
|
+
].filter(Boolean).join('\n') || status;
|
|
1354
|
+
return { status, details };
|
|
921
1355
|
}
|
|
922
1356
|
function summarizeDiagnostics(diagnostics) {
|
|
923
1357
|
if (!diagnostics?.length)
|