worldorbit 2.5.16 → 2.5.17
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/README.md +1 -1
- package/dist/browser/core/dist/index.js +750 -73
- package/dist/browser/editor/dist/index.js +1303 -135
- package/dist/browser/markdown/dist/index.js +631 -72
- package/dist/browser/viewer/dist/index.js +658 -77
- package/dist/unpkg/core/dist/index.js +750 -73
- package/dist/unpkg/editor/dist/index.js +1303 -135
- package/dist/unpkg/markdown/dist/index.js +631 -72
- package/dist/unpkg/viewer/dist/index.js +658 -77
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +284 -202
- package/dist/unpkg/worldorbit-markdown.min.js +66 -58
- package/dist/unpkg/worldorbit-viewer.min.js +76 -68
- package/dist/unpkg/worldorbit.js +797 -78
- package/dist/unpkg/worldorbit.min.js +80 -72
- package/package.json +1 -1
- package/packages/core/dist/atlas-edit.js +74 -0
- package/packages/core/dist/atlas-validate.js +122 -8
- package/packages/core/dist/draft-parse.js +212 -8
- package/packages/core/dist/draft.d.ts +5 -2
- package/packages/core/dist/draft.js +59 -3
- package/packages/core/dist/format.js +63 -1
- package/packages/core/dist/normalize.js +1 -0
- package/packages/core/dist/scene.js +248 -46
- package/packages/core/dist/types.d.ts +41 -2
- package/packages/editor/dist/editor.js +597 -61
- package/packages/editor/dist/types.d.ts +3 -1
- package/packages/viewer/dist/atlas-state.js +6 -0
- package/packages/viewer/dist/atlas-viewer.js +1 -0
- package/packages/viewer/dist/render.js +31 -2
- package/packages/viewer/dist/theme.js +1 -0
- package/packages/viewer/dist/tooltip.js +9 -0
- package/packages/viewer/dist/types.d.ts +8 -1
- package/packages/viewer/dist/viewer.js +12 -1
|
@@ -86,6 +86,34 @@ const FIELD_HELP = {
|
|
|
86
86
|
description: "Rotates the saved camera angle in degrees.",
|
|
87
87
|
references: ["90deg = quarter turn", "180deg = flip"],
|
|
88
88
|
},
|
|
89
|
+
"viewpoint-events": {
|
|
90
|
+
description: "Lists event IDs that this viewpoint should feature in its detail panel.",
|
|
91
|
+
references: ["solar-eclipse-naar", "transit-window conjunction"],
|
|
92
|
+
},
|
|
93
|
+
"event-kind": {
|
|
94
|
+
description: "Short semantic event type for tooling and viewer overlays.",
|
|
95
|
+
references: ["solar-eclipse", "lunar-eclipse", "transit"],
|
|
96
|
+
},
|
|
97
|
+
"event-target": {
|
|
98
|
+
description: "Primary object this event is centered on.",
|
|
99
|
+
references: ["Naar", "Seyra"],
|
|
100
|
+
},
|
|
101
|
+
"event-participants": {
|
|
102
|
+
description: "Objects that participate in the event snapshot or description.",
|
|
103
|
+
references: ["Iyath Naar Seyra", "Naar Seyra Orun"],
|
|
104
|
+
},
|
|
105
|
+
"event-timing": {
|
|
106
|
+
description: "Free-text timing note for the event.",
|
|
107
|
+
references: ['"Every late bloom season"', '"At local midyear"'],
|
|
108
|
+
},
|
|
109
|
+
"event-visibility": {
|
|
110
|
+
description: "Notes where or how the event is visible.",
|
|
111
|
+
references: ['"Visible from Naar"', '"Southern hemisphere only"'],
|
|
112
|
+
},
|
|
113
|
+
"event-viewpoints": {
|
|
114
|
+
description: "Viewpoint IDs that should list this event prominently.",
|
|
115
|
+
references: ["naar-system", "overview inner-system"],
|
|
116
|
+
},
|
|
89
117
|
"placement-target": {
|
|
90
118
|
description: "Names the body or reference this object is attached to.",
|
|
91
119
|
references: ["orbit Primary", "surface Homeworld", "at Naar:L4"],
|
|
@@ -206,12 +234,25 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
206
234
|
minimap: true,
|
|
207
235
|
tooltipMode: "hover",
|
|
208
236
|
onSelectionChange(selectedObject) {
|
|
237
|
+
const activeEventId = selection ? selectionEventId(selection) : null;
|
|
209
238
|
if (ignoreViewerSelection || !selectedObject) {
|
|
210
|
-
if (!ignoreViewerSelection
|
|
211
|
-
|
|
239
|
+
if (!ignoreViewerSelection) {
|
|
240
|
+
if (selection?.kind === "event-pose" && selection.id) {
|
|
241
|
+
setSelection({ kind: "event", id: selection.id }, false, true);
|
|
242
|
+
}
|
|
243
|
+
else if (selection?.kind === "object") {
|
|
244
|
+
setSelection(activeEventId ? { kind: "event", id: activeEventId } : null, false, true);
|
|
245
|
+
}
|
|
246
|
+
else if (selection?.kind === "event" && selection.id) {
|
|
247
|
+
setSelection({ kind: "event", id: selection.id }, false, true);
|
|
248
|
+
}
|
|
212
249
|
}
|
|
213
250
|
return;
|
|
214
251
|
}
|
|
252
|
+
if (activeEventId && findEventPose(atlasDocument, activeEventId, selectedObject.objectId)) {
|
|
253
|
+
setSelection({ kind: "event-pose", id: activeEventId, key: selectedObject.objectId }, false, true);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
215
256
|
setSelection({ kind: "object", id: selectedObject.objectId }, false, true);
|
|
216
257
|
},
|
|
217
258
|
onViewChange() {
|
|
@@ -221,6 +262,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
221
262
|
toolbar.addEventListener("click", handleToolbarClick);
|
|
222
263
|
outline.addEventListener("click", handleOutlineClick);
|
|
223
264
|
overlay.addEventListener("pointerdown", handleOverlayPointerDown);
|
|
265
|
+
inspector?.addEventListener("click", handleInspectorClick);
|
|
224
266
|
inspector?.addEventListener("input", handleInspectorInput);
|
|
225
267
|
inspector?.addEventListener("change", handleInspectorChange);
|
|
226
268
|
sourcePane?.addEventListener("input", handleSourceInput);
|
|
@@ -295,6 +337,28 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
295
337
|
replaceAtlasDocument(nextDocument, true, { kind: "object", id });
|
|
296
338
|
return id;
|
|
297
339
|
},
|
|
340
|
+
addEvent() {
|
|
341
|
+
const id = createUniqueId("event", atlasDocument.events.map((event) => event.id));
|
|
342
|
+
const created = {
|
|
343
|
+
id,
|
|
344
|
+
kind: "",
|
|
345
|
+
label: humanizeIdentifier(id),
|
|
346
|
+
summary: null,
|
|
347
|
+
targetObjectId: null,
|
|
348
|
+
participantObjectIds: [],
|
|
349
|
+
timing: null,
|
|
350
|
+
visibility: null,
|
|
351
|
+
tags: [],
|
|
352
|
+
color: null,
|
|
353
|
+
hidden: false,
|
|
354
|
+
positions: [],
|
|
355
|
+
};
|
|
356
|
+
const nextDocument = cloneAtlasDocument(atlasDocument);
|
|
357
|
+
nextDocument.events.push(created);
|
|
358
|
+
nextDocument.events.sort(compareEvents);
|
|
359
|
+
replaceAtlasDocument(nextDocument, true, { kind: "event", id });
|
|
360
|
+
return id;
|
|
361
|
+
},
|
|
298
362
|
addViewpoint() {
|
|
299
363
|
const id = createUniqueId("viewpoint", atlasDocument.system?.viewpoints.map((viewpoint) => viewpoint.id) ?? []);
|
|
300
364
|
const created = {
|
|
@@ -303,6 +367,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
303
367
|
summary: "",
|
|
304
368
|
focusObjectId: null,
|
|
305
369
|
selectedObjectId: null,
|
|
370
|
+
events: [],
|
|
306
371
|
projection: atlasDocument.system?.defaults.view ?? "topdown",
|
|
307
372
|
preset: atlasDocument.system?.defaults.preset ?? null,
|
|
308
373
|
zoom: null,
|
|
@@ -363,6 +428,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
363
428
|
toolbar.removeEventListener("click", handleToolbarClick);
|
|
364
429
|
outline.removeEventListener("click", handleOutlineClick);
|
|
365
430
|
overlay.removeEventListener("pointerdown", handleOverlayPointerDown);
|
|
431
|
+
inspector?.removeEventListener("click", handleInspectorClick);
|
|
366
432
|
inspector?.removeEventListener("input", handleInspectorInput);
|
|
367
433
|
inspector?.removeEventListener("change", handleInspectorChange);
|
|
368
434
|
sourcePane?.removeEventListener("input", handleSourceInput);
|
|
@@ -408,7 +474,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
408
474
|
}
|
|
409
475
|
function getCurrentSourceForExport() {
|
|
410
476
|
if (dragState?.changed) {
|
|
411
|
-
return
|
|
477
|
+
return formatAtlasSource(atlasDocument);
|
|
412
478
|
}
|
|
413
479
|
return canonicalSource;
|
|
414
480
|
}
|
|
@@ -447,7 +513,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
447
513
|
}
|
|
448
514
|
clearSourceInputTimer();
|
|
449
515
|
atlasDocument = cloneAtlasDocument(nextDocument);
|
|
450
|
-
canonicalSource =
|
|
516
|
+
canonicalSource = formatAtlasSource(atlasDocument);
|
|
451
517
|
if (!preserveSourceText) {
|
|
452
518
|
sourceText = canonicalSource;
|
|
453
519
|
}
|
|
@@ -488,10 +554,10 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
488
554
|
if (commitHistory) {
|
|
489
555
|
history.push(createHistoryEntry());
|
|
490
556
|
future.length = 0;
|
|
491
|
-
sourceText =
|
|
557
|
+
sourceText = formatAtlasSource(nextDocument);
|
|
492
558
|
}
|
|
493
559
|
atlasDocument = cloneAtlasDocument(nextDocument);
|
|
494
|
-
canonicalSource =
|
|
560
|
+
canonicalSource = formatAtlasSource(atlasDocument);
|
|
495
561
|
diagnostics = mergeDiagnostics(loadedDiagnostics, collectDocumentDiagnostics(atlasDocument));
|
|
496
562
|
selection = normalizeSelection(selection);
|
|
497
563
|
syncViewer({
|
|
@@ -510,11 +576,15 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
510
576
|
const previousState = viewer.getState();
|
|
511
577
|
const currentRenderOptions = viewer.getRenderOptions();
|
|
512
578
|
const nextPreset = atlasDocument.system?.defaults.preset ?? "atlas-card";
|
|
579
|
+
const nextActiveEventId = selection ? selectionEventId(selection) : null;
|
|
513
580
|
ignoreViewerSelection = true;
|
|
514
|
-
if (currentRenderOptions.preset !== nextPreset ||
|
|
581
|
+
if (currentRenderOptions.preset !== nextPreset ||
|
|
582
|
+
currentRenderOptions.projection !== "document" ||
|
|
583
|
+
(currentRenderOptions.activeEventId ?? null) !== nextActiveEventId) {
|
|
515
584
|
viewer.setRenderOptions({
|
|
516
585
|
preset: nextPreset,
|
|
517
586
|
projection: "document",
|
|
587
|
+
activeEventId: nextActiveEventId,
|
|
518
588
|
});
|
|
519
589
|
}
|
|
520
590
|
viewer.setDocument(materializeAtlasDocument(atlasDocument));
|
|
@@ -524,12 +594,19 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
524
594
|
else if (options.preserveCamera !== false) {
|
|
525
595
|
viewer.setState({
|
|
526
596
|
...previousState,
|
|
527
|
-
selectedObjectId: selection?.kind === "object"
|
|
597
|
+
selectedObjectId: selection?.kind === "object"
|
|
598
|
+
? selection.id ?? null
|
|
599
|
+
: selection?.kind === "event-pose"
|
|
600
|
+
? selection.key ?? null
|
|
601
|
+
: null,
|
|
528
602
|
});
|
|
529
603
|
}
|
|
530
604
|
else if (selection?.kind === "object" && selection.id) {
|
|
531
605
|
viewer.focusObject(selection.id);
|
|
532
606
|
}
|
|
607
|
+
else if (selection?.kind === "event-pose" && selection.key) {
|
|
608
|
+
viewer.focusObject(selection.key);
|
|
609
|
+
}
|
|
533
610
|
ignoreViewerSelection = false;
|
|
534
611
|
}
|
|
535
612
|
function emitSnapshot() {
|
|
@@ -547,9 +624,13 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
547
624
|
selection = normalizeSelection(nextSelection);
|
|
548
625
|
if (syncViewerSelection) {
|
|
549
626
|
ignoreViewerSelection = true;
|
|
627
|
+
viewer.setRenderOptions({ activeEventId: selection ? selectionEventId(selection) : null });
|
|
550
628
|
if (selection?.kind === "object" && selection.id) {
|
|
551
629
|
viewer.focusObject(selection.id);
|
|
552
630
|
}
|
|
631
|
+
else if (selection?.kind === "event-pose" && selection.key) {
|
|
632
|
+
viewer.focusObject(selection.key);
|
|
633
|
+
}
|
|
553
634
|
else if (selection?.kind === "viewpoint" && selection.id) {
|
|
554
635
|
viewer.goToViewpoint(selection.id);
|
|
555
636
|
}
|
|
@@ -604,6 +685,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
604
685
|
${OBJECT_TYPES.map((type) => `<option value="${escapeHtml(type)}"${type === objectType ? " selected" : ""}>${escapeHtml(humanizeIdentifier(type))}</option>`).join("")}
|
|
605
686
|
</select>
|
|
606
687
|
<button type="button" data-editor-action="add-object">Add object</button>
|
|
688
|
+
<button type="button" data-editor-action="add-event">Add event</button>
|
|
607
689
|
<button type="button" data-editor-action="add-viewpoint">Add viewpoint</button>
|
|
608
690
|
<button type="button" data-editor-action="add-annotation">Add annotation</button>
|
|
609
691
|
<button type="button" data-editor-action="add-metadata">Add metadata</button>
|
|
@@ -650,6 +732,14 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
650
732
|
.join("")
|
|
651
733
|
: `<p class="wo-editor-empty">No annotations yet.</p>`}
|
|
652
734
|
</div>
|
|
735
|
+
<div class="wo-editor-outline-section">
|
|
736
|
+
<h3>Events</h3>
|
|
737
|
+
${atlasDocument.events.length > 0
|
|
738
|
+
? atlasDocument.events
|
|
739
|
+
.map((eventEntry) => renderEventOutlineItems(eventEntry, activeKey, diagnosticBuckets))
|
|
740
|
+
.join("")
|
|
741
|
+
: `<p class="wo-editor-empty">No events yet.</p>`}
|
|
742
|
+
</div>
|
|
653
743
|
<div class="wo-editor-outline-section">
|
|
654
744
|
<h3>Objects</h3>
|
|
655
745
|
${atlasDocument.objects.length > 0
|
|
@@ -701,6 +791,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
701
791
|
selection: selection ? { path: { ...selection } } : null,
|
|
702
792
|
system: atlasDocument.system,
|
|
703
793
|
viewpoints: atlasDocument.system?.viewpoints ?? [],
|
|
794
|
+
events: atlasDocument.events,
|
|
704
795
|
objects: atlasDocument.objects,
|
|
705
796
|
};
|
|
706
797
|
if (!selection) {
|
|
@@ -729,6 +820,17 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
729
820
|
applyInspectorSectionState(inspector, inspectorSectionState);
|
|
730
821
|
decorateInspectorDiagnostics(selection, diagnostics);
|
|
731
822
|
return;
|
|
823
|
+
case "event":
|
|
824
|
+
inspector.innerHTML = diagnosticSummary + renderEventInspector(formState, selection.id ?? "");
|
|
825
|
+
applyInspectorSectionState(inspector, inspectorSectionState);
|
|
826
|
+
decorateInspectorDiagnostics(selection, diagnostics);
|
|
827
|
+
return;
|
|
828
|
+
case "event-pose":
|
|
829
|
+
inspector.innerHTML =
|
|
830
|
+
diagnosticSummary + renderEventPoseInspector(formState, selection.id ?? "", selection.key ?? "");
|
|
831
|
+
applyInspectorSectionState(inspector, inspectorSectionState);
|
|
832
|
+
decorateInspectorDiagnostics(selection, diagnostics);
|
|
833
|
+
return;
|
|
732
834
|
case "annotation":
|
|
733
835
|
inspector.innerHTML = diagnosticSummary + renderAnnotationInspector(formState, selection.id ?? "");
|
|
734
836
|
applyInspectorSectionState(inspector, inspectorSectionState);
|
|
@@ -761,10 +863,15 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
761
863
|
return;
|
|
762
864
|
}
|
|
763
865
|
overlay.innerHTML = "";
|
|
764
|
-
|
|
866
|
+
const selectedObjectId = selection?.kind === "object"
|
|
867
|
+
? selection.id ?? null
|
|
868
|
+
: selection?.kind === "event-pose"
|
|
869
|
+
? selection.key ?? null
|
|
870
|
+
: null;
|
|
871
|
+
if (!selectedObjectId) {
|
|
765
872
|
return;
|
|
766
873
|
}
|
|
767
|
-
const details = viewer.getObjectDetails(
|
|
874
|
+
const details = viewer.getObjectDetails(selectedObjectId);
|
|
768
875
|
if (!details) {
|
|
769
876
|
return;
|
|
770
877
|
}
|
|
@@ -883,6 +990,9 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
883
990
|
case "add-viewpoint":
|
|
884
991
|
api.addViewpoint();
|
|
885
992
|
return;
|
|
993
|
+
case "add-event":
|
|
994
|
+
api.addEvent();
|
|
995
|
+
return;
|
|
886
996
|
case "add-annotation":
|
|
887
997
|
api.addAnnotation();
|
|
888
998
|
return;
|
|
@@ -917,6 +1027,35 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
917
1027
|
key: button.dataset.pathKey || undefined,
|
|
918
1028
|
}, true, true);
|
|
919
1029
|
}
|
|
1030
|
+
function handleInspectorClick(event) {
|
|
1031
|
+
const pathButton = event.target?.closest("[data-path-kind]");
|
|
1032
|
+
if (pathButton) {
|
|
1033
|
+
setSelection({
|
|
1034
|
+
kind: pathButton.dataset.pathKind,
|
|
1035
|
+
id: pathButton.dataset.pathId || undefined,
|
|
1036
|
+
key: pathButton.dataset.pathKey || undefined,
|
|
1037
|
+
}, true, true);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const actionButton = event.target?.closest("[data-editor-action]");
|
|
1041
|
+
if (!actionButton) {
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
if (actionButton.dataset.editorAction === "add-event-pose") {
|
|
1045
|
+
const eventId = actionButton.dataset.editorEventId ||
|
|
1046
|
+
(selection?.kind === "event" || selection?.kind === "event-pose" ? selection.id ?? "" : "");
|
|
1047
|
+
if (!eventId) {
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
const nextDocument = addEventPose(atlasDocument, eventId);
|
|
1051
|
+
const createdEvent = nextDocument.events.find((entry) => entry.id === eventId);
|
|
1052
|
+
const createdPose = createdEvent?.positions.at(-1) ?? createdEvent?.positions[0];
|
|
1053
|
+
replaceAtlasDocument(nextDocument, true, createdPose
|
|
1054
|
+
? { kind: "event-pose", id: eventId, key: createdPose.objectId }
|
|
1055
|
+
: { kind: "event", id: eventId });
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
920
1059
|
function handleInspectorInput() {
|
|
921
1060
|
applyInspectorState(false);
|
|
922
1061
|
}
|
|
@@ -940,6 +1079,12 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
940
1079
|
case "viewpoint":
|
|
941
1080
|
replaceAtlasDocument(buildViewpointDocumentFromInspector(selection.id ?? ""), commitHistory, selection, false);
|
|
942
1081
|
return;
|
|
1082
|
+
case "event":
|
|
1083
|
+
replaceAtlasDocument(buildEventDocumentFromInspector(selection.id ?? ""), commitHistory, selection, false);
|
|
1084
|
+
return;
|
|
1085
|
+
case "event-pose":
|
|
1086
|
+
replaceAtlasDocument(buildEventPoseDocumentFromInspector(selection.id ?? "", selection.key ?? ""), commitHistory, selection, false);
|
|
1087
|
+
return;
|
|
943
1088
|
case "annotation":
|
|
944
1089
|
replaceAtlasDocument(buildAnnotationDocumentFromInspector(selection.id ?? ""), commitHistory, selection, false);
|
|
945
1090
|
return;
|
|
@@ -1010,6 +1155,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
1010
1155
|
kind,
|
|
1011
1156
|
objectId,
|
|
1012
1157
|
pointerId: event.pointerId,
|
|
1158
|
+
path: selection ? { ...selection } : { kind: "object", id: objectId },
|
|
1013
1159
|
startedFrom: createHistoryEntry(),
|
|
1014
1160
|
changed: false,
|
|
1015
1161
|
orbitRadiusContext: kind === "orbit-radius" && details
|
|
@@ -1022,8 +1168,8 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
1022
1168
|
function handleWindowPointerMove(event) {
|
|
1023
1169
|
if (!dragState ||
|
|
1024
1170
|
dragState.pointerId !== event.pointerId ||
|
|
1025
|
-
selection
|
|
1026
|
-
selection
|
|
1171
|
+
!selection ||
|
|
1172
|
+
selectionKey(selection) !== selectionKey(dragState.path)) {
|
|
1027
1173
|
return;
|
|
1028
1174
|
}
|
|
1029
1175
|
const details = viewer.getObjectDetails(dragState.objectId);
|
|
@@ -1035,27 +1181,27 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
1035
1181
|
switch (dragState.kind) {
|
|
1036
1182
|
case "orbit-phase":
|
|
1037
1183
|
if (details.object.placement?.mode === "orbit" && details.orbit) {
|
|
1038
|
-
nextDocument = updateOrbitPhase(atlasDocument, dragState.objectId, details, pointer);
|
|
1184
|
+
nextDocument = updateOrbitPhase(atlasDocument, dragState.path, dragState.objectId, details, pointer);
|
|
1039
1185
|
}
|
|
1040
1186
|
break;
|
|
1041
1187
|
case "orbit-radius":
|
|
1042
1188
|
if (details.object.placement?.mode === "orbit" && details.orbit) {
|
|
1043
|
-
nextDocument = updateOrbitRadius(atlasDocument, dragState.objectId, details, pointer, dragState.orbitRadiusContext ?? null);
|
|
1189
|
+
nextDocument = updateOrbitRadius(atlasDocument, dragState.path, dragState.objectId, details, pointer, dragState.orbitRadiusContext ?? null);
|
|
1044
1190
|
}
|
|
1045
1191
|
break;
|
|
1046
1192
|
case "at-reference":
|
|
1047
1193
|
if (details.object.placement?.mode === "at") {
|
|
1048
|
-
nextDocument = updateAtReference(atlasDocument, dragState.objectId, viewer.getScene(), pointer);
|
|
1194
|
+
nextDocument = updateAtReference(atlasDocument, dragState.path, dragState.objectId, viewer.getScene(), pointer);
|
|
1049
1195
|
}
|
|
1050
1196
|
break;
|
|
1051
1197
|
case "surface-target":
|
|
1052
1198
|
if (details.object.placement?.mode === "surface") {
|
|
1053
|
-
nextDocument = updateSurfaceTarget(atlasDocument, dragState.objectId, viewer.getScene(), pointer);
|
|
1199
|
+
nextDocument = updateSurfaceTarget(atlasDocument, dragState.path, dragState.objectId, viewer.getScene(), pointer);
|
|
1054
1200
|
}
|
|
1055
1201
|
break;
|
|
1056
1202
|
case "free-distance":
|
|
1057
1203
|
if (details.object.placement?.mode === "free") {
|
|
1058
|
-
nextDocument = updateFreeDistance(atlasDocument, dragState.objectId, viewer.getScene(), details, pointer);
|
|
1204
|
+
nextDocument = updateFreeDistance(atlasDocument, dragState.path, dragState.objectId, viewer.getScene(), details, pointer);
|
|
1059
1205
|
}
|
|
1060
1206
|
break;
|
|
1061
1207
|
}
|
|
@@ -1087,7 +1233,7 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
1087
1233
|
}
|
|
1088
1234
|
history.push(dragState.startedFrom);
|
|
1089
1235
|
future.length = 0;
|
|
1090
|
-
canonicalSource =
|
|
1236
|
+
canonicalSource = formatAtlasSource(atlasDocument);
|
|
1091
1237
|
sourceText = canonicalSource;
|
|
1092
1238
|
dragState = null;
|
|
1093
1239
|
renderAll();
|
|
@@ -1194,10 +1340,12 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
1194
1340
|
guides: readCheckbox(form, "layer-guides"),
|
|
1195
1341
|
"orbits-back": readCheckbox(form, "layer-orbits-back"),
|
|
1196
1342
|
"orbits-front": readCheckbox(form, "layer-orbits-front"),
|
|
1343
|
+
events: readCheckbox(form, "layer-events"),
|
|
1197
1344
|
objects: readCheckbox(form, "layer-objects"),
|
|
1198
1345
|
labels: readCheckbox(form, "layer-labels"),
|
|
1199
1346
|
metadata: readCheckbox(form, "layer-metadata"),
|
|
1200
1347
|
},
|
|
1348
|
+
events: splitTokens(readOptionalTextInput(form, "viewpoint-events")),
|
|
1201
1349
|
filter: {
|
|
1202
1350
|
query: readOptionalTextInput(form, "filter-query"),
|
|
1203
1351
|
objectTypes: parseObjectTypes(readOptionalTextInput(form, "filter-object-types")),
|
|
@@ -1214,6 +1362,73 @@ export function createWorldOrbitEditor(container, options = {}) {
|
|
|
1214
1362
|
}
|
|
1215
1363
|
return nextDocument;
|
|
1216
1364
|
}
|
|
1365
|
+
function buildEventDocumentFromInspector(currentId) {
|
|
1366
|
+
const nextDocument = cloneAtlasDocument(atlasDocument);
|
|
1367
|
+
const form = inspector?.querySelector("form[data-editor-form='event']");
|
|
1368
|
+
const current = nextDocument.events.find((entry) => entry.id === currentId);
|
|
1369
|
+
if (!form || !current) {
|
|
1370
|
+
return nextDocument;
|
|
1371
|
+
}
|
|
1372
|
+
const nextId = readTextInput(form, "event-id") || current.id;
|
|
1373
|
+
const replacement = {
|
|
1374
|
+
...current,
|
|
1375
|
+
id: nextId,
|
|
1376
|
+
kind: readTextInput(form, "event-kind"),
|
|
1377
|
+
label: readTextInput(form, "event-label") || current.label,
|
|
1378
|
+
summary: readOptionalTextInput(form, "event-summary"),
|
|
1379
|
+
targetObjectId: readOptionalTextInput(form, "event-target"),
|
|
1380
|
+
participantObjectIds: splitTokens(readOptionalTextInput(form, "event-participants")),
|
|
1381
|
+
timing: readOptionalTextInput(form, "event-timing"),
|
|
1382
|
+
visibility: readOptionalTextInput(form, "event-visibility"),
|
|
1383
|
+
tags: splitTokens(readOptionalTextInput(form, "event-tags")),
|
|
1384
|
+
color: readOptionalTextInput(form, "event-color"),
|
|
1385
|
+
hidden: readCheckbox(form, "event-hidden"),
|
|
1386
|
+
};
|
|
1387
|
+
nextDocument.events = nextDocument.events
|
|
1388
|
+
.filter((entry) => entry.id !== current.id)
|
|
1389
|
+
.concat(replacement)
|
|
1390
|
+
.sort(compareEvents);
|
|
1391
|
+
syncEventViewpointReferences(nextDocument, current.id, replacement.id, splitTokens(readOptionalTextInput(form, "event-viewpoints")));
|
|
1392
|
+
if (current.id !== replacement.id) {
|
|
1393
|
+
selection = { kind: "event", id: replacement.id };
|
|
1394
|
+
}
|
|
1395
|
+
return nextDocument;
|
|
1396
|
+
}
|
|
1397
|
+
function buildEventPoseDocumentFromInspector(eventId, objectId) {
|
|
1398
|
+
const nextDocument = cloneAtlasDocument(atlasDocument);
|
|
1399
|
+
const form = inspector?.querySelector("form[data-editor-form='event-pose']");
|
|
1400
|
+
const eventEntry = nextDocument.events.find((entry) => entry.id === eventId);
|
|
1401
|
+
const currentPose = eventEntry?.positions.find((entry) => entry.objectId === objectId);
|
|
1402
|
+
if (!form || !eventEntry || !currentPose) {
|
|
1403
|
+
return nextDocument;
|
|
1404
|
+
}
|
|
1405
|
+
const nextObjectId = readTextInput(form, "pose-object-id") || currentPose.objectId;
|
|
1406
|
+
const replacement = {
|
|
1407
|
+
objectId: nextObjectId,
|
|
1408
|
+
placement: buildPlacementFromPoseForm(form, currentPose),
|
|
1409
|
+
};
|
|
1410
|
+
const inner = parseOptionalUnit(readOptionalTextInput(form, "prop-inner"));
|
|
1411
|
+
const outer = parseOptionalUnit(readOptionalTextInput(form, "prop-outer"));
|
|
1412
|
+
if (inner) {
|
|
1413
|
+
replacement.inner = inner;
|
|
1414
|
+
}
|
|
1415
|
+
if (outer) {
|
|
1416
|
+
replacement.outer = outer;
|
|
1417
|
+
}
|
|
1418
|
+
eventEntry.positions = eventEntry.positions
|
|
1419
|
+
.filter((entry) => entry.objectId !== currentPose.objectId)
|
|
1420
|
+
.concat(replacement)
|
|
1421
|
+
.sort(compareEventPoses);
|
|
1422
|
+
if (eventEntry.targetObjectId !== replacement.objectId &&
|
|
1423
|
+
!eventEntry.participantObjectIds.includes(replacement.objectId)) {
|
|
1424
|
+
eventEntry.participantObjectIds.push(replacement.objectId);
|
|
1425
|
+
eventEntry.participantObjectIds.sort((left, right) => left.localeCompare(right));
|
|
1426
|
+
}
|
|
1427
|
+
if (currentPose.objectId !== replacement.objectId) {
|
|
1428
|
+
selection = { kind: "event-pose", id: eventId, key: replacement.objectId };
|
|
1429
|
+
}
|
|
1430
|
+
return nextDocument;
|
|
1431
|
+
}
|
|
1217
1432
|
function buildAnnotationDocumentFromInspector(currentId) {
|
|
1218
1433
|
const nextDocument = cloneAtlasDocument(atlasDocument);
|
|
1219
1434
|
const form = inspector?.querySelector("form[data-editor-form='annotation']");
|
|
@@ -1421,7 +1636,7 @@ function resolveInitialEditorState(options) {
|
|
|
1421
1636
|
const atlasDocument = cloneAtlasDocument(options.atlasDocument);
|
|
1422
1637
|
return {
|
|
1423
1638
|
atlasDocument,
|
|
1424
|
-
source:
|
|
1639
|
+
source: formatAtlasSource(atlasDocument),
|
|
1425
1640
|
diagnostics: collectDocumentDiagnostics(atlasDocument),
|
|
1426
1641
|
};
|
|
1427
1642
|
}
|
|
@@ -1431,7 +1646,7 @@ function resolveInitialEditorState(options) {
|
|
|
1431
1646
|
const atlasDocument = loaded.value.atlasDocument ?? upgradeDocumentToV2(loaded.value.document);
|
|
1432
1647
|
return {
|
|
1433
1648
|
atlasDocument,
|
|
1434
|
-
source:
|
|
1649
|
+
source: formatAtlasSource(atlasDocument),
|
|
1435
1650
|
diagnostics: mergeDiagnostics(resolveAtlasDiagnostics(atlasDocument, loaded.diagnostics), collectDocumentDiagnostics(atlasDocument)),
|
|
1436
1651
|
};
|
|
1437
1652
|
}
|
|
@@ -1439,10 +1654,13 @@ function resolveInitialEditorState(options) {
|
|
|
1439
1654
|
const atlasDocument = createEmptyAtlasDocument("WorldOrbit");
|
|
1440
1655
|
return {
|
|
1441
1656
|
atlasDocument,
|
|
1442
|
-
source:
|
|
1657
|
+
source: formatAtlasSource(atlasDocument),
|
|
1443
1658
|
diagnostics: collectDocumentDiagnostics(atlasDocument),
|
|
1444
1659
|
};
|
|
1445
1660
|
}
|
|
1661
|
+
function formatAtlasSource(document) {
|
|
1662
|
+
return formatDocument(document, { schema: document.version });
|
|
1663
|
+
}
|
|
1446
1664
|
function buildEditorMarkup() {
|
|
1447
1665
|
const previewOpen = shouldPreviewSectionBeOpenByDefault();
|
|
1448
1666
|
return `<section class="wo-editor-shell">
|
|
@@ -1563,6 +1781,17 @@ function renderOutlineButton(path, label, activeKey, diagnosticBuckets) {
|
|
|
1563
1781
|
: "";
|
|
1564
1782
|
return `<button type="button" class="wo-editor-outline-item${key === activeKey ? " is-active" : ""}" data-path-kind="${escapeHtml(path.kind)}"${path.id ? ` data-path-id="${escapeHtml(path.id)}"` : ""}${path.key ? ` data-path-key="${escapeHtml(path.key)}"` : ""}><span>${escapeHtml(label)}</span>${badge}</button>`;
|
|
1565
1783
|
}
|
|
1784
|
+
function renderEventOutlineItems(eventEntry, activeKey, diagnosticBuckets) {
|
|
1785
|
+
return `<div class="wo-editor-outline-group">
|
|
1786
|
+
${renderOutlineButton({ kind: "event", id: eventEntry.id }, eventEntry.label || eventEntry.id, activeKey, diagnosticBuckets)}
|
|
1787
|
+
${eventEntry.positions.length > 0
|
|
1788
|
+
? `<div class="wo-editor-outline-children">${[...eventEntry.positions]
|
|
1789
|
+
.sort(compareEventPoses)
|
|
1790
|
+
.map((pose) => renderOutlineButton({ kind: "event-pose", id: eventEntry.id, key: pose.objectId }, pose.objectId, activeKey, diagnosticBuckets))
|
|
1791
|
+
.join("")}</div>`
|
|
1792
|
+
: ""}
|
|
1793
|
+
</div>`;
|
|
1794
|
+
}
|
|
1566
1795
|
function renderSystemInspector(formState) {
|
|
1567
1796
|
return `<form class="wo-editor-form" data-editor-form="system">
|
|
1568
1797
|
<h2>System</h2>
|
|
@@ -1629,6 +1858,7 @@ function renderViewpointInspector(formState, id) {
|
|
|
1629
1858
|
${renderCheckboxField("Guides", "layer-guides", viewpoint.layers.guides !== false)}
|
|
1630
1859
|
${renderCheckboxField("Orbits back", "layer-orbits-back", viewpoint.layers["orbits-back"] !== false)}
|
|
1631
1860
|
${renderCheckboxField("Orbits front", "layer-orbits-front", viewpoint.layers["orbits-front"] !== false)}
|
|
1861
|
+
${renderCheckboxField("Events", "layer-events", viewpoint.layers.events !== false)}
|
|
1632
1862
|
${renderCheckboxField("Objects", "layer-objects", viewpoint.layers.objects !== false)}
|
|
1633
1863
|
${renderCheckboxField("Labels", "layer-labels", viewpoint.layers.labels !== false)}
|
|
1634
1864
|
${renderCheckboxField("Metadata", "layer-metadata", viewpoint.layers.metadata !== false)}
|
|
@@ -1636,7 +1866,83 @@ function renderViewpointInspector(formState, id) {
|
|
|
1636
1866
|
${renderInspectorSection("viewpoint", "filter", "Filter", `${renderTextField("Filter query", "filter-query", viewpoint.filter?.query ?? "")}
|
|
1637
1867
|
${renderTextField("Filter object types", "filter-object-types", viewpoint.filter?.objectTypes.join(" ") ?? "")}
|
|
1638
1868
|
${renderTextField("Filter tags", "filter-tags", viewpoint.filter?.tags.join(" ") ?? "")}
|
|
1639
|
-
${renderTextField("Filter groups", "filter-groups", viewpoint.filter?.groupIds.join(" ") ?? "")}
|
|
1869
|
+
${renderTextField("Filter groups", "filter-groups", viewpoint.filter?.groupIds.join(" ") ?? "")}
|
|
1870
|
+
${renderTextField("Events", "viewpoint-events", viewpoint.events.join(" "))}`)}
|
|
1871
|
+
</form>`;
|
|
1872
|
+
}
|
|
1873
|
+
function renderEventInspector(formState, id) {
|
|
1874
|
+
const eventEntry = formState.events.find((entry) => entry.id === id);
|
|
1875
|
+
if (!eventEntry) {
|
|
1876
|
+
return `<p class="wo-editor-empty">Event not found.</p>`;
|
|
1877
|
+
}
|
|
1878
|
+
const linkedViewpoints = formState.viewpoints
|
|
1879
|
+
.filter((viewpoint) => viewpoint.events.includes(eventEntry.id))
|
|
1880
|
+
.map((viewpoint) => viewpoint.id)
|
|
1881
|
+
.join(" ");
|
|
1882
|
+
return `<form class="wo-editor-form" data-editor-form="event">
|
|
1883
|
+
<h2>Event</h2>
|
|
1884
|
+
${renderInspectorSection("event", "basics", "Basics", `${renderTextField("ID", "event-id", eventEntry.id)}
|
|
1885
|
+
${renderTextField("Kind", "event-kind", eventEntry.kind)}
|
|
1886
|
+
${renderTextField("Label", "event-label", eventEntry.label)}
|
|
1887
|
+
${renderTextAreaField("Summary", "event-summary", eventEntry.summary ?? "")}
|
|
1888
|
+
${renderTextField("Target object", "event-target", eventEntry.targetObjectId ?? "")}
|
|
1889
|
+
${renderTextField("Participants", "event-participants", eventEntry.participantObjectIds.join(" "))}
|
|
1890
|
+
${renderTextField("Timing", "event-timing", eventEntry.timing ?? "")}
|
|
1891
|
+
${renderTextField("Visibility", "event-visibility", eventEntry.visibility ?? "")}
|
|
1892
|
+
${renderTextField("Tags", "event-tags", eventEntry.tags.join(" "))}
|
|
1893
|
+
${renderTextField("Color", "event-color", eventEntry.color ?? "")}
|
|
1894
|
+
${renderCheckboxField("Hidden", "event-hidden", eventEntry.hidden === true)}`, true)}
|
|
1895
|
+
${renderInspectorSection("event", "viewpoints", "Viewpoints", `${renderTextField("Viewpoints", "event-viewpoints", linkedViewpoints)}`)}
|
|
1896
|
+
${renderInspectorSection("event", "positions", "Positions", `${eventEntry.positions.length > 0
|
|
1897
|
+
? `<div class="wo-editor-inline-list">${eventEntry.positions
|
|
1898
|
+
.map((pose) => renderOutlineButton({ kind: "event-pose", id: eventEntry.id, key: pose.objectId }, pose.objectId, null, new Map()))
|
|
1899
|
+
.join("")}</div>`
|
|
1900
|
+
: `<p class="wo-editor-empty">No event poses yet.</p>`}
|
|
1901
|
+
<div class="wo-editor-inline-actions">
|
|
1902
|
+
<button type="button" data-editor-action="add-event-pose" data-editor-event-id="${escapeHtml(eventEntry.id)}">Add pose</button>
|
|
1903
|
+
</div>`)}
|
|
1904
|
+
</form>`;
|
|
1905
|
+
}
|
|
1906
|
+
function renderEventPoseInspector(formState, eventId, objectId) {
|
|
1907
|
+
const eventEntry = formState.events.find((entry) => entry.id === eventId);
|
|
1908
|
+
const pose = eventEntry?.positions.find((entry) => entry.objectId === objectId);
|
|
1909
|
+
if (!eventEntry || !pose) {
|
|
1910
|
+
return `<p class="wo-editor-empty">Event pose not found.</p>`;
|
|
1911
|
+
}
|
|
1912
|
+
const placementMode = pose.placement?.mode ?? "";
|
|
1913
|
+
const placementTarget = pose.placement?.mode === "orbit" || pose.placement?.mode === "surface" || pose.placement?.mode === "at"
|
|
1914
|
+
? pose.placement.target
|
|
1915
|
+
: "";
|
|
1916
|
+
const freeValue = pose.placement?.mode === "free"
|
|
1917
|
+
? pose.placement.distance
|
|
1918
|
+
? formatUnitValue(pose.placement.distance)
|
|
1919
|
+
: pose.placement.descriptor ?? ""
|
|
1920
|
+
: "";
|
|
1921
|
+
return `<form class="wo-editor-form" data-editor-form="event-pose">
|
|
1922
|
+
<h2>Event Pose</h2>
|
|
1923
|
+
<p class="wo-editor-inline-note">Event <strong>${escapeHtml(eventEntry.label || eventEntry.id)}</strong></p>
|
|
1924
|
+
${renderInspectorSection("event-pose", "identity", "Identity", `${renderTextField("Object", "pose-object-id", pose.objectId)}
|
|
1925
|
+
<div class="wo-editor-inline-actions">
|
|
1926
|
+
<button type="button" data-path-kind="event" data-path-id="${escapeHtml(eventEntry.id)}">Select event</button>
|
|
1927
|
+
</div>`, true)}
|
|
1928
|
+
${renderInspectorSection("event-pose", "placement", "Placement", `${renderSelectField("Placement mode", "placement-mode", [
|
|
1929
|
+
["", "None"],
|
|
1930
|
+
["orbit", "Orbit"],
|
|
1931
|
+
["at", "At"],
|
|
1932
|
+
["surface", "Surface"],
|
|
1933
|
+
["free", "Free"],
|
|
1934
|
+
], placementMode)}
|
|
1935
|
+
${renderTextField("Placement target", "placement-target", placementTarget)}
|
|
1936
|
+
${renderTextField("Free value", "placement-free", freeValue)}
|
|
1937
|
+
${renderTextField("Distance", "placement-distance", pose.placement?.mode === "orbit" && pose.placement.distance ? formatUnitValue(pose.placement.distance) : "")}
|
|
1938
|
+
${renderTextField("Semi-major", "placement-semiMajor", pose.placement?.mode === "orbit" && pose.placement.semiMajor ? formatUnitValue(pose.placement.semiMajor) : "")}
|
|
1939
|
+
${renderTextField("Eccentricity", "placement-eccentricity", pose.placement?.mode === "orbit" && pose.placement.eccentricity !== undefined ? String(pose.placement.eccentricity) : "")}
|
|
1940
|
+
${renderTextField("Period", "placement-period", pose.placement?.mode === "orbit" && pose.placement.period ? formatUnitValue(pose.placement.period) : "")}
|
|
1941
|
+
${renderTextField("Angle", "placement-angle", pose.placement?.mode === "orbit" && pose.placement.angle ? formatUnitValue(pose.placement.angle) : "")}
|
|
1942
|
+
${renderTextField("Inclination", "placement-inclination", pose.placement?.mode === "orbit" && pose.placement.inclination ? formatUnitValue(pose.placement.inclination) : "")}
|
|
1943
|
+
${renderTextField("Phase", "placement-phase", pose.placement?.mode === "orbit" && pose.placement.phase ? formatUnitValue(pose.placement.phase) : "")}
|
|
1944
|
+
${renderTextField("Inner", "prop-inner", pose.inner ? formatUnitValue(pose.inner) : "")}
|
|
1945
|
+
${renderTextField("Outer", "prop-outer", pose.outer ? formatUnitValue(pose.outer) : "")}`, true)}
|
|
1640
1946
|
</form>`;
|
|
1641
1947
|
}
|
|
1642
1948
|
function renderAnnotationInspector(formState, id) {
|
|
@@ -1749,6 +2055,12 @@ function readCheckbox(form, name) {
|
|
|
1749
2055
|
return form.elements.namedItem(name)?.checked ?? false;
|
|
1750
2056
|
}
|
|
1751
2057
|
function buildPlacementFromForm(form, current) {
|
|
2058
|
+
return buildPlacementFromValues(form, current.placement, current.id);
|
|
2059
|
+
}
|
|
2060
|
+
function buildPlacementFromPoseForm(form, current) {
|
|
2061
|
+
return buildPlacementFromValues(form, current.placement, current.objectId);
|
|
2062
|
+
}
|
|
2063
|
+
function buildPlacementFromValues(form, currentPlacement, fallbackTarget) {
|
|
1752
2064
|
const mode = readTextInput(form, "placement-mode");
|
|
1753
2065
|
const target = readOptionalTextInput(form, "placement-target");
|
|
1754
2066
|
switch (mode) {
|
|
@@ -1756,9 +2068,9 @@ function buildPlacementFromForm(form, current) {
|
|
|
1756
2068
|
return {
|
|
1757
2069
|
mode,
|
|
1758
2070
|
target: target ??
|
|
1759
|
-
(
|
|
1760
|
-
?
|
|
1761
|
-
:
|
|
2071
|
+
(currentPlacement?.mode === "orbit"
|
|
2072
|
+
? currentPlacement.target
|
|
2073
|
+
: fallbackTarget),
|
|
1762
2074
|
distance: parseOptionalUnit(readOptionalTextInput(form, "placement-distance")),
|
|
1763
2075
|
semiMajor: parseOptionalUnit(readOptionalTextInput(form, "placement-semiMajor")),
|
|
1764
2076
|
eccentricity: parseNullableNumber(readOptionalTextInput(form, "placement-eccentricity")) ?? undefined,
|
|
@@ -1770,13 +2082,13 @@ function buildPlacementFromForm(form, current) {
|
|
|
1770
2082
|
case "at":
|
|
1771
2083
|
return {
|
|
1772
2084
|
mode,
|
|
1773
|
-
target: target ??
|
|
1774
|
-
reference: parseAtReferenceString(target ??
|
|
2085
|
+
target: target ?? fallbackTarget,
|
|
2086
|
+
reference: parseAtReferenceString(target ?? fallbackTarget),
|
|
1775
2087
|
};
|
|
1776
2088
|
case "surface":
|
|
1777
2089
|
return {
|
|
1778
2090
|
mode,
|
|
1779
|
-
target: target ??
|
|
2091
|
+
target: target ?? fallbackTarget,
|
|
1780
2092
|
};
|
|
1781
2093
|
case "free": {
|
|
1782
2094
|
const freeValue = readOptionalTextInput(form, "placement-free");
|
|
@@ -1920,9 +2232,48 @@ function renameObjectReferences(document, fromId, toId) {
|
|
|
1920
2232
|
annotation.sourceObjectId = toId;
|
|
1921
2233
|
}
|
|
1922
2234
|
}
|
|
2235
|
+
for (const eventEntry of document.events) {
|
|
2236
|
+
if (eventEntry.targetObjectId === fromId) {
|
|
2237
|
+
eventEntry.targetObjectId = toId;
|
|
2238
|
+
}
|
|
2239
|
+
eventEntry.participantObjectIds = eventEntry.participantObjectIds.map((entry) => entry === fromId ? toId : entry);
|
|
2240
|
+
for (const pose of eventEntry.positions) {
|
|
2241
|
+
if (pose.objectId === fromId) {
|
|
2242
|
+
pose.objectId = toId;
|
|
2243
|
+
}
|
|
2244
|
+
if (pose.placement?.mode === "orbit" && pose.placement.target === fromId) {
|
|
2245
|
+
pose.placement.target = toId;
|
|
2246
|
+
}
|
|
2247
|
+
if (pose.placement?.mode === "surface" && pose.placement.target === fromId) {
|
|
2248
|
+
pose.placement.target = toId;
|
|
2249
|
+
}
|
|
2250
|
+
if (pose.placement?.mode === "at") {
|
|
2251
|
+
const reference = pose.placement.reference;
|
|
2252
|
+
if (reference.kind === "anchor" && reference.objectId === fromId) {
|
|
2253
|
+
reference.objectId = toId;
|
|
2254
|
+
}
|
|
2255
|
+
if (reference.kind === "lagrange") {
|
|
2256
|
+
if (reference.primary === fromId) {
|
|
2257
|
+
reference.primary = toId;
|
|
2258
|
+
}
|
|
2259
|
+
if (reference.secondary === fromId) {
|
|
2260
|
+
reference.secondary = toId;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
pose.placement.target = formatAtReference(reference);
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
eventEntry.positions.sort(compareEventPoses);
|
|
2267
|
+
}
|
|
1923
2268
|
}
|
|
1924
2269
|
function removeSelectedNode(document, selection) {
|
|
1925
2270
|
const next = removeAtlasDocumentNode(document, selection);
|
|
2271
|
+
if (selection.kind === "event" && selection.id) {
|
|
2272
|
+
for (const viewpoint of next.system?.viewpoints ?? []) {
|
|
2273
|
+
viewpoint.events = viewpoint.events.filter((eventId) => eventId !== selection.id);
|
|
2274
|
+
}
|
|
2275
|
+
return next;
|
|
2276
|
+
}
|
|
1926
2277
|
if (selection.kind !== "object" || !selection.id) {
|
|
1927
2278
|
return next;
|
|
1928
2279
|
}
|
|
@@ -1959,9 +2310,47 @@ function removeSelectedNode(document, selection) {
|
|
|
1959
2310
|
annotation.sourceObjectId = null;
|
|
1960
2311
|
}
|
|
1961
2312
|
}
|
|
2313
|
+
for (const eventEntry of next.events) {
|
|
2314
|
+
if (eventEntry.targetObjectId === selection.id) {
|
|
2315
|
+
eventEntry.targetObjectId = null;
|
|
2316
|
+
}
|
|
2317
|
+
eventEntry.participantObjectIds = eventEntry.participantObjectIds.filter((entry) => entry !== selection.id);
|
|
2318
|
+
eventEntry.positions = eventEntry.positions.filter((entry) => entry.objectId !== selection.id);
|
|
2319
|
+
for (const pose of eventEntry.positions) {
|
|
2320
|
+
if (pose.placement?.mode === "orbit" && pose.placement.target === selection.id) {
|
|
2321
|
+
pose.placement = null;
|
|
2322
|
+
}
|
|
2323
|
+
if (pose.placement?.mode === "surface" && pose.placement.target === selection.id) {
|
|
2324
|
+
pose.placement = null;
|
|
2325
|
+
}
|
|
2326
|
+
if (pose.placement?.mode === "at") {
|
|
2327
|
+
const reference = pose.placement.reference;
|
|
2328
|
+
const touchesSelection = (reference.kind === "anchor" && reference.objectId === selection.id) ||
|
|
2329
|
+
(reference.kind === "lagrange" &&
|
|
2330
|
+
(reference.primary === selection.id || reference.secondary === selection.id));
|
|
2331
|
+
if (touchesSelection) {
|
|
2332
|
+
pose.placement = null;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
1962
2337
|
return next;
|
|
1963
2338
|
}
|
|
1964
|
-
function
|
|
2339
|
+
function findEditablePlacementOwner(document, path, objectId) {
|
|
2340
|
+
if (path.kind === "event-pose" && path.id && path.key) {
|
|
2341
|
+
const pose = findEventPose(document, path.id, path.key);
|
|
2342
|
+
if (pose?.placement) {
|
|
2343
|
+
return { placement: pose.placement };
|
|
2344
|
+
}
|
|
2345
|
+
return null;
|
|
2346
|
+
}
|
|
2347
|
+
const object = findObject(document, objectId);
|
|
2348
|
+
if (object?.placement) {
|
|
2349
|
+
return { placement: object.placement };
|
|
2350
|
+
}
|
|
2351
|
+
return null;
|
|
2352
|
+
}
|
|
2353
|
+
function updateOrbitPhase(document, path, objectId, details, pointer) {
|
|
1965
2354
|
const orbit = details.orbit;
|
|
1966
2355
|
if (!orbit || details.object.placement?.mode !== "orbit") {
|
|
1967
2356
|
return document;
|
|
@@ -1972,17 +2361,17 @@ function updateOrbitPhase(document, objectId, details, pointer) {
|
|
|
1972
2361
|
const radians = Math.atan2((unrotated.y - orbit.cy) / Math.max(ry, 1), (unrotated.x - orbit.cx) / Math.max(rx, 1));
|
|
1973
2362
|
const phaseDeg = normalizeDegrees((radians * 180) / Math.PI);
|
|
1974
2363
|
const next = cloneAtlasDocument(document);
|
|
1975
|
-
const
|
|
1976
|
-
if (!
|
|
2364
|
+
const placementOwner = findEditablePlacementOwner(next, path, objectId);
|
|
2365
|
+
if (!placementOwner || placementOwner.placement.mode !== "orbit") {
|
|
1977
2366
|
return document;
|
|
1978
2367
|
}
|
|
1979
|
-
|
|
2368
|
+
placementOwner.placement.phase = {
|
|
1980
2369
|
value: roundNumber(phaseDeg, 2),
|
|
1981
2370
|
unit: "deg",
|
|
1982
2371
|
};
|
|
1983
2372
|
return next;
|
|
1984
2373
|
}
|
|
1985
|
-
function updateOrbitRadius(document, objectId, details, pointer, dragContext) {
|
|
2374
|
+
function updateOrbitRadius(document, path, objectId, details, pointer, dragContext) {
|
|
1986
2375
|
const orbit = details.orbit;
|
|
1987
2376
|
if (!orbit || details.object.placement?.mode !== "orbit" || !dragContext) {
|
|
1988
2377
|
return document;
|
|
@@ -1992,49 +2381,49 @@ function updateOrbitRadius(document, objectId, details, pointer, dragContext) {
|
|
|
1992
2381
|
const nextBaseRadius = Math.max(nextDisplayedRadius - dragContext.radiusOffsetPx, dragContext.innerPx);
|
|
1993
2382
|
const nextMetric = orbitRadiusPxToMetric(nextBaseRadius, dragContext.innerPx, dragContext.stepPx);
|
|
1994
2383
|
const next = cloneAtlasDocument(document);
|
|
1995
|
-
const
|
|
1996
|
-
if (!
|
|
2384
|
+
const placementOwner = findEditablePlacementOwner(next, path, objectId);
|
|
2385
|
+
if (!placementOwner || placementOwner.placement.mode !== "orbit") {
|
|
1997
2386
|
return document;
|
|
1998
2387
|
}
|
|
1999
|
-
const currentValue =
|
|
2000
|
-
|
|
2388
|
+
const currentValue = placementOwner.placement.semiMajor ??
|
|
2389
|
+
placementOwner.placement.distance ?? {
|
|
2001
2390
|
value: 1,
|
|
2002
2391
|
unit: "au",
|
|
2003
2392
|
};
|
|
2004
2393
|
const scaled = distanceMetricToUnitValue(Math.max(nextMetric, 0), dragContext.preferredUnit ?? currentValue.unit);
|
|
2005
|
-
if (
|
|
2006
|
-
|
|
2394
|
+
if (placementOwner.placement.semiMajor) {
|
|
2395
|
+
placementOwner.placement.semiMajor = scaled;
|
|
2007
2396
|
}
|
|
2008
2397
|
else {
|
|
2009
|
-
|
|
2398
|
+
placementOwner.placement.distance = scaled;
|
|
2010
2399
|
}
|
|
2011
2400
|
return next;
|
|
2012
2401
|
}
|
|
2013
|
-
function updateAtReference(document, objectId, scene, pointer) {
|
|
2402
|
+
function updateAtReference(document, path, objectId, scene, pointer) {
|
|
2014
2403
|
const candidate = findNearestAtCandidate(scene, objectId, pointer);
|
|
2015
2404
|
if (!candidate) {
|
|
2016
2405
|
return document;
|
|
2017
2406
|
}
|
|
2018
2407
|
const next = cloneAtlasDocument(document);
|
|
2019
|
-
const
|
|
2020
|
-
if (!
|
|
2408
|
+
const placementOwner = findEditablePlacementOwner(next, path, objectId);
|
|
2409
|
+
if (!placementOwner || placementOwner.placement.mode !== "at") {
|
|
2021
2410
|
return document;
|
|
2022
2411
|
}
|
|
2023
|
-
|
|
2024
|
-
|
|
2412
|
+
placementOwner.placement.reference = candidate.reference;
|
|
2413
|
+
placementOwner.placement.target = formatAtReference(candidate.reference);
|
|
2025
2414
|
return next;
|
|
2026
2415
|
}
|
|
2027
|
-
function updateSurfaceTarget(document, objectId, scene, pointer) {
|
|
2416
|
+
function updateSurfaceTarget(document, path, objectId, scene, pointer) {
|
|
2028
2417
|
const target = findNearestSceneObject(scene, objectId, pointer, (entry) => SURFACE_TARGET_TYPES.has(entry.object.type));
|
|
2029
2418
|
if (!target) {
|
|
2030
2419
|
return document;
|
|
2031
2420
|
}
|
|
2032
2421
|
const next = cloneAtlasDocument(document);
|
|
2033
|
-
const
|
|
2034
|
-
if (!
|
|
2422
|
+
const placementOwner = findEditablePlacementOwner(next, path, objectId);
|
|
2423
|
+
if (!placementOwner || placementOwner.placement.mode !== "surface") {
|
|
2035
2424
|
return document;
|
|
2036
2425
|
}
|
|
2037
|
-
|
|
2426
|
+
placementOwner.placement.target = target.objectId;
|
|
2038
2427
|
return next;
|
|
2039
2428
|
}
|
|
2040
2429
|
function createOrbitRadiusDragContext(document, scene, details) {
|
|
@@ -2042,8 +2431,9 @@ function createOrbitRadiusDragContext(document, scene, details) {
|
|
|
2042
2431
|
return null;
|
|
2043
2432
|
}
|
|
2044
2433
|
const targetId = details.object.placement.target;
|
|
2045
|
-
const siblingCount =
|
|
2046
|
-
entry.placement.target === targetId
|
|
2434
|
+
const siblingCount = scene.objects.filter((entry) => entry.object.placement?.mode === "orbit" &&
|
|
2435
|
+
entry.object.placement.target === targetId &&
|
|
2436
|
+
!entry.hidden).length;
|
|
2047
2437
|
const spacingFactor = layoutPresetSpacingForScene(scene.layoutPreset);
|
|
2048
2438
|
const stepPx = (siblingCount > 2 ? 54 : 64) * spacingFactor * scene.scaleModel.orbitDistanceMultiplier;
|
|
2049
2439
|
const innerPx = details.parent.radius +
|
|
@@ -2059,28 +2449,28 @@ function createOrbitRadiusDragContext(document, scene, details) {
|
|
|
2059
2449
|
preferredUnit: currentValue?.unit ?? null,
|
|
2060
2450
|
};
|
|
2061
2451
|
}
|
|
2062
|
-
function updateFreeDistance(document, objectId, scene, details, pointer) {
|
|
2452
|
+
function updateFreeDistance(document, path, objectId, scene, details, pointer) {
|
|
2063
2453
|
if (details.object.placement?.mode !== "free") {
|
|
2064
2454
|
return document;
|
|
2065
2455
|
}
|
|
2066
2456
|
const railX = scene.width - scene.padding - 140;
|
|
2067
2457
|
const offsetPx = Math.max(0, railX - pointer.x);
|
|
2068
2458
|
const next = cloneAtlasDocument(document);
|
|
2069
|
-
const
|
|
2070
|
-
if (!
|
|
2459
|
+
const placementOwner = findEditablePlacementOwner(next, path, objectId);
|
|
2460
|
+
if (!placementOwner || placementOwner.placement.mode !== "free") {
|
|
2071
2461
|
return document;
|
|
2072
2462
|
}
|
|
2073
|
-
const preferredUnit = normalizeFreeDistanceUnit(
|
|
2463
|
+
const preferredUnit = normalizeFreeDistanceUnit(placementOwner.placement.distance?.unit ?? null);
|
|
2074
2464
|
const metric = offsetPx / Math.max(FREE_DISTANCE_PIXEL_FACTOR * scene.scaleModel.freePlacementMultiplier, 1);
|
|
2075
2465
|
if (metric < 0.01) {
|
|
2076
|
-
|
|
2077
|
-
if (!
|
|
2078
|
-
delete
|
|
2466
|
+
placementOwner.placement.distance = undefined;
|
|
2467
|
+
if (!placementOwner.placement.descriptor) {
|
|
2468
|
+
delete placementOwner.placement.descriptor;
|
|
2079
2469
|
}
|
|
2080
2470
|
return next;
|
|
2081
2471
|
}
|
|
2082
|
-
|
|
2083
|
-
delete
|
|
2472
|
+
placementOwner.placement.distance = distanceMetricToUnitValue(metric, preferredUnit);
|
|
2473
|
+
delete placementOwner.placement.descriptor;
|
|
2084
2474
|
return next;
|
|
2085
2475
|
}
|
|
2086
2476
|
function findNearestSceneObject(scene, selectedObjectId, pointer, predicate = () => true) {
|
|
@@ -2356,9 +2746,60 @@ function mapDiagnosticFieldToInputNames(selection, field) {
|
|
|
2356
2746
|
return ["viewpoint-zoom"];
|
|
2357
2747
|
case "rotationDeg":
|
|
2358
2748
|
return ["viewpoint-rotation"];
|
|
2749
|
+
case "events":
|
|
2750
|
+
return ["viewpoint-events"];
|
|
2359
2751
|
default:
|
|
2360
2752
|
return [];
|
|
2361
2753
|
}
|
|
2754
|
+
case "event":
|
|
2755
|
+
switch (field) {
|
|
2756
|
+
case "id":
|
|
2757
|
+
return ["event-id"];
|
|
2758
|
+
case "kind":
|
|
2759
|
+
return ["event-kind"];
|
|
2760
|
+
case "label":
|
|
2761
|
+
return ["event-label"];
|
|
2762
|
+
case "summary":
|
|
2763
|
+
return ["event-summary"];
|
|
2764
|
+
case "targetObjectId":
|
|
2765
|
+
case "target":
|
|
2766
|
+
return ["event-target"];
|
|
2767
|
+
case "participantObjectIds":
|
|
2768
|
+
case "participants":
|
|
2769
|
+
return ["event-participants"];
|
|
2770
|
+
case "timing":
|
|
2771
|
+
return ["event-timing"];
|
|
2772
|
+
case "visibility":
|
|
2773
|
+
return ["event-visibility"];
|
|
2774
|
+
case "tags":
|
|
2775
|
+
return ["event-tags"];
|
|
2776
|
+
case "color":
|
|
2777
|
+
return ["event-color"];
|
|
2778
|
+
case "hidden":
|
|
2779
|
+
return ["event-hidden"];
|
|
2780
|
+
default:
|
|
2781
|
+
return [];
|
|
2782
|
+
}
|
|
2783
|
+
case "event-pose":
|
|
2784
|
+
if (field === "objectId") {
|
|
2785
|
+
return ["pose-object-id"];
|
|
2786
|
+
}
|
|
2787
|
+
if (field === "placement") {
|
|
2788
|
+
return ["placement-mode"];
|
|
2789
|
+
}
|
|
2790
|
+
if (field === "reference" || field === "target") {
|
|
2791
|
+
return ["placement-target"];
|
|
2792
|
+
}
|
|
2793
|
+
if (field === "descriptor") {
|
|
2794
|
+
return ["placement-free"];
|
|
2795
|
+
}
|
|
2796
|
+
if (PLACEMENT_DIAGNOSTIC_FIELDS.has(field)) {
|
|
2797
|
+
return [`placement-${field}`];
|
|
2798
|
+
}
|
|
2799
|
+
if (field === "inner" || field === "outer") {
|
|
2800
|
+
return [`prop-${field}`];
|
|
2801
|
+
}
|
|
2802
|
+
return [];
|
|
2362
2803
|
case "annotation":
|
|
2363
2804
|
switch (field) {
|
|
2364
2805
|
case "id":
|
|
@@ -2444,6 +2885,10 @@ function describePath(path) {
|
|
|
2444
2885
|
return `Metadata: ${path.key ?? ""}`;
|
|
2445
2886
|
case "group":
|
|
2446
2887
|
return `Group: ${path.id ?? ""}`;
|
|
2888
|
+
case "event":
|
|
2889
|
+
return `Event: ${path.id ?? ""}`;
|
|
2890
|
+
case "event-pose":
|
|
2891
|
+
return `Event Pose: ${path.id ?? ""} / ${path.key ?? ""}`;
|
|
2447
2892
|
case "object":
|
|
2448
2893
|
return `Object: ${path.id ?? ""}`;
|
|
2449
2894
|
case "viewpoint":
|
|
@@ -2455,11 +2900,72 @@ function describePath(path) {
|
|
|
2455
2900
|
}
|
|
2456
2901
|
}
|
|
2457
2902
|
function selectionKey(path) {
|
|
2458
|
-
return path ? `${path.kind}:${path.id ?? path.key ?? ""}` : null;
|
|
2903
|
+
return path ? `${path.kind}:${path.id ?? ""}:${path.key ?? ""}` : null;
|
|
2904
|
+
}
|
|
2905
|
+
function selectionEventId(path) {
|
|
2906
|
+
if (!path) {
|
|
2907
|
+
return null;
|
|
2908
|
+
}
|
|
2909
|
+
return path.kind === "event" || path.kind === "event-pose" ? path.id ?? null : null;
|
|
2459
2910
|
}
|
|
2460
2911
|
function compareObjects(left, right) {
|
|
2461
2912
|
return left.id.localeCompare(right.id);
|
|
2462
2913
|
}
|
|
2914
|
+
function compareEvents(left, right) {
|
|
2915
|
+
return left.id.localeCompare(right.id);
|
|
2916
|
+
}
|
|
2917
|
+
function compareEventPoses(left, right) {
|
|
2918
|
+
return left.objectId.localeCompare(right.objectId);
|
|
2919
|
+
}
|
|
2920
|
+
function findEvent(document, eventId) {
|
|
2921
|
+
return document.events.find((entry) => entry.id === eventId) ?? null;
|
|
2922
|
+
}
|
|
2923
|
+
function findEventPose(document, eventId, objectId) {
|
|
2924
|
+
return findEvent(document, eventId)?.positions.find((entry) => entry.objectId === objectId) ?? null;
|
|
2925
|
+
}
|
|
2926
|
+
function findObject(document, objectId) {
|
|
2927
|
+
return document.objects.find((entry) => entry.id === objectId) ?? null;
|
|
2928
|
+
}
|
|
2929
|
+
function addEventPose(document, eventId) {
|
|
2930
|
+
const next = cloneAtlasDocument(document);
|
|
2931
|
+
const eventEntry = next.events.find((entry) => entry.id === eventId);
|
|
2932
|
+
if (!eventEntry) {
|
|
2933
|
+
return document;
|
|
2934
|
+
}
|
|
2935
|
+
const baseObject = next.objects.find((object) => !eventEntry.positions.some((pose) => pose.objectId === object.id)) ??
|
|
2936
|
+
next.objects[0];
|
|
2937
|
+
if (!baseObject) {
|
|
2938
|
+
return document;
|
|
2939
|
+
}
|
|
2940
|
+
if (eventEntry.targetObjectId !== baseObject.id &&
|
|
2941
|
+
!eventEntry.participantObjectIds.includes(baseObject.id)) {
|
|
2942
|
+
eventEntry.participantObjectIds.push(baseObject.id);
|
|
2943
|
+
eventEntry.participantObjectIds.sort((left, right) => left.localeCompare(right));
|
|
2944
|
+
}
|
|
2945
|
+
eventEntry.positions.push(createEventPoseFromObject(baseObject));
|
|
2946
|
+
eventEntry.positions.sort(compareEventPoses);
|
|
2947
|
+
return next;
|
|
2948
|
+
}
|
|
2949
|
+
function createEventPoseFromObject(object) {
|
|
2950
|
+
return {
|
|
2951
|
+
objectId: object.id,
|
|
2952
|
+
placement: object.placement ? structuredClone(object.placement) : null,
|
|
2953
|
+
inner: readUnitValue(object.properties.inner),
|
|
2954
|
+
outer: readUnitValue(object.properties.outer),
|
|
2955
|
+
};
|
|
2956
|
+
}
|
|
2957
|
+
function syncEventViewpointReferences(document, previousEventId, nextEventId, viewpointIds) {
|
|
2958
|
+
const desired = new Set(viewpointIds);
|
|
2959
|
+
for (const viewpoint of document.system?.viewpoints ?? []) {
|
|
2960
|
+
const currentIds = new Set(viewpoint.events);
|
|
2961
|
+
currentIds.delete(previousEventId);
|
|
2962
|
+
currentIds.delete(nextEventId);
|
|
2963
|
+
if (desired.has(viewpoint.id)) {
|
|
2964
|
+
currentIds.add(nextEventId);
|
|
2965
|
+
}
|
|
2966
|
+
viewpoint.events = [...currentIds].sort((left, right) => left.localeCompare(right));
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2463
2969
|
function createUniqueId(prefix, existing) {
|
|
2464
2970
|
const safePrefix = prefix.trim() || "item";
|
|
2465
2971
|
let counter = 1;
|
|
@@ -2494,6 +3000,9 @@ function readUnitProperty(value) {
|
|
|
2494
3000
|
? formatUnitValue(value)
|
|
2495
3001
|
: "";
|
|
2496
3002
|
}
|
|
3003
|
+
function readUnitValue(value) {
|
|
3004
|
+
return value && typeof value === "object" && "value" in value ? value : undefined;
|
|
3005
|
+
}
|
|
2497
3006
|
function readNumberProperty(value) {
|
|
2498
3007
|
return typeof value === "number" ? String(value) : "";
|
|
2499
3008
|
}
|
|
@@ -2803,6 +3312,12 @@ function installEditorStyles() {
|
|
|
2803
3312
|
.wo-editor-overlay-diagnostic-warning { border: 1px solid rgba(240, 180, 100, 0.24); }
|
|
2804
3313
|
.wo-editor-outline { display: grid; gap: 14px; }
|
|
2805
3314
|
.wo-editor-outline-section { display: grid; gap: 8px; }
|
|
3315
|
+
.wo-editor-outline-group { display: grid; gap: 6px; }
|
|
3316
|
+
.wo-editor-outline-children {
|
|
3317
|
+
display: grid;
|
|
3318
|
+
gap: 6px;
|
|
3319
|
+
padding-left: 16px;
|
|
3320
|
+
}
|
|
2806
3321
|
.wo-editor-outline-section h3 {
|
|
2807
3322
|
margin: 0;
|
|
2808
3323
|
color: rgba(237,246,255,0.68);
|
|
@@ -2842,6 +3357,27 @@ function installEditorStyles() {
|
|
|
2842
3357
|
background: rgba(255, 120, 120, 0.18);
|
|
2843
3358
|
color: #ffb2b2;
|
|
2844
3359
|
}
|
|
3360
|
+
.wo-editor-inline-list { display: grid; gap: 8px; }
|
|
3361
|
+
.wo-editor-inline-actions {
|
|
3362
|
+
display: flex;
|
|
3363
|
+
flex-wrap: wrap;
|
|
3364
|
+
gap: 10px;
|
|
3365
|
+
margin-top: 12px;
|
|
3366
|
+
}
|
|
3367
|
+
.wo-editor-inline-actions button {
|
|
3368
|
+
border: 1px solid rgba(255,255,255,0.14);
|
|
3369
|
+
border-radius: 999px;
|
|
3370
|
+
background: rgba(255,255,255,0.06);
|
|
3371
|
+
color: #edf6ff;
|
|
3372
|
+
cursor: pointer;
|
|
3373
|
+
font: 600 12px/1.2 "Segoe UI Variable", "Segoe UI", sans-serif;
|
|
3374
|
+
padding: 8px 12px;
|
|
3375
|
+
}
|
|
3376
|
+
.wo-editor-inline-note {
|
|
3377
|
+
margin: 0 0 12px;
|
|
3378
|
+
color: rgba(237,246,255,0.72);
|
|
3379
|
+
font: 500 12px/1.5 "Segoe UI Variable", "Segoe UI", sans-serif;
|
|
3380
|
+
}
|
|
2845
3381
|
.wo-editor-diagnostics { display: grid; gap: 10px; }
|
|
2846
3382
|
.wo-editor-diagnostic {
|
|
2847
3383
|
display: grid;
|