@dreamerbird/camera-studio 0.2.0 → 0.3.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 (126) hide show
  1. package/dist/CameraScene.d.ts +1 -1
  2. package/dist/CameraScene.d.ts.map +1 -1
  3. package/dist/CameraScene.js +36 -6
  4. package/dist/PlacementManager.d.ts +15 -1
  5. package/dist/PlacementManager.d.ts.map +1 -1
  6. package/dist/PlacementManager.js +123 -14
  7. package/dist/agent/api.d.ts +5 -0
  8. package/dist/agent/api.d.ts.map +1 -1
  9. package/dist/agent/api.js +25 -0
  10. package/dist/agent/api.test.js +75 -1
  11. package/dist/agent/commands/assets.d.ts +5 -0
  12. package/dist/agent/commands/assets.d.ts.map +1 -1
  13. package/dist/agent/commands/assets.js +93 -21
  14. package/dist/agent/commands/assets.test.js +53 -0
  15. package/dist/agent/commands/materials.d.ts +18 -0
  16. package/dist/agent/commands/materials.d.ts.map +1 -0
  17. package/dist/agent/commands/materials.js +192 -0
  18. package/dist/agent/commands/materials.test.d.ts +2 -0
  19. package/dist/agent/commands/materials.test.d.ts.map +1 -0
  20. package/dist/agent/commands/materials.test.js +294 -0
  21. package/dist/agent/commands/modelRecovery.test.d.ts +2 -0
  22. package/dist/agent/commands/modelRecovery.test.d.ts.map +1 -0
  23. package/dist/agent/commands/modelRecovery.test.js +122 -0
  24. package/dist/agent/commands/models.d.ts +13 -0
  25. package/dist/agent/commands/models.d.ts.map +1 -1
  26. package/dist/agent/commands/models.js +98 -1
  27. package/dist/agent/commands/primitives.d.ts +27 -0
  28. package/dist/agent/commands/primitives.d.ts.map +1 -0
  29. package/dist/agent/commands/primitives.js +117 -0
  30. package/dist/agent/commands/primitives.test.d.ts +2 -0
  31. package/dist/agent/commands/primitives.test.d.ts.map +1 -0
  32. package/dist/agent/commands/primitives.test.js +135 -0
  33. package/dist/agent/expectedCommands.d.ts +1 -1
  34. package/dist/agent/expectedCommands.d.ts.map +1 -1
  35. package/dist/agent/expectedCommands.js +6 -0
  36. package/dist/agent/propAccess.d.ts +2 -0
  37. package/dist/agent/propAccess.d.ts.map +1 -1
  38. package/dist/agent/propAccess.js +21 -0
  39. package/dist/agent/registerCommands.d.ts.map +1 -1
  40. package/dist/agent/registerCommands.js +5 -1
  41. package/dist/agent/runtime/objectRuntime.d.ts +2 -0
  42. package/dist/agent/runtime/objectRuntime.d.ts.map +1 -1
  43. package/dist/agent/runtime/objectRuntime.js +15 -1
  44. package/dist/agent/runtime.d.ts +1 -0
  45. package/dist/agent/runtime.d.ts.map +1 -1
  46. package/dist/agent/runtime.js +2 -0
  47. package/dist/assetLibrary/dispose.d.ts +4 -2
  48. package/dist/assetLibrary/dispose.d.ts.map +1 -1
  49. package/dist/assetLibrary/dispose.js +7 -5
  50. package/dist/assetLibrary/gltfLoader.d.ts +2 -1
  51. package/dist/assetLibrary/gltfLoader.d.ts.map +1 -1
  52. package/dist/assetLibrary/gltfLoader.js +2 -2
  53. package/dist/assetLibrary/gltfResourceSet.d.ts +6 -0
  54. package/dist/assetLibrary/gltfResourceSet.d.ts.map +1 -0
  55. package/dist/assetLibrary/gltfResourceSet.js +27 -0
  56. package/dist/assetLibrary/gltfResourceSet.test.d.ts +2 -0
  57. package/dist/assetLibrary/gltfResourceSet.test.d.ts.map +1 -0
  58. package/dist/assetLibrary/gltfResourceSet.test.js +40 -0
  59. package/dist/assetLibrary/importModelFile.d.ts +5 -0
  60. package/dist/assetLibrary/importModelFile.d.ts.map +1 -1
  61. package/dist/assetLibrary/importModelFile.js +3 -16
  62. package/dist/assetLibrary/importModelFiles.d.ts +15 -0
  63. package/dist/assetLibrary/importModelFiles.d.ts.map +1 -0
  64. package/dist/assetLibrary/importModelFiles.js +58 -0
  65. package/dist/assetLibrary/importModelFiles.test.d.ts +2 -0
  66. package/dist/assetLibrary/importModelFiles.test.d.ts.map +1 -0
  67. package/dist/assetLibrary/importModelFiles.test.js +217 -0
  68. package/dist/assetLibrary/importPipeline.d.ts +15 -1
  69. package/dist/assetLibrary/importPipeline.d.ts.map +1 -1
  70. package/dist/assetLibrary/importPipeline.js +74 -38
  71. package/dist/assetLibrary/importPipeline.test.js +25 -0
  72. package/dist/assetLibrary/library.d.ts +1 -0
  73. package/dist/assetLibrary/library.d.ts.map +1 -1
  74. package/dist/assetLibrary/library.js +22 -1
  75. package/dist/assetLibrary/library.test.js +76 -0
  76. package/dist/bootstrap.d.ts +2 -1
  77. package/dist/bootstrap.d.ts.map +1 -1
  78. package/dist/bootstrap.js +69 -7
  79. package/dist/bootstrap.test.js +111 -0
  80. package/dist/config.d.ts +10 -1
  81. package/dist/config.d.ts.map +1 -1
  82. package/dist/hostFlows.d.ts +1 -0
  83. package/dist/hostFlows.d.ts.map +1 -1
  84. package/dist/hostFlows.js +9 -0
  85. package/dist/index.d.ts +10 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +6 -1
  88. package/dist/materials/modelMaterials.d.ts +19 -0
  89. package/dist/materials/modelMaterials.d.ts.map +1 -0
  90. package/dist/materials/modelMaterials.js +253 -0
  91. package/dist/materials/modelMaterials.test.d.ts +2 -0
  92. package/dist/materials/modelMaterials.test.d.ts.map +1 -0
  93. package/dist/materials/modelMaterials.test.js +188 -0
  94. package/dist/materials/types.d.ts +20 -0
  95. package/dist/materials/types.d.ts.map +1 -0
  96. package/dist/materials/types.js +1 -0
  97. package/dist/materials/useModelMaterials.d.ts +10 -0
  98. package/dist/materials/useModelMaterials.d.ts.map +1 -0
  99. package/dist/materials/useModelMaterials.js +26 -0
  100. package/dist/materials/useModelMaterials.test.d.ts +2 -0
  101. package/dist/materials/useModelMaterials.test.d.ts.map +1 -0
  102. package/dist/materials/useModelMaterials.test.js +75 -0
  103. package/dist/poses.d.ts +38 -0
  104. package/dist/poses.d.ts.map +1 -0
  105. package/dist/poses.js +41 -0
  106. package/dist/poses.test.d.ts +2 -0
  107. package/dist/poses.test.d.ts.map +1 -0
  108. package/dist/poses.test.js +38 -0
  109. package/dist/primitives.d.ts +5 -0
  110. package/dist/primitives.d.ts.map +1 -0
  111. package/dist/primitives.js +15 -0
  112. package/dist/primitives.test.d.ts +2 -0
  113. package/dist/primitives.test.d.ts.map +1 -0
  114. package/dist/primitives.test.js +27 -0
  115. package/dist/reset.realStudio.test.d.ts +2 -0
  116. package/dist/reset.realStudio.test.d.ts.map +1 -0
  117. package/dist/reset.realStudio.test.js +60 -0
  118. package/dist/studioRoot.d.ts +4 -0
  119. package/dist/studioRoot.d.ts.map +1 -0
  120. package/dist/studioRoot.js +36 -0
  121. package/dist/studioRoot.test.d.ts +2 -0
  122. package/dist/studioRoot.test.d.ts.map +1 -0
  123. package/dist/studioRoot.test.js +62 -0
  124. package/dist/useCameraStudio.d.ts.map +1 -1
  125. package/dist/useCameraStudio.js +3 -2
  126. package/package.json +9 -1
@@ -0,0 +1,117 @@
1
+ import { AgentApiError } from '../errors';
2
+ import { formatSceneObjectId } from '../objectIds';
3
+ import { getSceneObjectSummary } from '../objectResolver';
4
+ import { requireFiniteNumber, requireObject, requireString, requireVec3, } from '../validation';
5
+ import { PRIMITIVE_KINDS } from '../../primitives';
6
+ const createInput = {
7
+ type: 'object',
8
+ properties: {
9
+ kind: {
10
+ type: 'string',
11
+ enum: [...PRIMITIVE_KINDS],
12
+ description: '米制基础几何体类型',
13
+ },
14
+ position: {
15
+ type: 'object',
16
+ properties: {
17
+ x: { type: 'number' },
18
+ y: { type: 'number' },
19
+ z: { type: 'number' },
20
+ },
21
+ required: ['x', 'y', 'z'],
22
+ additionalProperties: false,
23
+ description: '创建位置(世界坐标)',
24
+ },
25
+ expectedRevision: {
26
+ type: 'number',
27
+ description: '乐观并发:读取场景时的 revision',
28
+ },
29
+ },
30
+ required: ['kind', 'position'],
31
+ additionalProperties: false,
32
+ };
33
+ const createOutput = {
34
+ type: 'object',
35
+ properties: {
36
+ objectId: { type: 'string', description: '带类型场景对象 ID,model:<key>' },
37
+ revision: { type: 'number' },
38
+ },
39
+ required: ['objectId', 'revision'],
40
+ };
41
+ function validateCreate(value) {
42
+ const args = requireObject(value);
43
+ const kind = requireString(args, 'kind');
44
+ if (!PRIMITIVE_KINDS.includes(kind)) {
45
+ throw new AgentApiError('INVALID_ARGUMENT', `未知基础几何体类型: ${kind}`, {
46
+ details: { kind },
47
+ });
48
+ }
49
+ const position = requireVec3(args.position);
50
+ let expectedRevision;
51
+ if (args.expectedRevision !== undefined) {
52
+ expectedRevision = requireFiniteNumber(args.expectedRevision, 'expectedRevision');
53
+ }
54
+ return { kind: kind, position, expectedRevision };
55
+ }
56
+ export function registerPrimitiveCommands(registry, runtime) {
57
+ registry.register({
58
+ name: 'primitives.create',
59
+ kind: 'mutation',
60
+ stateDomain: 'project',
61
+ undoable: 'single-step',
62
+ completion: 'mounted',
63
+ inputSchema: createInput,
64
+ outputSchema: createOutput,
65
+ errorCodes: [
66
+ 'API_DISPOSED',
67
+ 'INVALID_ARGUMENT',
68
+ 'REVISION_CONFLICT',
69
+ 'SCENE_UNAVAILABLE',
70
+ 'RESOURCE_LOAD_FAILED',
71
+ 'INTERNAL_ERROR',
72
+ ],
73
+ validate: validateCreate,
74
+ execute: async (args) => {
75
+ runtime.assertActive();
76
+ await runtime.project.ready;
77
+ if (args.expectedRevision !== undefined) {
78
+ runtime.assertRevision(args.expectedRevision);
79
+ }
80
+ const createPrimitive = runtime.handlers.createPrimitive;
81
+ if (!createPrimitive) {
82
+ throw new AgentApiError('SCENE_UNAVAILABLE', 'Scene Runtime 未绑定 createPrimitive,无法创建基础几何体');
83
+ }
84
+ const placed = createPrimitive({ kind: args.kind, position: args.position });
85
+ try {
86
+ await placed.persisted;
87
+ }
88
+ catch (error) {
89
+ runtime.assertActive();
90
+ throw new AgentApiError('RESOURCE_LOAD_FAILED', `基础几何体挂载失败: ${args.kind}`, {
91
+ details: {
92
+ kind: args.kind,
93
+ cause: error instanceof Error ? error.message : String(error),
94
+ },
95
+ });
96
+ }
97
+ runtime.assertActive();
98
+ const objectId = formatSceneObjectId('model', placed.key);
99
+ let mounted;
100
+ try {
101
+ mounted = getSceneObjectSummary(runtime.sheet, objectId).mounted;
102
+ }
103
+ catch (error) {
104
+ throw new AgentApiError('INTERNAL_ERROR', `放置已提交但场景对象不可见: ${objectId}`, {
105
+ details: {
106
+ objectId,
107
+ cause: error instanceof Error ? error.message : String(error),
108
+ },
109
+ });
110
+ }
111
+ if (!mounted) {
112
+ throw new AgentApiError('INTERNAL_ERROR', `放置已提交但 SheetObject 未登记: ${objectId}`, { details: { objectId } });
113
+ }
114
+ return { objectId, revision: await runtime.nextRevision() };
115
+ },
116
+ });
117
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=primitives.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.test.d.ts","sourceRoot":"","sources":["../../../src/agent/commands/primitives.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,135 @@
1
+ import { getProject, types as t, val } from '@dreamerbird/core';
2
+ import { globals } from '@dreamerbird/core/globals';
3
+ import { PLACEMENTS_REGISTRY_KEY } from '../../PlacementManager';
4
+ import { createCameraStudioApi } from '../api';
5
+ import { formatSceneObjectId } from '../objectIds';
6
+ import { AgentCommandRegistry } from '../registry';
7
+ import { createAgentRuntime } from '../runtime';
8
+ import { registerSceneSheetObject, unregisterSceneSheetObject, } from '../runtime/objectRuntime';
9
+ import { registerPrimitiveCommands } from './primitives';
10
+ let projectSeq = 0;
11
+ const cleanups = [];
12
+ afterEach(() => {
13
+ while (cleanups.length)
14
+ cleanups.pop()();
15
+ });
16
+ const POSITION = { x: 0, y: 0, z: 0 };
17
+ function deferred() {
18
+ let resolve;
19
+ const promise = new Promise((res) => {
20
+ resolve = res;
21
+ });
22
+ return { promise, resolve };
23
+ }
24
+ function setup() {
25
+ const project = getProject(`agent-primitives-${++projectSeq}`, {
26
+ state: {
27
+ definitionVersion: globals.currentProjectStateDefinitionVersion,
28
+ sheetsById: {
29
+ Scene: {
30
+ staticOverrides: { byObject: {} },
31
+ sequence: {
32
+ type: 'PositionalSequence',
33
+ length: 10,
34
+ subUnitsPerUnit: 30,
35
+ tracksByObject: {},
36
+ },
37
+ },
38
+ },
39
+ },
40
+ });
41
+ const sheet = project.sheet('Scene');
42
+ let notifyHistoric = () => { };
43
+ const runtime = createAgentRuntime({
44
+ project,
45
+ sheet,
46
+ library: {},
47
+ getStudio: () => undefined,
48
+ subscribeHistoric: (listener) => {
49
+ notifyHistoric = listener;
50
+ return () => { };
51
+ },
52
+ });
53
+ const controller = createCameraStudioApi({
54
+ buildRegistry: (currentRuntime) => {
55
+ const registry = new AgentCommandRegistry();
56
+ registerPrimitiveCommands(registry, currentRuntime);
57
+ return registry;
58
+ },
59
+ });
60
+ controller.activate(runtime);
61
+ cleanups.push(() => controller.dispose());
62
+ const createPrimitive = jest.fn();
63
+ return {
64
+ api: controller.api,
65
+ controller,
66
+ sheet,
67
+ runtime,
68
+ createPrimitive,
69
+ notifyHistoric: () => notifyHistoric(),
70
+ };
71
+ }
72
+ function bindCreatePrimitive(controller, createPrimitive, key, persisted) {
73
+ createPrimitive.mockReturnValue({ key, persisted });
74
+ const unbind = controller.bindSceneHandlers({
75
+ createPrimitive: (args) => createPrimitive(args),
76
+ });
77
+ cleanups.push(unbind);
78
+ }
79
+ function mountPrimitive(sheet, key) {
80
+ const registryObj = sheet.object(PLACEMENTS_REGISTRY_KEY, { list: t.string('[]') }, { hideFromOutline: true });
81
+ registryObj.initialValue = {
82
+ list: JSON.stringify([{ key, primitive: 'box', position: POSITION }]),
83
+ };
84
+ const obj = sheet.object(key, {
85
+ position: t.compound({ x: t.number(0), y: t.number(0), z: t.number(0) }),
86
+ });
87
+ const id = formatSceneObjectId('model', key);
88
+ registerSceneSheetObject(id, obj);
89
+ cleanups.push(() => unregisterSceneSheetObject(id, obj));
90
+ }
91
+ async function untilCreatePrimitiveCalled(createPrimitive) {
92
+ for (let i = 0; i < 50 && createPrimitive.mock.calls.length === 0; i++) {
93
+ await new Promise((resolve) => setTimeout(resolve, 0));
94
+ }
95
+ expect(createPrimitive).toHaveBeenCalled();
96
+ }
97
+ test('api.primitives.create resolves after mounting and persists the primitive entry', async () => {
98
+ const { api, controller, sheet, createPrimitive, notifyHistoric } = setup();
99
+ const mount = deferred();
100
+ bindCreatePrimitive(controller, createPrimitive, 'Box', mount.promise);
101
+ await api.ready;
102
+ let resolved = false;
103
+ const promise = api.primitives
104
+ .create({ kind: 'box', position: POSITION })
105
+ .then((result) => {
106
+ resolved = true;
107
+ return result;
108
+ });
109
+ await untilCreatePrimitiveCalled(createPrimitive);
110
+ expect(createPrimitive).toHaveBeenCalledWith({
111
+ kind: 'box',
112
+ position: POSITION,
113
+ });
114
+ expect(resolved).toBe(false);
115
+ mountPrimitive(sheet, 'Box');
116
+ notifyHistoric();
117
+ mount.resolve();
118
+ await expect(promise).resolves.toEqual({
119
+ objectId: 'model:Box',
120
+ revision: 2,
121
+ });
122
+ const registryObj = sheet.object(PLACEMENTS_REGISTRY_KEY, {
123
+ list: t.string('[]'),
124
+ });
125
+ expect(JSON.parse(val(registryObj.props.list))).toEqual([
126
+ { key: 'Box', primitive: 'box', position: POSITION },
127
+ ]);
128
+ });
129
+ test('api.primitives.create rejects an unknown kind before any handler call', async () => {
130
+ const { api, controller, createPrimitive } = setup();
131
+ bindCreatePrimitive(controller, createPrimitive, 'Box', Promise.resolve());
132
+ await api.ready;
133
+ await expect(api.primitives.create({ kind: 'cone', position: POSITION })).rejects.toMatchObject({ code: 'INVALID_ARGUMENT' });
134
+ expect(createPrimitive).not.toHaveBeenCalled();
135
+ });
@@ -4,6 +4,6 @@
4
4
  * 任何变更必须先改 spec 第 9 节,再同步本清单——契约覆盖测试
5
5
  * (contractCoverage.test.ts)对缺少与多出都失败。
6
6
  */
7
- export declare const EXPECTED_AGENT_COMMANDS: readonly ["scene.getSnapshot", "selection.get", "selection.selectObject", "selection.clear", "objects.list", "objects.get", "objects.update", "objects.delete", "models.create", "assets.list", "assets.get", "assets.importModel", "assets.rename", "assets.delete", "poses.apply", "poses.applyCustom", "poses.clear", "poses.saveCurrent", "poses.setGesture", "poses.session.start", "poses.session.commit", "poses.session.cancel", "animations.apply", "animations.clear", "animations.getStatus", "cameras.create", "cameras.setActive", "cameras.setAimTarget", "cameras.bindPath", "cameras.unbindPath", "anchors.create", "anchors.setParent", "anchors.clearParent", "paths.listPresets", "paths.createPreset", "paths.addPoint", "paths.updatePoint", "paths.deletePoint", "paths.setPointTiming", "paths.sample", "paths.session.startEditing", "paths.session.exitEditing", "paths.session.selectPoint", "sequence.getState", "sequence.play", "sequence.pause", "sequence.seek", "sequence.setLength", "keyframes.list", "keyframes.set", "keyframes.move", "keyframes.delete", "history.undo", "history.redo", "view.getState", "view.setShotView", "view.setPipVisible", "view.orbit", "view.pan", "view.dolly", "view.reset", "ui.hide", "ui.restore", "ui.setLeftStripHidden", "ui.openPanel", "ui.closePanel", "ui.setGizmoMode", "project.exportState", "exports.startVideo", "jobs.get", "jobs.cancel"];
7
+ export declare const EXPECTED_AGENT_COMMANDS: readonly ["scene.getSnapshot", "selection.get", "selection.selectObject", "selection.clear", "objects.list", "objects.get", "objects.update", "objects.delete", "models.create", "models.retry", "models.replace", "primitives.create", "materials.list", "materials.update", "assets.list", "assets.get", "assets.importModel", "assets.importModels", "assets.rename", "assets.delete", "poses.apply", "poses.applyCustom", "poses.clear", "poses.saveCurrent", "poses.setGesture", "poses.session.start", "poses.session.commit", "poses.session.cancel", "animations.apply", "animations.clear", "animations.getStatus", "cameras.create", "cameras.setActive", "cameras.setAimTarget", "cameras.bindPath", "cameras.unbindPath", "anchors.create", "anchors.setParent", "anchors.clearParent", "paths.listPresets", "paths.createPreset", "paths.addPoint", "paths.updatePoint", "paths.deletePoint", "paths.setPointTiming", "paths.sample", "paths.session.startEditing", "paths.session.exitEditing", "paths.session.selectPoint", "sequence.getState", "sequence.play", "sequence.pause", "sequence.seek", "sequence.setLength", "keyframes.list", "keyframes.set", "keyframes.move", "keyframes.delete", "history.undo", "history.redo", "view.getState", "view.setShotView", "view.setPipVisible", "view.orbit", "view.pan", "view.dolly", "view.reset", "ui.hide", "ui.restore", "ui.setLeftStripHidden", "ui.openPanel", "ui.closePanel", "ui.setGizmoMode", "project.exportState", "exports.startVideo", "jobs.get", "jobs.cancel"];
8
8
  export type ExpectedAgentCommand = (typeof EXPECTED_AGENT_COMMANDS)[number];
9
9
  //# sourceMappingURL=expectedCommands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"expectedCommands.d.ts","sourceRoot":"","sources":["../../src/agent/expectedCommands.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,+1CAwE1B,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"expectedCommands.d.ts","sourceRoot":"","sources":["../../src/agent/expectedCommands.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,m9CA8E1B,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA"}
@@ -14,9 +14,15 @@ export const EXPECTED_AGENT_COMMANDS = [
14
14
  'objects.update',
15
15
  'objects.delete',
16
16
  'models.create',
17
+ 'models.retry',
18
+ 'models.replace',
19
+ 'primitives.create',
20
+ 'materials.list',
21
+ 'materials.update',
17
22
  'assets.list',
18
23
  'assets.get',
19
24
  'assets.importModel',
25
+ 'assets.importModels',
20
26
  'assets.rename',
21
27
  'assets.delete',
22
28
  'poses.apply',
@@ -28,6 +28,8 @@ export declare function assertStaticPropsWritable(sheetObject: ISheetObject<$Int
28
28
  * SCENE_UNAVAILABLE(不静默返回,spec 8.3)。
29
29
  */
30
30
  export declare function setStaticProps(sheetObject: ISheetObject<$IntentionalAny>, props: Record<string, unknown>): void;
31
+ export declare function assertStaticPropsWithLeafGuardWritable(sheetObject: ISheetObject<$IntentionalAny>, props: Record<string, unknown>, guardedPropPaths: string[]): void;
32
+ export declare function setStaticPropsWithLeafGuard(sheetObject: ISheetObject<$IntentionalAny>, props: Record<string, unknown>, guardedPropPaths: string[]): void;
31
33
  /**
32
34
  * 领域静态写入 + 坐标定格同事务(2026-10-19,Agent API F1 修复):
33
35
  * 绑定/父级切换类命令(setParent/clearParent/unbindPath/setAimTarget 解除)
@@ -1 +1 @@
1
- {"version":3,"file":"propAccess.d.ts","sourceRoot":"","sources":["../../src/agent/propAccess.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gCAAgC,CAAA;AACnE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAA;AAInD;;;GAGG;AACH,QAAA,MAAM,gBAAgB,aAWpB,CAAA;AAEF,OAAO,EAAC,gBAAgB,EAAC,CAAA;AAEzB,6DAA6D;AAC7D,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,YAAY,CAAC,eAAe,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,CAAC,CAM1B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,YAAY,CAAC,eAAe,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,OAAO,CAaT;AA4FD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAyBN;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAGN;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI,CAGN"}
1
+ {"version":3,"file":"propAccess.d.ts","sourceRoot":"","sources":["../../src/agent/propAccess.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,gCAAgC,CAAA;AACnE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAA;AAInD;;;GAGG;AACH,QAAA,MAAM,gBAAgB,aAYpB,CAAA;AAEF,OAAO,EAAC,gBAAgB,EAAC,CAAA;AAEzB,6DAA6D;AAC7D,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,YAAY,CAAC,eAAe,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,CAAC,CAM1B;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,YAAY,CAAC,eAAe,CAAC,EAClC,QAAQ,EAAE,MAAM,GACf,OAAO,CAaT;AA4FD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAyBN;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAGN;AAED,wBAAgB,sCAAsC,CACpD,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,gBAAgB,EAAE,MAAM,EAAE,GACzB,IAAI,CAmBN;AAED,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,gBAAgB,EAAE,MAAM,EAAE,GACzB,IAAI,CAGN;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,YAAY,CAAC,eAAe,CAAC,EAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,IAAI,CAGN"}
@@ -25,6 +25,7 @@ const domainOwnedProps = new Set([
25
25
  'gestureL',
26
26
  'gestureR',
27
27
  'animation',
28
+ 'materialOverrides',
28
29
  'aimTarget',
29
30
  'pathKey',
30
31
  'parent',
@@ -154,6 +155,26 @@ export function setStaticProps(sheetObject, props) {
154
155
  assertStaticPropsWritable(sheetObject, props);
155
156
  writePropsInTransaction(sheetObject, props);
156
157
  }
158
+ export function assertStaticPropsWithLeafGuardWritable(sheetObject, props, guardedPropPaths) {
159
+ const current = sheetObject.value;
160
+ for (const [key, value] of Object.entries(props)) {
161
+ if (!current || !(key in current)) {
162
+ throw new AgentApiError('INVALID_ARGUMENT', `未知属性: ${key}`, {
163
+ details: { prop: key },
164
+ });
165
+ }
166
+ validateValueShape(key, current[key], value);
167
+ }
168
+ for (const propPath of guardedPropPaths) {
169
+ if (isPropSequenced(sheetObject, propPath)) {
170
+ throw new AgentApiError('INVALID_STATE', `属性 ${propPath} 已建轨,请改用 keyframes.* 写入`, { details: { prop: propPath } });
171
+ }
172
+ }
173
+ }
174
+ export function setStaticPropsWithLeafGuard(sheetObject, props, guardedPropPaths) {
175
+ assertStaticPropsWithLeafGuardWritable(sheetObject, props, guardedPropPaths);
176
+ writePropsInTransaction(sheetObject, props);
177
+ }
157
178
  /**
158
179
  * 领域静态写入 + 坐标定格同事务(2026-10-19,Agent API F1 修复):
159
180
  * 绑定/父级切换类命令(setParent/clearParent/unbindPath/setAimTarget 解除)
@@ -1 +1 @@
1
- {"version":3,"file":"registerCommands.d.ts","sourceRoot":"","sources":["../../src/agent/registerCommands.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,WAAW,CAAA;AA8B3C;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,YAAY,GACpB,IAAI,CAkCN"}
1
+ {"version":3,"file":"registerCommands.d.ts","sourceRoot":"","sources":["../../src/agent/registerCommands.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,WAAW,CAAA;AAgC3C;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,YAAY,GACpB,IAAI,CAoCN"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 全量 Agent 命令注册(2026-10-19,Task 15):按 Task 4–14 落地顺序
3
- * 注册全部 18 个命令组(spec §9 命令面,71 条命令),供生产会话与
3
+ * 注册全部 20 个命令组(spec §9 命令面,75 条命令),供生产会话与
4
4
  * 契约测试共用同一个注册入口。
5
5
  *
6
6
  * 重复命令兜底:registry.register 本身按名拒重(INVALID_STATE),注册
@@ -20,6 +20,8 @@ import { registerHistoryCommands } from './commands/history';
20
20
  import { registerKeyframeCommands } from './commands/keyframes';
21
21
  // Task 8:模型创建 + 资产管理 + 任务查询/取消
22
22
  import { registerModelCommands } from './commands/models';
23
+ import { registerPrimitiveCommands } from './commands/primitives';
24
+ import { registerMaterialCommands } from './commands/materials';
23
25
  import { registerAssetCommands } from './commands/assets';
24
26
  import { registerJobCommands } from './commands/jobs';
25
27
  // Task 9:姿势/手势 + 动画
@@ -54,6 +56,8 @@ export function registerAllAgentCommands(registry, runtime) {
54
56
  registerKeyframeCommands(registry, runtime);
55
57
  // Task 8
56
58
  registerModelCommands(registry, runtime);
59
+ registerPrimitiveCommands(registry, runtime);
60
+ registerMaterialCommands(registry, runtime);
57
61
  registerAssetCommands(registry, runtime);
58
62
  registerJobCommands(registry, runtime);
59
63
  // Task 9
@@ -9,6 +9,8 @@
9
9
  */
10
10
  import type { ISheetObject } from '@dreamerbird/core';
11
11
  import type { SceneObjectId } from '../types';
12
+ export declare function subscribeSceneSheetObjects(listener: () => void): () => void;
13
+ export declare function clearSceneSheetObjects(): void;
12
14
  /** 登记某业务对象当前挂载的 SheetObject(objRef 填充时) */
13
15
  export declare function registerSceneSheetObject(id: SceneObjectId, obj: ISheetObject<any>): void;
14
16
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"objectRuntime.d.ts","sourceRoot":"","sources":["../../../src/agent/runtime/objectRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAI3C,2CAA2C;AAC3C,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,aAAa,EACjB,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,GACrB,IAAI,CAEN;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,aAAa,EACjB,GAAG,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GACtB,IAAI,CAGN;AAED,oDAAoD;AACpD,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,aAAa,GAChB,YAAY,CAAC,GAAG,CAAC,GAAG,SAAS,CAE/B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAK5E"}
1
+ {"version":3,"file":"objectRuntime.d.ts","sourceRoot":"","sources":["../../../src/agent/runtime/objectRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,UAAU,CAAA;AAS3C,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAG3E;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAED,2CAA2C;AAC3C,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,aAAa,EACjB,GAAG,EAAE,YAAY,CAAC,GAAG,CAAC,GACrB,IAAI,CAGN;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,aAAa,EACjB,GAAG,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GACtB,IAAI,CAGN;AAED,oDAAoD;AACpD,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,aAAa,GAChB,YAAY,CAAC,GAAG,CAAC,GAAG,SAAS,CAE/B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAK5E"}
@@ -1,7 +1,20 @@
1
1
  const sceneSheetObjects = new Map();
2
+ const listeners = new Set();
3
+ function emitChange() {
4
+ listeners.forEach((listener) => listener());
5
+ }
6
+ export function subscribeSceneSheetObjects(listener) {
7
+ listeners.add(listener);
8
+ return () => listeners.delete(listener);
9
+ }
10
+ export function clearSceneSheetObjects() {
11
+ sceneSheetObjects.clear();
12
+ emitChange();
13
+ }
2
14
  /** 登记某业务对象当前挂载的 SheetObject(objRef 填充时) */
3
15
  export function registerSceneSheetObject(id, obj) {
4
16
  sceneSheetObjects.set(id, obj);
17
+ emitChange();
5
18
  }
6
19
  /**
7
20
  * 按 id 清理登记(objRef 清空/组件卸载时)。传入 obj 时仅当登记的还是
@@ -10,7 +23,8 @@ export function registerSceneSheetObject(id, obj) {
10
23
  export function unregisterSceneSheetObject(id, obj) {
11
24
  if (obj && sceneSheetObjects.get(id) !== obj)
12
25
  return;
13
- sceneSheetObjects.delete(id);
26
+ if (sceneSheetObjects.delete(id))
27
+ emitChange();
14
28
  }
15
29
  /** 当前挂载的 SheetObject;未挂载(注册表有条目但组件未起)为 undefined */
16
30
  export function getSceneSheetObject(id) {
@@ -30,6 +30,7 @@ export type AgentRuntime = {
30
30
  readonly library: Library;
31
31
  readonly getStudio: () => IStudio | undefined;
32
32
  readonly jobs: AgentJobManager;
33
+ subscribeHistoric(listener: () => void): () => void;
33
34
  /** 当前会话的 historic revision,从 1 开始 */
34
35
  readonly revision: number;
35
36
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/agent/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAC,MAAM,mBAAmB,CAAA;AAGhE,OAAO,EAAC,eAAe,EAAC,MAAM,QAAQ,CAAA;AACtC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,yBAAyB,CAAA;AAyCpD;;;;GAIG;AAEH,MAAM,WAAW,yBAAyB;CAAG;AAE7C,KAAK,QAAQ,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;AAElD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,OAAO,GAAG,SAAS,CAAA;IACpC;2FACuF;IACvF,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;CACxD,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,GAAG,SAAS,CAAA;IAC7C,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACrC,6DAA6D;IAC7D,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACtC,mBAAmB;IACnB,YAAY,IAAI,IAAI,CAAA;IACpB,uCAAuC;IACvC,aAAa,IAAI,IAAI,CAAA;IACrB,wDAAwD;IACxD,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAA;IAC5C;qEACiE;IACjE,QAAQ,IAAI,YAAY,CAAA;IACxB,qDAAqD;IACrD,SAAS,IAAI,IAAI,CAAA;IACjB;8DAC0D;IAC1D,OAAO,IAAI,IAAI,CAAA;CAChB,CAAA;AAsED,0DAA0D;AAC1D,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOnE;AAqHD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY,CAyBvE"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/agent/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAC,MAAM,mBAAmB,CAAA;AAGhE,OAAO,EAAC,eAAe,EAAC,MAAM,QAAQ,CAAA;AACtC,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,yBAAyB,CAAA;AAyCpD;;;;GAIG;AAEH,MAAM,WAAW,yBAAyB;CAAG;AAE7C,KAAK,QAAQ,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAA;AAElD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,OAAO,GAAG,SAAS,CAAA;IACpC;2FACuF;IACvF,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;CACxD,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,GAAG,SAAS,CAAA;IAC7C,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,iBAAiB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IACnD,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACrC,6DAA6D;IAC7D,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACtC,mBAAmB;IACnB,YAAY,IAAI,IAAI,CAAA;IACpB,uCAAuC;IACvC,aAAa,IAAI,IAAI,CAAA;IACrB,wDAAwD;IACxD,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAAA;IAC5C;qEACiE;IACjE,QAAQ,IAAI,YAAY,CAAA;IACxB,qDAAqD;IACrD,SAAS,IAAI,IAAI,CAAA;IACjB;8DAC0D;IAC1D,OAAO,IAAI,IAAI,CAAA;CAChB,CAAA;AAuED,0DAA0D;AAC1D,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOnE;AAsHD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY,CA0BvE"}
@@ -87,6 +87,7 @@ function makeRuntime(core, generation) {
87
87
  library: core.library,
88
88
  getStudio: core.getStudio,
89
89
  jobs: core.jobs,
90
+ subscribeHistoric: core.subscribeHistoric,
90
91
  get revision() {
91
92
  return core.revision;
92
93
  },
@@ -197,6 +198,7 @@ export function createAgentRuntime(deps) {
197
198
  revision: 1,
198
199
  currentGeneration: first,
199
200
  generations: new Set([first]),
201
+ subscribeHistoric: deps.subscribeHistoric,
200
202
  unsubscribeHistoric: () => { },
201
203
  };
202
204
  // 订阅必须在 core 建好之后:真实 subscribeHistoric 走 core
@@ -1,4 +1,6 @@
1
1
  import type { Object3D } from 'three';
2
- /** 递归释放对象树的 GPU 资源(几何体/材质/纹理),实例卸载时调用 */
3
- export declare function disposeObjectTree(root: Object3D): void;
2
+ /** 递归释放对象树的 GPU 资源;纹理默认随材质释放,共享纹理的克隆实例可关闭 */
3
+ export declare function disposeObjectTree(root: Object3D, options?: {
4
+ disposeTextures?: boolean;
5
+ }): void;
4
6
  //# sourceMappingURL=dispose.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispose.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/dispose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,QAAQ,EAAU,MAAM,OAAO,CAAA;AAEtD,yCAAyC;AACzC,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAqBtD"}
1
+ {"version":3,"file":"dispose.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/dispose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,QAAQ,EAAU,MAAM,OAAO,CAAA;AAEtD,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAM,GACxC,IAAI,CAuBN"}
@@ -1,5 +1,5 @@
1
- /** 递归释放对象树的 GPU 资源(几何体/材质/纹理),实例卸载时调用 */
2
- export function disposeObjectTree(root) {
1
+ /** 递归释放对象树的 GPU 资源;纹理默认随材质释放,共享纹理的克隆实例可关闭 */
2
+ export function disposeObjectTree(root, options = {}) {
3
3
  root.traverse((o) => {
4
4
  const mesh = o;
5
5
  if (!mesh.isMesh)
@@ -11,9 +11,11 @@ export function disposeObjectTree(root) {
11
11
  ? [mesh.material]
12
12
  : [];
13
13
  for (const m of mats) {
14
- for (const v of Object.values(m)) {
15
- if (v?.isTexture)
16
- v.dispose();
14
+ if (options.disposeTextures !== false) {
15
+ for (const v of Object.values(m)) {
16
+ if (v?.isTexture)
17
+ v.dispose();
18
+ }
17
19
  }
18
20
  m.dispose();
19
21
  }
@@ -1,8 +1,9 @@
1
+ import type { LoadingManager } from 'three';
1
2
  import { GLTFLoader } from 'three-stdlib';
2
3
  /**
3
4
  * 统一创建 GLTFLoader:接 Draco 解码(解码器文件在 public/draco,
4
5
  * 从 three 原样拷贝)。未压缩的 GLB 走原路径,不受影响;
5
6
  * DRACOLoader 全局一个,解码 wasm 只加载一次。
6
7
  */
7
- export declare function createGLTFLoader(): GLTFLoader;
8
+ export declare function createGLTFLoader(manager?: LoadingManager): GLTFLoader;
8
9
  //# sourceMappingURL=gltfLoader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gltfLoader.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/gltfLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,UAAU,EAAC,MAAM,cAAc,CAAA;AAIpD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAG7C"}
1
+ {"version":3,"file":"gltfLoader.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/gltfLoader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,OAAO,CAAA;AACzC,OAAO,EAAc,UAAU,EAAC,MAAM,cAAc,CAAA;AAIpD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,UAAU,CAGrE"}
@@ -5,7 +5,7 @@ let dracoLoader;
5
5
  * 从 three 原样拷贝)。未压缩的 GLB 走原路径,不受影响;
6
6
  * DRACOLoader 全局一个,解码 wasm 只加载一次。
7
7
  */
8
- export function createGLTFLoader() {
8
+ export function createGLTFLoader(manager) {
9
9
  dracoLoader ?? (dracoLoader = new DRACOLoader().setDecoderPath('/draco/'));
10
- return new GLTFLoader().setDRACOLoader(dracoLoader);
10
+ return new GLTFLoader(manager).setDRACOLoader(dracoLoader);
11
11
  }
@@ -0,0 +1,6 @@
1
+ import { LoadingManager } from 'three';
2
+ export declare function createResourceUrlModifier(files: File[]): {
3
+ manager: LoadingManager;
4
+ dispose(): void;
5
+ };
6
+ //# sourceMappingURL=gltfResourceSet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gltfResourceSet.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/gltfResourceSet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,OAAO,CAAA;AAEpC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,IAAI,EAAE;;;EAuBtD"}
@@ -0,0 +1,27 @@
1
+ import { LoadingManager } from 'three';
2
+ export function createResourceUrlModifier(files) {
3
+ const urls = new Map();
4
+ const byName = new Map(files.map((file) => [file.name, file]));
5
+ const manager = new LoadingManager();
6
+ manager.setURLModifier((url) => {
7
+ if (/^(blob:|data:|https?:)/.test(url))
8
+ return url;
9
+ const name = decodeURIComponent(url.split('/').pop() ?? url);
10
+ const file = byName.get(name);
11
+ if (!file)
12
+ throw new Error(`GLTF 外部资源缺失: ${name}`);
13
+ let objectUrl = urls.get(name);
14
+ if (!objectUrl) {
15
+ objectUrl = URL.createObjectURL(file);
16
+ urls.set(name, objectUrl);
17
+ }
18
+ return objectUrl;
19
+ });
20
+ return {
21
+ manager,
22
+ dispose() {
23
+ urls.forEach((url) => URL.revokeObjectURL(url));
24
+ urls.clear();
25
+ },
26
+ };
27
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gltfResourceSet.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gltfResourceSet.test.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/gltfResourceSet.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { createResourceUrlModifier } from './gltfResourceSet';
2
+ describe('createResourceUrlModifier', () => {
3
+ const originalCreateObjectURL = URL.createObjectURL;
4
+ const originalRevokeObjectURL = URL.revokeObjectURL;
5
+ let nextId = 0;
6
+ beforeEach(() => {
7
+ URL.createObjectURL = jest.fn(() => `blob:resource-${++nextId}`);
8
+ URL.revokeObjectURL = jest.fn();
9
+ });
10
+ afterEach(() => {
11
+ URL.createObjectURL = originalCreateObjectURL;
12
+ URL.revokeObjectURL = originalRevokeObjectURL;
13
+ });
14
+ it('按相对 URI 的文件名映射并复用 object URL,dispose 全部回收', () => {
15
+ const bin = new File([new Uint8Array(36)], 'triangle.bin');
16
+ const png = new File(['png'], 'tiny pixel.png', { type: 'image/png' });
17
+ const resources = createResourceUrlModifier([bin, png]);
18
+ const binUrl = resources.manager.resolveURL('./geometry/triangle.bin');
19
+ const pngUrl = resources.manager.resolveURL('./textures/tiny%20pixel.png');
20
+ expect(resources.manager.resolveURL('triangle.bin')).toBe(binUrl);
21
+ expect(URL.createObjectURL).toHaveBeenCalledTimes(2);
22
+ resources.dispose();
23
+ expect(URL.revokeObjectURL).toHaveBeenCalledTimes(2);
24
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith(binUrl);
25
+ expect(URL.revokeObjectURL).toHaveBeenCalledWith(pngUrl);
26
+ });
27
+ it('保留已有绝对资源 URL,缺失文件报出资源名', () => {
28
+ const resources = createResourceUrlModifier([]);
29
+ for (const url of [
30
+ 'blob:existing',
31
+ 'data:image/png;base64,x',
32
+ 'https://example.com/a.bin',
33
+ 'http://example.com/a.bin',
34
+ ]) {
35
+ expect(resources.manager.resolveURL(url)).toBe(url);
36
+ }
37
+ expect(() => resources.manager.resolveURL('./missing.bin')).toThrow('GLTF 外部资源缺失: missing.bin');
38
+ resources.dispose();
39
+ });
40
+ });
@@ -1,3 +1,8 @@
1
+ /**
2
+ * 宿主外部模型入库:单文件兼容入口,委托 importModelFiles。
3
+ * 放置管线只认库条目(按 id 解析 blob),外部模型必须真实入库,
4
+ * 姿势/骨架匹配/持久化全部照常。library/deps 可注入(测试用)。
5
+ */
1
6
  import type { ImportDeps } from './importPipeline';
2
7
  import type { Library } from './library';
3
8
  import type { ModelEntry } from './types';
@@ -1 +1 @@
1
- {"version":3,"file":"importModelFile.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/importModelFile.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAEhD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,SAAS,CAAA;AAEvC,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,OAAsB,EAC/B,IAAI,GAAE,UAA6B,GAClC,OAAO,CAAC,UAAU,CAAC,CAerB"}
1
+ {"version":3,"file":"importModelFile.d.ts","sourceRoot":"","sources":["../../src/assetLibrary/importModelFile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAA;AAGhD,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,WAAW,CAAA;AACtC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,SAAS,CAAA;AAEvC,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,OAAsB,EAC/B,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAAC,UAAU,CAAC,CAGrB"}