babylon.quarks-editor 0.26.0 → 0.27.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.
Files changed (151) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +38 -5
  3. package/dist/core/assetDragSession.d.ts +1 -1
  4. package/dist/core/assetTree.d.ts +1 -1
  5. package/dist/core/binding.d.ts +2 -2
  6. package/dist/core/binding.js +1 -1
  7. package/dist/core/defaultEffect.d.ts +2 -2
  8. package/dist/core/defaultEffect.js +3 -3
  9. package/dist/core/editorScene.d.ts +1 -1
  10. package/dist/core/effectTree.d.ts +1 -1
  11. package/dist/core/effectTree.js +13 -88
  12. package/dist/core/emitterShapeWireframe.d.ts +2 -2
  13. package/dist/core/emitterShapeWireframe.js +45 -13
  14. package/dist/core/galleryDropMarker.d.ts +2 -2
  15. package/dist/core/galleryDropMarker.js +6 -6
  16. package/dist/core/galleryLayout.d.ts +3 -3
  17. package/dist/core/galleryLayout.js +1 -1
  18. package/dist/core/galleryPlayback.d.ts +1 -1
  19. package/dist/core/galleryScene.d.ts +4 -4
  20. package/dist/core/galleryScene.js +3 -3
  21. package/dist/core/gallerySelectionMarker.d.ts +4 -4
  22. package/dist/core/gallerySelectionMarker.js +5 -5
  23. package/dist/core/groups.d.ts +1 -1
  24. package/dist/core/groups.js +1 -1
  25. package/dist/core/history.d.ts +1 -1
  26. package/dist/core/indexedDbProjectStore.d.ts +1 -1
  27. package/dist/core/lineShaders.d.ts +2 -2
  28. package/dist/core/lineShaders.js +2 -2
  29. package/dist/core/loadEffect.d.ts +2 -2
  30. package/dist/core/loadEffect.js +1 -1
  31. package/dist/core/pickAssetFiles.js +1 -1
  32. package/dist/core/sceneEntries.d.ts +4 -4
  33. package/dist/core/sceneEntries.js +2 -2
  34. package/dist/core/scrub.d.ts +1 -1
  35. package/dist/core/shapes.d.ts +1 -1
  36. package/dist/core/systems.d.ts +2 -2
  37. package/dist/core/textureExport.d.ts +1 -1
  38. package/dist/core/timeline.d.ts +2 -2
  39. package/dist/core/transport.d.ts +3 -3
  40. package/dist/core/transport.js +2 -2
  41. package/dist/core/viewportPicker.d.ts +4 -4
  42. package/dist/core/viewportPicker.js +8 -7
  43. package/dist/index.d.ts +21 -21
  44. package/dist/index.js +12 -12
  45. package/dist/react/AssetsPanel.d.ts +2 -2
  46. package/dist/react/AssetsPanel.js +6 -6
  47. package/dist/react/ChoiceDialog.js +1 -1
  48. package/dist/react/ContextMenu.js +1 -1
  49. package/dist/react/CurveEditor.d.ts +1 -1
  50. package/dist/react/CurveEditor.js +2 -2
  51. package/dist/react/EditorWorkspace.d.ts +2 -2
  52. package/dist/react/EditorWorkspace.js +4 -4
  53. package/dist/react/EffectEditor.d.ts +3 -3
  54. package/dist/react/EffectEditor.js +7 -7
  55. package/dist/react/EffectEditorHost.d.ts +6 -6
  56. package/dist/react/EffectEditorHost.js +59 -62
  57. package/dist/react/EffectTreeView.d.ts +1 -1
  58. package/dist/react/EffectTreeView.js +3 -3
  59. package/dist/react/GradientEditor.d.ts +1 -1
  60. package/dist/react/GradientEditor.js +3 -3
  61. package/dist/react/InspectorInfoIcon.js +3 -3
  62. package/dist/react/InspectorLabel.js +3 -3
  63. package/dist/react/MessageDialog.js +1 -1
  64. package/dist/react/ModuleSection.js +4 -4
  65. package/dist/react/ObjectModule.d.ts +1 -1
  66. package/dist/react/ObjectModule.js +4 -4
  67. package/dist/react/ProjectsPage.d.ts +1 -1
  68. package/dist/react/ProjectsPage.js +6 -6
  69. package/dist/react/PromptDialog.js +1 -1
  70. package/dist/react/ScenePanel.d.ts +2 -2
  71. package/dist/react/ScenePanel.js +5 -5
  72. package/dist/react/SubEmittersModule.d.ts +1 -1
  73. package/dist/react/SubEmittersModule.js +5 -5
  74. package/dist/react/TextureSelect.d.ts +24 -0
  75. package/dist/react/TextureSelect.js +74 -0
  76. package/dist/react/TextureUVField.d.ts +14 -0
  77. package/dist/react/TextureUVField.js +23 -0
  78. package/dist/react/TimelinePanel.d.ts +7 -7
  79. package/dist/react/TimelinePanel.js +11 -9
  80. package/dist/react/TimelineTrackRow.d.ts +3 -3
  81. package/dist/react/TimelineTrackRow.js +6 -6
  82. package/dist/react/TransportBar.d.ts +4 -4
  83. package/dist/react/TransportBar.js +3 -3
  84. package/dist/react/ValueField.d.ts +1 -1
  85. package/dist/react/ValueField.js +3 -3
  86. package/dist/react/behaviorModules.d.ts +4 -2
  87. package/dist/react/behaviorModules.js +52 -16
  88. package/dist/react/fields.d.ts +1 -0
  89. package/dist/react/fields.js +3 -3
  90. package/dist/react/index.d.ts +40 -38
  91. package/dist/react/index.js +25 -23
  92. package/dist/react/inspectorHints.js +31 -0
  93. package/dist/react/modules.d.ts +3 -1
  94. package/dist/react/modules.js +99 -100
  95. package/package.json +3 -2
  96. package/src/core/assetDragSession.ts +1 -1
  97. package/src/core/assetTree.ts +1 -1
  98. package/src/core/binding.ts +3 -3
  99. package/src/core/defaultEffect.ts +4 -4
  100. package/src/core/editorScene.ts +1 -1
  101. package/src/core/effectTree.ts +13 -101
  102. package/src/core/emitterShapeWireframe.ts +48 -16
  103. package/src/core/galleryDropMarker.ts +8 -8
  104. package/src/core/galleryLayout.ts +4 -4
  105. package/src/core/galleryPlayback.ts +1 -1
  106. package/src/core/galleryScene.ts +5 -5
  107. package/src/core/gallerySelectionMarker.ts +7 -7
  108. package/src/core/groups.ts +1 -1
  109. package/src/core/history.ts +1 -1
  110. package/src/core/indexedDbProjectStore.ts +1 -1
  111. package/src/core/lineShaders.ts +2 -2
  112. package/src/core/loadEffect.ts +3 -3
  113. package/src/core/pickAssetFiles.ts +2 -2
  114. package/src/core/sceneEntries.ts +5 -5
  115. package/src/core/scrub.ts +1 -1
  116. package/src/core/shapes.ts +1 -1
  117. package/src/core/systems.ts +2 -2
  118. package/src/core/textureExport.ts +1 -1
  119. package/src/core/timeline.ts +3 -3
  120. package/src/core/transport.ts +4 -4
  121. package/src/core/viewportPicker.ts +12 -11
  122. package/src/index.ts +21 -21
  123. package/src/react/AssetsPanel.tsx +6 -6
  124. package/src/react/ChoiceDialog.tsx +1 -1
  125. package/src/react/ContextMenu.tsx +1 -1
  126. package/src/react/CurveEditor.tsx +2 -2
  127. package/src/react/EditorWorkspace.tsx +5 -5
  128. package/src/react/EffectEditor.tsx +10 -8
  129. package/src/react/EffectEditorHost.tsx +64 -69
  130. package/src/react/EffectTreeView.tsx +4 -4
  131. package/src/react/GradientEditor.tsx +3 -3
  132. package/src/react/InspectorInfoIcon.tsx +3 -3
  133. package/src/react/InspectorLabel.tsx +3 -3
  134. package/src/react/MessageDialog.tsx +1 -1
  135. package/src/react/ModuleSection.tsx +4 -4
  136. package/src/react/ObjectModule.tsx +5 -5
  137. package/src/react/ProjectsPage.tsx +6 -6
  138. package/src/react/PromptDialog.tsx +1 -1
  139. package/src/react/ScenePanel.tsx +7 -7
  140. package/src/react/SubEmittersModule.tsx +6 -6
  141. package/src/react/TextureSelect.tsx +118 -0
  142. package/src/react/TextureUVField.tsx +62 -0
  143. package/src/react/TimelinePanel.tsx +17 -15
  144. package/src/react/TimelineTrackRow.tsx +9 -9
  145. package/src/react/TransportBar.tsx +7 -7
  146. package/src/react/ValueField.tsx +3 -3
  147. package/src/react/behaviorModules.tsx +211 -28
  148. package/src/react/fields.tsx +10 -3
  149. package/src/react/index.ts +41 -38
  150. package/src/react/inspectorHints.ts +40 -0
  151. package/src/react/modules.tsx +375 -131
@@ -1,8 +1,7 @@
1
- import {Matrix, Quaternion} from '@babylonjs/core/Maths/math.vector';
2
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
1
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
3
2
  import type {ParticleSystem} from 'babylon.quarks';
4
- import {ParticleEmitter} from 'babylon.quarks';
5
- import {isEditorSceneNode} from './editorScene';
3
+ import {ParticleEmitter, serializeEffect} from 'babylon.quarks';
4
+ import {isEditorSceneNode} from './editorScene.js';
6
5
 
7
6
  /** A node in the editor's effect hierarchy: a Group (organizational) or a ParticleEmitter. */
8
7
  export interface EffectTreeNode {
@@ -45,40 +44,14 @@ export function collectSystems(tree: EffectTreeNode): ParticleSystem[] {
45
44
  return out;
46
45
  }
47
46
 
48
- interface SerializeMeta {
49
- textures: {[k: string]: {url?: string; name?: string; invertY?: boolean}};
50
- materials: {[k: string]: {[key: string]: unknown}};
51
- geometries: {[k: string]: unknown};
52
- }
53
-
54
- function localMatrix(node: TransformNode): number[] {
55
- const rotation = node.rotationQuaternion ?? Quaternion.FromEulerVector(node.rotation);
56
- return Matrix.Compose(node.scaling, rotation, node.position).asArray().slice();
57
- }
58
-
59
- function serializeNode(tree: EffectTreeNode, meta: SerializeMeta, fallbackUuid: string): Record<string, unknown> {
60
- // Emitter nodes must serialize with the ParticleEmitter's own `.uuid` — that is exactly
61
- // what EmitSubParticleSystem.toJSON writes as its target reference, so the two match and
62
- // QuarksLoader.linkReferences can re-resolve sub-emitters on reimport.
63
- const uuid = tree.system
64
- ? (tree.node as unknown as {uuid: string}).uuid
65
- : ((tree.node as {_quarksUUID?: string})._quarksUUID ?? fallbackUuid);
66
- const children = tree.children.map((child, i) => serializeNode(child, meta, `${fallbackUuid}-${i}`));
67
- const base = {
68
- uuid,
69
- name: tree.name,
70
- layers: 1,
71
- matrix: localMatrix(tree.node),
72
- children: children.length > 0 ? children : undefined,
73
- };
74
- if (tree.system) {
75
- return {
76
- ...base,
77
- type: 'ParticleEmitter',
78
- ps: tree.system.toJSON(meta as never, {useUrlForImage: true} as never),
79
- };
80
- }
81
- return {...base, type: 'Group'};
47
+ /** Writes the effect under `roots`, leaving out the editor's own helper nodes. */
48
+ function serialize(roots: TransformNode | TransformNode[], name: string): string {
49
+ const json = serializeEffect(roots, {
50
+ name,
51
+ generator: 'babylon.quarks-editor',
52
+ filter: (node) => !isEditorSceneNode(node),
53
+ });
54
+ return JSON.stringify(json, null, 2);
82
55
  }
83
56
 
84
57
  /**
@@ -86,9 +59,7 @@ function serializeNode(tree: EffectTreeNode, meta: SerializeMeta, fallbackUuid:
86
59
  * preserving the group/emitter hierarchy (not flattened).
87
60
  */
88
61
  export function serializeEffectTree(root: TransformNode, name = 'effect'): string {
89
- const meta: SerializeMeta = {textures: {}, materials: {}, geometries: {}};
90
- const object = serializeNode(buildEffectTree(root), meta, `${name}-node`);
91
- return finishEnvelope(object, meta);
62
+ return serialize(root, name);
92
63
  }
93
64
 
94
65
  /**
@@ -96,64 +67,5 @@ export function serializeEffectTree(root: TransformNode, name = 'effect'): strin
96
67
  * spans multiple top-level trees (e.g. a demo running several loaded effects at once).
97
68
  */
98
69
  export function serializeEffectForest(roots: TransformNode[], name = 'effect'): string {
99
- if (roots.length === 1) {
100
- return serializeEffectTree(roots[0], name);
101
- }
102
- const meta: SerializeMeta = {textures: {}, materials: {}, geometries: {}};
103
- const children = roots.map((root, i) => serializeNode(buildEffectTree(root), meta, `${name}-node-${i}`));
104
- const object = {
105
- uuid: `${name}-root`,
106
- name,
107
- type: 'Group',
108
- layers: 1,
109
- matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
110
- children,
111
- };
112
- return finishEnvelope(object, meta);
113
- }
114
-
115
- /** Flattens accumulated meta and wraps the serialized object into the Quarks envelope. */
116
- function finishEnvelope(object: Record<string, unknown>, meta: SerializeMeta): string {
117
- // toJSON stashes live Texture instances and material records in meta; flatten them into
118
- // the serializable images/textures/materials arrays QuarksLoader expects.
119
- const images: Array<{uuid: string; url: string}> = [];
120
- const textures: Array<{uuid: string; image?: string; invertY?: boolean; noMipmap?: boolean}> = [];
121
- for (const [uuid, tex] of Object.entries(meta.textures)) {
122
- const url = tex?.url ?? tex?.name;
123
- const invertY = typeof tex?.invertY === 'boolean' ? tex.invertY : undefined;
124
- const isEnvAtlas =
125
- tex?.name === 'quarksEnvAtlas' || (typeof tex?.name === 'string' && tex.name.endsWith('_envAtlas'));
126
- if (url) {
127
- const imageUuid = `${uuid}-image`;
128
- images.push({uuid: imageUuid, url});
129
- const entry: {uuid: string; image?: string; invertY?: boolean; noMipmap?: boolean} = {
130
- uuid,
131
- image: imageUuid,
132
- };
133
- if (invertY === false || isEnvAtlas) {
134
- entry.invertY = false;
135
- entry.noMipmap = true;
136
- }
137
- textures.push(entry);
138
- } else {
139
- textures.push({uuid});
140
- }
141
- }
142
- const materials = Object.values(meta.materials).map((material) => {
143
- const {sourceMaterial: _sourceMaterial, ...serializable} = material;
144
- return serializable;
145
- });
146
-
147
- return JSON.stringify(
148
- {
149
- metadata: {version: 4.5, type: 'Object3D', generator: 'babylon.quarks-editor'},
150
- geometries: Object.values(meta.geometries),
151
- materials,
152
- textures,
153
- images,
154
- object,
155
- },
156
- null,
157
- 2
158
- );
70
+ return serialize(roots.length === 1 ? roots[0] : roots, name);
159
71
  }
@@ -1,14 +1,14 @@
1
- import {Color3} from '@babylonjs/core/Maths/math.color';
2
- import {Vector3} from '@babylonjs/core/Maths/math.vector';
3
- import {LinesMesh} from '@babylonjs/core/Meshes/linesMesh';
4
- import {Mesh} from '@babylonjs/core/Meshes/mesh';
5
- import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder';
6
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
7
- import type {Scene} from '@babylonjs/core/scene';
1
+ import {Color3} from '@babylonjs/core/Maths/math.color.js';
2
+ import {Vector3} from '@babylonjs/core/Maths/math.vector.js';
3
+ import {LinesMesh} from '@babylonjs/core/Meshes/linesMesh.js';
4
+ import {Mesh} from '@babylonjs/core/Meshes/mesh.js';
5
+ import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder.js';
6
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
7
+ import type {Scene} from '@babylonjs/core/scene.js';
8
8
  import type {ParticleSystem} from 'babylon.quarks';
9
- import {EDITOR_SCENE_NODE_METADATA} from './editorScene';
10
- import './lineShaders';
11
- import {getShapeType, readShapeParams} from './shapes';
9
+ import {EDITOR_SCENE_NODE_METADATA} from './editorScene.js';
10
+ import './lineShaders.js';
11
+ import {getShapeType, readShapeParams} from './shapes.js';
12
12
 
13
13
  const LINE_COLOR = new Color3(0.45, 0.82, 1);
14
14
  const DIRECTION_COLOR = new Color3(0.75, 0.92, 1);
@@ -225,6 +225,38 @@ function buildEmitterWireframe(system: ParticleSystem, scene: Scene): Mesh[] {
225
225
  }
226
226
  break;
227
227
  }
228
+ case 'box': {
229
+ // Spawn anywhere in the box (volume), on its faces (shell) or along its edges;
230
+ // velocity straight +Z, like Unity's Box shape.
231
+ const halfW = params.width / 2;
232
+ const halfH = params.height / 2;
233
+ const halfD = params.depth / 2;
234
+ lines('wf-box-front', xyRect(params.width, params.height, halfD));
235
+ lines('wf-box-back', xyRect(params.width, params.height, -halfD));
236
+ const corners: Array<[number, number]> = [
237
+ [-halfW, -halfH],
238
+ [halfW, -halfH],
239
+ [halfW, halfH],
240
+ [-halfW, halfH],
241
+ ];
242
+ corners.forEach(([x, y], i) => {
243
+ lines(`wf-box-side-${i}`, [new Vector3(x, y, -halfD), new Vector3(x, y, halfD)]);
244
+ });
245
+ const ray = Math.max(halfD * 2, 0.25);
246
+ lines('wf-box-dir', [new Vector3(0, 0, halfD), new Vector3(0, 0, halfD + ray)], DIRECTION_COLOR);
247
+ break;
248
+ }
249
+ case 'edge': {
250
+ // Spawn on a line along X of half-length `radius`; velocity straight +Y.
251
+ const radius = Math.max(params.radius, 0.001);
252
+ lines('wf-edge', [new Vector3(-radius, 0, 0), new Vector3(radius, 0, 0)]);
253
+ const ray = Math.max(radius * 0.5, 0.25);
254
+ for (let i = 0; i <= 4; i++) {
255
+ const x = -radius + (i / 4) * radius * 2;
256
+ lines(`wf-edge-ray-${i}`, [new Vector3(x, 0, 0), new Vector3(x, ray, 0)], DIRECTION_COLOR);
257
+ }
258
+ break;
259
+ }
228
260
  case 'mesh_surface': {
229
261
  const source = params.mesh;
230
262
  if (source && !source.isDisposed()) {
@@ -351,15 +383,15 @@ function arcPoint(radius: number, theta: number, z = 0): Vector3 {
351
383
  return new Vector3(Math.cos(theta) * radius, Math.sin(theta) * radius, z);
352
384
  }
353
385
 
354
- function xyRect(width: number, height: number): Vector3[] {
386
+ function xyRect(width: number, height: number, z = 0): Vector3[] {
355
387
  const halfW = width / 2;
356
388
  const halfH = height / 2;
357
389
  return [
358
- new Vector3(-halfW, -halfH, 0),
359
- new Vector3(halfW, -halfH, 0),
360
- new Vector3(halfW, halfH, 0),
361
- new Vector3(-halfW, halfH, 0),
362
- new Vector3(-halfW, -halfH, 0),
390
+ new Vector3(-halfW, -halfH, z),
391
+ new Vector3(halfW, -halfH, z),
392
+ new Vector3(halfW, halfH, z),
393
+ new Vector3(-halfW, halfH, z),
394
+ new Vector3(-halfW, -halfH, z),
363
395
  ];
364
396
  }
365
397
 
@@ -1,11 +1,11 @@
1
- import {StandardMaterial} from '@babylonjs/core/Materials/standardMaterial';
2
- import {Color3} from '@babylonjs/core/Maths/math.color';
3
- import {Vector3} from '@babylonjs/core/Maths/math.vector';
4
- import {Mesh} from '@babylonjs/core/Meshes/mesh';
5
- import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder';
6
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
7
- import type {Scene} from '@babylonjs/core/scene';
8
- import {DEFAULT_GALLERY_SPACING, gallerySpan} from './galleryLayout';
1
+ import {StandardMaterial} from '@babylonjs/core/Materials/standardMaterial.js';
2
+ import {Color3} from '@babylonjs/core/Maths/math.color.js';
3
+ import {Vector3} from '@babylonjs/core/Maths/math.vector.js';
4
+ import {Mesh} from '@babylonjs/core/Meshes/mesh.js';
5
+ import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder.js';
6
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
7
+ import type {Scene} from '@babylonjs/core/scene.js';
8
+ import {DEFAULT_GALLERY_SPACING, gallerySpan} from './galleryLayout.js';
9
9
 
10
10
  /** Ground decal shown while dragging gallery items over the viewport. */
11
11
  export class GalleryDropMarker {
@@ -1,7 +1,7 @@
1
- import type {ArcRotateCamera} from '@babylonjs/core/Cameras/arcRotateCamera';
2
- import {Vector3} from '@babylonjs/core/Maths/math.vector';
3
- import type {Mesh} from '@babylonjs/core/Meshes/mesh';
4
- import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
1
+ import type {ArcRotateCamera} from '@babylonjs/core/Cameras/arcRotateCamera.js';
2
+ import {Vector3} from '@babylonjs/core/Maths/math.vector.js';
3
+ import type {Mesh} from '@babylonjs/core/Meshes/mesh.js';
4
+ import type {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
5
5
 
6
6
  /** Default gap between neighbouring effects in a folder gallery import. */
7
7
  export const DEFAULT_GALLERY_SPACING = 4;
@@ -1,5 +1,5 @@
1
1
  import {QuarksUtil} from 'babylon.quarks';
2
- import type {SceneEntry} from './sceneEntries';
2
+ import type {SceneEntry} from './sceneEntries.js';
3
3
 
4
4
  function isPreviewEntry(entry: SceneEntry, previewRootIds: ReadonlySet<number>): boolean {
5
5
  return previewRootIds.has(entry.root.uniqueId);
@@ -1,9 +1,9 @@
1
- import '@babylonjs/core/Culling/ray';
2
- import {Vector3} from '@babylonjs/core/Maths/math.vector';
3
- import type {Scene} from '@babylonjs/core/scene';
1
+ import '@babylonjs/core/Culling/ray.js';
2
+ import {Vector3} from '@babylonjs/core/Maths/math.vector.js';
3
+ import type {Scene} from '@babylonjs/core/scene.js';
4
4
  import type {BatchedRenderer} from 'babylon.quarks';
5
- import {DEFAULT_GALLERY_SPACING, galleryGridPosition} from './galleryLayout';
6
- import type {SceneEntry} from './sceneEntries';
5
+ import {DEFAULT_GALLERY_SPACING, galleryGridPosition} from './galleryLayout.js';
6
+ import type {SceneEntry} from './sceneEntries.js';
7
7
 
8
8
  /** Clears live particles and freezes simulation for one scene entry. */
9
9
  export function clearSceneEntrySimulation(entry: SceneEntry, renderer: BatchedRenderer): void {
@@ -1,10 +1,10 @@
1
- import {Color3} from '@babylonjs/core/Maths/math.color';
2
- import {Vector3} from '@babylonjs/core/Maths/math.vector';
3
- import {LinesMesh} from '@babylonjs/core/Meshes/linesMesh';
4
- import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder';
5
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
6
- import type {Scene} from '@babylonjs/core/scene';
7
- import './lineShaders';
1
+ import {Color3} from '@babylonjs/core/Maths/math.color.js';
2
+ import {Vector3} from '@babylonjs/core/Maths/math.vector.js';
3
+ import {LinesMesh} from '@babylonjs/core/Meshes/linesMesh.js';
4
+ import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder.js';
5
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
6
+ import type {Scene} from '@babylonjs/core/scene.js';
7
+ import './lineShaders.js';
8
8
 
9
9
  export const GROUND_RING_RADIUS = 1.2;
10
10
  const RING_SEGMENTS = 64;
@@ -1,4 +1,4 @@
1
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
1
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
2
2
 
3
3
  /**
4
4
  * Groups the given nodes under a new organizational TransformNode. Each node's world
@@ -1,4 +1,4 @@
1
- import type {EffectBinding} from './binding';
1
+ import type {EffectBinding} from './binding.js';
2
2
 
3
3
  const COALESCE_MS = 400;
4
4
  const MAX_DEPTH = 50;
@@ -5,7 +5,7 @@ import type {
5
5
  StoredAsset,
6
6
  StoredScene,
7
7
  StoredSceneEntry,
8
- } from './projectStore';
8
+ } from './projectStore.js';
9
9
 
10
10
  const DB_NAME = 'babylon-quarks-editor';
11
11
  const DB_VERSION = 1;
@@ -3,5 +3,5 @@
3
3
  // Babylon never looks, it falls back to fetching `src/Shaders/color.fragment.fx`, gets the
4
4
  // page's HTML back and the selection rings never render. Registering the shader up front, from
5
5
  // the same module graph as the rest of the editor, makes the lookup succeed either way.
6
- import '@babylonjs/core/Shaders/color.fragment';
7
- import '@babylonjs/core/Shaders/color.vertex';
6
+ import '@babylonjs/core/Shaders/color.fragment.js';
7
+ import '@babylonjs/core/Shaders/color.vertex.js';
@@ -1,6 +1,6 @@
1
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
2
- import type {Node} from '@babylonjs/core/node';
3
- import type {Scene} from '@babylonjs/core/scene';
1
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
2
+ import type {Node} from '@babylonjs/core/node.js';
3
+ import type {Scene} from '@babylonjs/core/scene.js';
4
4
  import type {BatchedRenderer, ParticleSystem} from 'babylon.quarks';
5
5
  import {ParticleEmitter, QuarksLoader, QuarksUtil} from 'babylon.quarks';
6
6
 
@@ -1,4 +1,4 @@
1
- import {isAssetImportFile, relativePathOf} from './assets';
1
+ import {isAssetImportFile, relativePathOf} from './assets.js';
2
2
 
3
3
  interface DirectoryPickerWindow {
4
4
  showDirectoryPicker?: (options?: {mode?: 'read' | 'readwrite'}) => Promise<FileSystemDirectoryHandle>;
@@ -54,7 +54,7 @@ export async function pickAssetFolderFiles(): Promise<File[] | null | undefined>
54
54
  }
55
55
 
56
56
  try {
57
- const dir = (await picker.call(window, {mode: 'read'})) as DirectoryHandleWithEntries;
57
+ const dir = (await picker.call(window, {mode: 'read'})) as unknown as DirectoryHandleWithEntries;
58
58
  const files = await readAssetFilesFromDirectory(dir);
59
59
  return files.sort((a, b) => relativePathOf(a).localeCompare(relativePathOf(b)));
60
60
  } catch (err) {
@@ -1,10 +1,10 @@
1
- import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
2
- import type {Scene} from '@babylonjs/core/scene';
1
+ import type {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
2
+ import type {Scene} from '@babylonjs/core/scene.js';
3
3
  import type {BatchedRenderer, ParticleEmitter, ParticleSystem} from 'babylon.quarks';
4
4
  import {QuarksUtil} from 'babylon.quarks';
5
- import type {EffectAsset} from './assets';
6
- import {EffectBinding} from './binding';
7
- import {disposeLoadedEffect, parseEffectFromJson} from './loadEffect';
5
+ import type {EffectAsset} from './assets.js';
6
+ import {EffectBinding} from './binding.js';
7
+ import {disposeLoadedEffect, parseEffectFromJson} from './loadEffect.js';
8
8
 
9
9
  /**
10
10
  * One effect instance placed in the scene. Every instance is live (its systems are registered
package/src/core/scrub.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type {BatchedRenderer, ParticleSystem} from 'babylon.quarks';
2
- import type {EffectBinding} from './binding';
2
+ import type {EffectBinding} from './binding.js';
3
3
 
4
4
  const WIDE_STEP_THRESHOLD = 8;
5
5
 
@@ -1,4 +1,4 @@
1
- import type {Mesh} from '@babylonjs/core/Meshes/mesh';
1
+ import type {Mesh} from '@babylonjs/core/Meshes/mesh.js';
2
2
  import type {EmitterShape} from 'babylon.quarks';
3
3
  import {
4
4
  BoxEmitFrom,
@@ -1,4 +1,4 @@
1
- import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
1
+ import type {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
2
2
  import {
3
3
  ConeEmitter,
4
4
  ConstantColor,
@@ -8,7 +8,7 @@ import {
8
8
  RenderMode,
9
9
  Vector4,
10
10
  } from 'babylon.quarks';
11
- import type {EffectBinding} from './binding';
11
+ import type {EffectBinding} from './binding.js';
12
12
 
13
13
  export interface ChildSystemOptions {
14
14
  name?: string;
@@ -1,4 +1,4 @@
1
- import type {Texture} from '@babylonjs/core/Materials/Textures/texture';
1
+ import type {Texture} from '@babylonjs/core/Materials/Textures/texture.js';
2
2
 
3
3
  const bakedUrls = new WeakMap<Texture, string>();
4
4
 
@@ -1,8 +1,8 @@
1
- import type {TransformNode} from '@babylonjs/core/Meshes/transformNode';
1
+ import type {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
2
2
  import type {FunctionValueGenerator, ParticleSystem, ValueGenerator} from 'babylon.quarks';
3
3
  import {ConstantValue, IntervalValue} from 'babylon.quarks';
4
- import type {EffectBinding} from './binding';
5
- import type {EffectTreeNode} from './effectTree';
4
+ import type {EffectBinding} from './binding.js';
5
+ import type {EffectTreeNode} from './effectTree.js';
6
6
 
7
7
  /** Guards against a literally zero-width ruler (e.g. no tracks yet) — not a usability stretch. */
8
8
  const MIN_TIMELINE_SPAN = 0.01;
@@ -1,8 +1,8 @@
1
- import type {Scene} from '@babylonjs/core/scene';
1
+ import type {Scene} from '@babylonjs/core/scene.js';
2
2
  import type {BatchedRenderer} from 'babylon.quarks';
3
- import type {EffectBinding} from './binding';
4
- import {clearFocusFinished, isFocusFinished, setFocusElapsed, type PlaybackState} from './playback';
5
- import {playFocus, scrubFocusTo} from './scrub';
3
+ import type {EffectBinding} from './binding.js';
4
+ import {clearFocusFinished, isFocusFinished, setFocusElapsed, type PlaybackState} from './playback.js';
5
+ import {playFocus, scrubFocusTo} from './scrub.js';
6
6
 
7
7
  /**
8
8
  * Stage Play/Pause shared by the viewport transport bar and the timeline. Pressing Play on a
@@ -1,11 +1,11 @@
1
- import '@babylonjs/core/Culling/ray';
2
- import {Mesh} from '@babylonjs/core/Meshes/mesh';
3
- import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder';
4
- import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
5
- import type {Scene} from '@babylonjs/core/scene';
6
- import {EDITOR_SCENE_NODE_METADATA} from './editorScene';
7
- import {GROUND_RING_RADIUS, GalleryHoverMarker} from './gallerySelectionMarker';
8
- import type {SceneEntry} from './sceneEntries';
1
+ import '@babylonjs/core/Culling/ray.js';
2
+ import {Mesh} from '@babylonjs/core/Meshes/mesh.js';
3
+ import {MeshBuilder} from '@babylonjs/core/Meshes/meshBuilder.js';
4
+ import {TransformNode} from '@babylonjs/core/Meshes/transformNode.js';
5
+ import type {Scene} from '@babylonjs/core/scene.js';
6
+ import {EDITOR_SCENE_NODE_METADATA} from './editorScene.js';
7
+ import {GROUND_RING_RADIUS, GalleryHoverMarker} from './gallerySelectionMarker.js';
8
+ import type {SceneEntry} from './sceneEntries.js';
9
9
 
10
10
  interface PickMetadata {
11
11
  quarksEditorPick: true;
@@ -48,9 +48,10 @@ export class GalleryViewportPicker {
48
48
  } else if (soloRoot && !soloRoot.isDisposed()) {
49
49
  this.createHandle(soloRoot);
50
50
  }
51
- const hovered = this.hoveredRoot;
52
- this.hoveredRoot = null;
53
- this.setHover(hovered && !hovered.isDisposed() ? hovered : null);
51
+ if (this.hoveredRoot?.isDisposed()) {
52
+ this.hoveredRoot = null;
53
+ this.hoverMarker.hide();
54
+ }
54
55
  }
55
56
 
56
57
  /** Yellow focus-style ring under the hovered effect root. */
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
- export {EffectBinding} from './core/binding';
2
- export type {EditorListener} from './core/binding';
3
- export {GroundPlaneResolver, ensureGroundResolver} from './core/collision';
1
+ export {EffectBinding} from './core/binding.js';
2
+ export type {EditorListener} from './core/binding.js';
3
+ export {GroundPlaneResolver, ensureGroundResolver} from './core/collision.js';
4
4
  export {
5
5
  DEFAULT_GRADIENT_STOPS,
6
6
  buildColorOverLife,
@@ -11,16 +11,16 @@ export {
11
11
  rgbaToVector4,
12
12
  stopToCss,
13
13
  stopsToCssGradient,
14
- } from './core/colors';
15
- export type {GradientStop} from './core/colors';
16
- export {buildEffectTree, collectSystems, serializeEffectForest, serializeEffectTree} from './core/effectTree';
17
- export type {EffectTreeNode} from './core/effectTree';
18
- export {EffectHistory} from './core/history';
19
- export type {HistoryStacks} from './core/history';
20
- export {IndexedDbProjectStore} from './core/indexedDbProjectStore';
21
- export {applyRendererMaterial, getMaterialLabel} from './core/material';
22
- export type {RendererMaterialPatch} from './core/material';
23
- export {formatBytes} from './core/projectStore';
14
+ } from './core/colors.js';
15
+ export type {GradientStop} from './core/colors.js';
16
+ export {buildEffectTree, collectSystems, serializeEffectForest, serializeEffectTree} from './core/effectTree.js';
17
+ export type {EffectTreeNode} from './core/effectTree.js';
18
+ export {EffectHistory} from './core/history.js';
19
+ export type {HistoryStacks} from './core/history.js';
20
+ export {IndexedDbProjectStore} from './core/indexedDbProjectStore.js';
21
+ export {applyRendererMaterial, getMaterialLabel} from './core/material.js';
22
+ export type {RendererMaterialPatch} from './core/material.js';
23
+ export {formatBytes} from './core/projectStore.js';
24
24
  export type {
25
25
  ProjectMeta,
26
26
  ProjectStore,
@@ -28,7 +28,7 @@ export type {
28
28
  StoredAsset,
29
29
  StoredScene,
30
30
  StoredSceneEntry,
31
- } from './core/projectStore';
31
+ } from './core/projectStore.js';
32
32
  export {
33
33
  DEFAULT_SHAPE_PARAMS,
34
34
  SHAPE_PARAM_KEYS,
@@ -36,10 +36,10 @@ export {
36
36
  createShape,
37
37
  getShapeType,
38
38
  readShapeParams,
39
- } from './core/shapes';
40
- export type {ShapeParams, ShapeType} from './core/shapes';
41
- export {createChildSystem} from './core/systems';
42
- export type {ChildSystemOptions} from './core/systems';
43
- export {ensurePortableTextureUrl} from './core/textureExport';
44
- export {buildCurve, buildScalar, mergePieces, readPieces, readScalar, sampleCurve, splitPieces} from './core/values';
45
- export type {CurvePiece, ScalarGenerator, ScalarMode, ScalarValueState} from './core/values';
39
+ } from './core/shapes.js';
40
+ export type {ShapeParams, ShapeType} from './core/shapes.js';
41
+ export {createChildSystem} from './core/systems.js';
42
+ export type {ChildSystemOptions} from './core/systems.js';
43
+ export {ensurePortableTextureUrl} from './core/textureExport.js';
44
+ export {buildCurve, buildScalar, mergePieces, readPieces, readScalar, sampleCurve, splitPieces} from './core/values.js';
45
+ export type {CurvePiece, ScalarGenerator, ScalarMode, ScalarValueState} from './core/values.js';
@@ -7,7 +7,7 @@ import {
7
7
  SparklesIcon,
8
8
  } from '@heroicons/react/24/solid';
9
9
  import React, {useCallback, useMemo, useRef, useState} from 'react';
10
- import {setAssetDragSession} from '../core/assetDragSession';
10
+ import {setAssetDragSession} from '../core/assetDragSession.js';
11
11
  import {
12
12
  buildAssetTree,
13
13
  collectFolderAssets,
@@ -17,11 +17,11 @@ import {
17
17
  type AssetDragPayload,
18
18
  type AssetTreeFolder,
19
19
  type AssetTreeNode,
20
- } from '../core/assetTree';
21
- import {assetIconUrl, type AssetLoadProgress, type EffectAsset} from '../core/assets';
22
- import {ContextMenu, type ContextMenuItem} from './ContextMenu';
23
- import {iconStyle} from './icons';
24
- import {buttonStyle, hiddenFileInputStyle, inputStyle, theme} from './theme';
20
+ } from '../core/assetTree.js';
21
+ import {assetIconUrl, type AssetLoadProgress, type EffectAsset} from '../core/assets.js';
22
+ import {ContextMenu, type ContextMenuItem} from './ContextMenu.js';
23
+ import {iconStyle} from './icons.js';
24
+ import {buttonStyle, hiddenFileInputStyle, inputStyle, theme} from './theme.js';
25
25
 
26
26
  const TREE_WIDTH = 200;
27
27
  const TILE_WIDTH = 92;
@@ -1,5 +1,5 @@
1
1
  import React, {useEffect} from 'react';
2
- import {buttonStyle, theme} from './theme';
2
+ import {buttonStyle, theme} from './theme.js';
3
3
 
4
4
  export interface ChoiceDialogAction {
5
5
  label: string;
@@ -1,6 +1,6 @@
1
1
  import React, {useEffect, useLayoutEffect, useRef, useState} from 'react';
2
2
  import {createPortal} from 'react-dom';
3
- import {theme} from './theme';
3
+ import {theme} from './theme.js';
4
4
 
5
5
  export interface ContextMenuItem {
6
6
  label: string;
@@ -1,6 +1,6 @@
1
1
  import {useRef, useState} from 'react';
2
- import {CurvePiece, mergePieces, sampleCurve, splitPieces} from '../core/values';
3
- import {inputStyle, theme} from './theme';
2
+ import {CurvePiece, mergePieces, sampleCurve, splitPieces} from '../core/values.js';
3
+ import {inputStyle, theme} from './theme.js';
4
4
 
5
5
  const WIDTH = 218;
6
6
  const HEIGHT = 84;
@@ -1,10 +1,10 @@
1
1
  import React, {useEffect, useMemo, useState} from 'react';
2
2
  import {createRoot} from 'react-dom/client';
3
- import {IndexedDbProjectStore} from '../core/indexedDbProjectStore';
4
- import type {ProjectMeta, ProjectStore} from '../core/projectStore';
5
- import {EffectEditorHost, type EffectEditorHostProps} from './EffectEditorHost';
6
- import {ProjectsPage} from './ProjectsPage';
7
- import {theme} from './theme';
3
+ import {IndexedDbProjectStore} from '../core/indexedDbProjectStore.js';
4
+ import type {ProjectMeta, ProjectStore} from '../core/projectStore.js';
5
+ import {EffectEditorHost, type EffectEditorHostProps} from './EffectEditorHost.js';
6
+ import {ProjectsPage} from './ProjectsPage.js';
7
+ import {theme} from './theme.js';
8
8
 
9
9
  export interface EditorWorkspaceProps extends Omit<EffectEditorHostProps, 'project' | 'onExitProject' | 'effectJson'> {
10
10
  /** Where projects are kept; defaults to the browser's IndexedDB. */