@webspatial/core-sdk 1.3.0 → 1.4.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.
@@ -4,7 +4,7 @@ import {
4
4
  CommandResultSuccess,
5
5
  } from '../CommandResultUtils'
6
6
 
7
- // 添加window扩展接口
7
+ // add window interface for JSB call
8
8
  declare global {
9
9
  interface Window {
10
10
  __handleJSBMessage: (message: string) => any
@@ -24,7 +24,7 @@ type JSBError = {
24
24
  console.log('PuppeteerPlatform')
25
25
 
26
26
  export class PuppeteerPlatform implements PlatformAbility {
27
- // 存储iframe实例
27
+ // store iframe instance
28
28
  private iframeRegistry: Map<string, HTMLIFrameElement> = new Map()
29
29
 
30
30
  constructor() {}
@@ -32,7 +32,7 @@ export class PuppeteerPlatform implements PlatformAbility {
32
32
  callJSB(cmd: string, msg: string): Promise<CommandResult> {
33
33
  return new Promise(resolve => {
34
34
  try {
35
- // 检查__handleJSBMessage是否存在
35
+ // check __handleJSBMessage exist
36
36
  if (window.__handleJSBMessage) {
37
37
  try {
38
38
  console.log(` core-sdk Puppeteer Platform: callJSB: ${cmd}::${msg}`)
@@ -45,7 +45,7 @@ export class PuppeteerPlatform implements PlatformAbility {
45
45
  resolve(CommandResultFailure('500', 'JSB execution error'))
46
46
  }
47
47
  } else {
48
- // 如果不存在,返回默认结果
48
+ // if not exist, return default result
49
49
  resolve(CommandResultSuccess('ok'))
50
50
  }
51
51
  } catch (error: unknown) {
@@ -58,7 +58,7 @@ export class PuppeteerPlatform implements PlatformAbility {
58
58
  }
59
59
 
60
60
  /**
61
- * 同步创建Spatialized2DElementPuppeteer Runner
61
+ * Synchronously create Spatialized2DElement to Puppeteer Runner
62
62
  */
63
63
  private createSpatializedElementSync(
64
64
  spatialId: string,
@@ -68,10 +68,10 @@ export class PuppeteerPlatform implements PlatformAbility {
68
68
  console.log(
69
69
  `[Puppeteer Platform] Creating spatialized element sync with id: ${spatialId}, url: ${webspatialUrl}`,
70
70
  )
71
- // 直接调用Puppeteer Runner的方法来创建元素
71
+ // directly call Puppeteer Runner method to create element
72
72
  const win = window as any
73
73
  if (win.__handleJSBMessage) {
74
- // 使用更简洁的格式,确保JSBManager能正确使用我们传递的spatialId
74
+ // use simpler format to ensure JSBManager can correctly use our passed spatialId
75
75
  const createCommand = {
76
76
  id: spatialId,
77
77
  url: webspatialUrl,
@@ -96,9 +96,9 @@ export class PuppeteerPlatform implements PlatformAbility {
96
96
  )
97
97
  return new Promise(resolve => {
98
98
  try {
99
- // 创建完整的webspatial URL
99
+ // create complete webspatial URL
100
100
  const webspatialUrl = `webspatial://${command}${query ? `?${query}` : ''}`
101
- // 使用iframe创建新窗口
101
+ // use iframe to create new window
102
102
  const { spatialId, iframe, windowProxy } = this.createIframeWindow(
103
103
  webspatialUrl,
104
104
  target,
@@ -112,7 +112,7 @@ export class PuppeteerPlatform implements PlatformAbility {
112
112
  console.log(
113
113
  `[Puppeteer Platform] iframe created with spatialId: ${spatialId}`,
114
114
  )
115
- // 注册iframe
115
+ // store iframe instance
116
116
  this.iframeRegistry.set(spatialId, iframe)
117
117
  resolve(CommandResultSuccess({ windowProxy, id: spatialId }))
118
118
  } catch (error) {
@@ -131,7 +131,7 @@ export class PuppeteerPlatform implements PlatformAbility {
131
131
  features?: string,
132
132
  ): CommandResult {
133
133
  try {
134
- // 创建完整的webspatial URL
134
+ // create complete webspatial URL
135
135
  const webspatialUrl = `webspatial://${command}${query ? `?${query}` : ''}`
136
136
  console.log(`Calling webspatial protocol sync: ${webspatialUrl}`)
137
137
 
@@ -147,7 +147,7 @@ export class PuppeteerPlatform implements PlatformAbility {
147
147
  this.createSpatializedElementSync(spatialId, webspatialUrl)
148
148
  }
149
149
 
150
- // 注册iframe
150
+ // store iframe instance
151
151
  this.iframeRegistry.set(spatialId, iframe)
152
152
 
153
153
  return CommandResultSuccess({ windowProxy, id: spatialId })
@@ -161,27 +161,27 @@ export class PuppeteerPlatform implements PlatformAbility {
161
161
  }
162
162
 
163
163
  /**
164
- * 创建基于iframe的窗口
164
+ * Synchronously create iframe-based window
165
165
  */
166
166
  private createIframeWindow(url: string, target?: string, features?: string) {
167
- // 创建iframe元素
167
+ // create iframe element
168
168
  const iframe = document.createElement('iframe')
169
169
 
170
- // 设置iframe属性
170
+ // set iframe attributes
171
171
  iframe.style.border = 'none'
172
- iframe.style.display = 'none' // 初始隐藏
172
+ iframe.style.display = 'none'
173
173
  iframe.style.width = '100%'
174
174
  iframe.style.height = '100%'
175
175
 
176
- // 生成唯一的spatialId
176
+ // set iframe id
177
177
  const spatialId = this.generateUUID()
178
178
  iframe.spatialId = spatialId
179
179
  iframe.id = `spatial-iframe-${spatialId}`
180
180
 
181
- // 解析features参数
181
+ // parse features parameter
182
182
  const featuresObj = this.parseFeatures(features || '')
183
183
 
184
- // 根据features设置iframe样式
184
+ // set iframe styles based on features
185
185
  if (featuresObj.width) {
186
186
  iframe.style.width = featuresObj.width
187
187
  }
@@ -197,36 +197,36 @@ export class PuppeteerPlatform implements PlatformAbility {
197
197
  iframe.style.position = 'absolute'
198
198
  }
199
199
 
200
- // 添加iframeDOM
200
+ // add iframe to DOM
201
201
  document.body.appendChild(iframe)
202
202
 
203
- // 创建增强的windowProxy模拟对象
203
+ // create enhanced windowProxy object
204
204
  const windowProxy = this.createEnhancedWindowProxy(iframe, url, spatialId)
205
205
 
206
- // 设置iframesrc
206
+ // set iframe src
207
207
  iframe.src = 'about:blank'
208
208
 
209
209
  console.log(
210
210
  `PuppeteerPlatform created iframe window with spatialId: ${spatialId}, URL: ${url}`,
211
211
  )
212
212
 
213
- // 初始化iframe内容
213
+ // initialize iframe content
214
214
  this.initializeIframeContent(iframe, url, spatialId)
215
215
 
216
216
  return { spatialId, iframe, windowProxy }
217
217
  }
218
218
 
219
219
  /**
220
- * 创建增强的windowProxy对象
220
+ * create enhanced windowProxy object
221
221
  */
222
222
  private createEnhancedWindowProxy(
223
223
  iframe: HTMLIFrameElement,
224
224
  url: string,
225
225
  spatialId: string,
226
226
  ) {
227
- // 创建增强的windowProxy模拟对象
227
+ // create enhanced windowProxy object
228
228
  return {
229
- // 基本属性
229
+ // basic properties
230
230
  location: {
231
231
  href: url,
232
232
  toString: () => url,
@@ -240,25 +240,25 @@ export class PuppeteerPlatform implements PlatformAbility {
240
240
  userAgent: `Mozilla/5.0 (WebKit) SpatialId/${spatialId}`,
241
241
  },
242
242
 
243
- // 方法
243
+ // methods
244
244
  close: () => {
245
245
  console.log(`Closing iframe with spatialId: ${spatialId}`)
246
246
  iframe.remove()
247
247
  this.iframeRegistry.delete(spatialId)
248
248
  },
249
249
 
250
- // 文档访问
250
+ // document access
251
251
  document: iframe.contentDocument || ({} as Document),
252
252
  contentWindow: iframe.contentWindow || ({} as Window),
253
253
 
254
- // 添加消息通信方法
254
+ // add message communication method
255
255
  postMessage: (message: any, targetOrigin?: string) => {
256
256
  if (iframe.contentWindow) {
257
257
  iframe.contentWindow.postMessage(message, targetOrigin || '*')
258
258
  }
259
259
  },
260
260
 
261
- // 添加事件监听方法
261
+ // add event listener method
262
262
  addEventListener: (
263
263
  type: string,
264
264
  listener: EventListenerOrEventListenerObject,
@@ -277,11 +277,11 @@ export class PuppeteerPlatform implements PlatformAbility {
277
277
  }
278
278
  },
279
279
 
280
- // 执行JavaScript
280
+ // execute JavaScript
281
281
  executeScript: (code: string): any => {
282
282
  if (iframe.contentWindow) {
283
283
  try {
284
- // 使用类型断言和更安全的方式执行脚本
284
+ // use type assertion and safer way to execute script
285
285
  const win = iframe.contentWindow as any
286
286
  return win.eval(code)
287
287
  } catch (error) {
@@ -295,16 +295,16 @@ export class PuppeteerPlatform implements PlatformAbility {
295
295
  return null
296
296
  },
297
297
 
298
- // 获取iframe引用
298
+ // get iframe reference
299
299
  getIframe: () => iframe,
300
300
 
301
- // 获取spatialId
301
+ // get spatialId
302
302
  getSpatialId: () => spatialId,
303
303
  }
304
304
  }
305
305
 
306
306
  /**
307
- * 初始化iframe内容
307
+ * initialize iframe content
308
308
  */
309
309
  private initializeIframeContent(
310
310
  iframe: HTMLIFrameElement,
@@ -312,20 +312,20 @@ export class PuppeteerPlatform implements PlatformAbility {
312
312
  spatialId: string,
313
313
  ): void {
314
314
  try {
315
- // 等待iframe加载完成
315
+ // wait for iframe to load
316
316
  iframe.onload = () => {
317
317
  try {
318
- // 设置iframe内容
318
+ // set iframe content
319
319
  const iframeContent = `
320
- // 注入通信脚本
320
+ // inject communication script
321
321
  window.webSpatialId = '${spatialId}';
322
322
  window.SpatialId = '${spatialId}';
323
323
 
324
- // 重写window.open以支持webspatial协议
324
+ // override window.open to support webspatial protocol
325
325
  const originalOpen = window.open;
326
326
  window.open = function(url, target, features) {
327
327
  if (url && url.startsWith('webspatial://')) {
328
- // 通过windowProxy处理webspatial协议
328
+ // handle webspatial protocol through windowProxy
329
329
  const windowProxy = new Proxy({}, {
330
330
  get: function(target, prop) {
331
331
  if (prop === 'toString') {
@@ -339,33 +339,33 @@ export class PuppeteerPlatform implements PlatformAbility {
339
339
  return originalOpen.call(window, url, target, features);
340
340
  };
341
341
 
342
- // 设置navigator.userAgent以识别webspatial环境
342
+ // set navigator.userAgent to identify webspatial environment
343
343
  Object.defineProperty(navigator, 'userAgent', {
344
344
  value: 'WebSpatial/1.0 ' + navigator.userAgent,
345
345
  configurable: true
346
346
  });
347
347
 
348
- // 发送加载完成消息
348
+ // send loaded message
349
349
  window.parent.postMessage({
350
350
  type: 'iframe_loaded',
351
351
  spatialId: '${spatialId}',
352
352
  url: '${url}'
353
353
  }, '${window.location.origin}');
354
354
 
355
- // 设置消息处理器
355
+ // set message handler
356
356
  window.addEventListener('message', (event) => {
357
357
  if (event.origin !== window.parent.location.origin) return;
358
358
 
359
359
  const data = event.data;
360
360
  if (data && data.type === 'webspatial_command') {
361
- // 处理来自父窗口的命令
361
+ // handle command from parent window
362
362
  console.log('Received command in iframe from parent:', data.command);
363
- // 这里可以添加命令处理逻辑
363
+ // add command handling logic here
364
364
  }
365
365
  });
366
366
  `
367
367
 
368
- // 使用document.write代替eval,更安全且符合类型定义
368
+ // use document.write instead of eval for security and type compliance
369
369
  const doc = iframe.contentDocument
370
370
  if (doc) {
371
371
  doc.open()
@@ -400,7 +400,7 @@ export class PuppeteerPlatform implements PlatformAbility {
400
400
  }
401
401
 
402
402
  /**
403
- * 解析features字符串为对象
403
+ * parse features string to object
404
404
  */
405
405
  private parseFeatures(features: string): Record<string, string> {
406
406
  const result: Record<string, string> = {}
@@ -417,7 +417,7 @@ export class PuppeteerPlatform implements PlatformAbility {
417
417
  }
418
418
 
419
419
  /**
420
- * 发送消息到指定spatialId的iframe
420
+ * send message to iframe with specified spatialId
421
421
  */
422
422
  public sendMessageToIframe(spatialId: string, message: any): boolean {
423
423
  const iframe = this.iframeRegistry.get(spatialId)
@@ -429,7 +429,7 @@ export class PuppeteerPlatform implements PlatformAbility {
429
429
  }
430
430
 
431
431
  /**
432
- * 获取所有活跃的iframe
432
+ * get all active iframes
433
433
  */
434
434
  public getAllActiveIframes(): Array<{
435
435
  spatialId: string
@@ -445,10 +445,10 @@ export class PuppeteerPlatform implements PlatformAbility {
445
445
  }
446
446
 
447
447
  /**
448
- * 清理资源
448
+ * dispose all active iframes
449
449
  */
450
450
  public dispose(): void {
451
- // 关闭所有iframe
451
+ // close all iframes
452
452
  this.iframeRegistry.forEach((iframe, spatialId) => {
453
453
  console.log(`Disposing iframe with spatialId: ${spatialId}`)
454
454
  iframe.remove()
@@ -456,7 +456,7 @@ export class PuppeteerPlatform implements PlatformAbility {
456
456
  this.iframeRegistry.clear()
457
457
  }
458
458
 
459
- // 生成UUID函数
459
+ // generate UUID function
460
460
  private generateUUID(): string {
461
461
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
462
462
  /[xy]/g,
@@ -2,6 +2,7 @@ import { SpatialObject } from '../SpatialObject'
2
2
  import {
3
3
  CreateAttachmentEntityCommand,
4
4
  UpdateAttachmentEntityCommand,
5
+ InitializeAttachmentCommand,
5
6
  } from '../JSBCommand'
6
7
  import {
7
8
  AttachmentEntityOptions,
@@ -41,5 +42,6 @@ export async function createAttachmentEntity(
41
42
  throw new Error('createAttachmentEntity failed: ' + result?.errorMessage)
42
43
  }
43
44
  const { id, windowProxy } = result.data!
45
+ await new InitializeAttachmentCommand(id, options).execute()
44
46
  return new Attachment(id, windowProxy, options)
45
47
  }
@@ -23,16 +23,16 @@ import { SpatialComponent } from '../component/SpatialComponent'
23
23
  import { SpatialWebEvent } from '../../SpatialWebEvent'
24
24
  import { createSpatialEvent } from '../../SpatialWebEventCreator'
25
25
  import {
26
- CubeInfoMsg,
27
26
  ObjectDestroyMsg,
28
27
  SpatialDragEndMsg,
29
28
  SpatialDragMsg,
29
+ SpatialDragStartMsg,
30
+ SpatialMagnifyEndMsg,
30
31
  SpatialMagnifyMsg,
31
32
  SpatialRotateEndMsg,
32
33
  SpatialRotateMsg,
33
34
  SpatialTapMsg,
34
35
  SpatialWebMsgType,
35
- TransformMsg,
36
36
  } from '../../WebMsgCommand'
37
37
 
38
38
  export class SpatialEntity extends SpatialObject {
@@ -43,6 +43,31 @@ export class SpatialEntity extends SpatialObject {
43
43
  events: Record<string, (data: any) => void> = {}
44
44
  children: SpatialEntity[] = []
45
45
  parent: SpatialEntityOrReality | null = null
46
+ private _enableInput: boolean = false
47
+
48
+ get enableInput(): boolean {
49
+ return this._enableInput
50
+ }
51
+
52
+ set enableInput(value: boolean) {
53
+ // Why enabling only 'spatialtap' makes the entity interactive:
54
+ // - On the native (Swift/RealityKit) side, SpatialEntity.updateGesture(type, isEnable)
55
+ // toggles per-gesture flags. Then enableInteractive = enableTap || enableRotate || enableDrag || enableMagnify.
56
+ // - As soon as any gesture (e.g., 'spatialtap') is enabled, enableInteractive becomes true and
57
+ // InputTargetComponent is attached, making the entity targetable by targetedToAnyEntity().
58
+ // - The view layer forwards hit gestures to the web, so enabling 'spatialtap' is sufficient to
59
+ // make the entity targetable; enable additional gestures only when needed.
60
+ if (this._enableInput === value) return
61
+ this._enableInput = value
62
+ void this.updateEntityEvent('spatialtap', value).catch(err => {
63
+ console.error('enableInput updateEntityEvent failed', 'spatialtap', err)
64
+ // Roll back local flag if the native toggle fails to keep web/native states consistent.
65
+ // Otherwise, the web side would think the entity is interactive while RealityKit is not.
66
+ if (this._enableInput === value) {
67
+ this._enableInput = !value
68
+ }
69
+ })
70
+ }
46
71
  constructor(
47
72
  id: string,
48
73
  public userData?: SpatialEntityUserData,
@@ -123,13 +148,15 @@ export class SpatialEntity extends SpatialObject {
123
148
  return new UpdateEntityEventCommand(this, eventName, isEnable).execute()
124
149
  }
125
150
  private onReceiveEvent = (
126
- data: // | CubeInfoMsg
127
- // | TransformMsg
128
- | SpatialTapMsg
129
- // | SpatialDragMsg
130
- // | SpatialDragEndMsg
131
- // | SpatialRotateMsg
132
- // | SpatialRotateEndMsg
151
+ data:
152
+ | SpatialTapMsg
153
+ | SpatialDragStartMsg
154
+ | SpatialDragMsg
155
+ | SpatialDragEndMsg
156
+ | SpatialMagnifyMsg
157
+ | SpatialMagnifyEndMsg
158
+ | SpatialRotateMsg
159
+ | SpatialRotateEndMsg
133
160
  | ObjectDestroyMsg,
134
161
  ) => {
135
162
  // console.log('SpatialEntityEvent', data)
@@ -139,27 +166,21 @@ export class SpatialEntity extends SpatialObject {
139
166
  }
140
167
  // tap
141
168
  else if (type === SpatialWebMsgType.spatialtap) {
142
- const evt = createSpatialEvent(
143
- SpatialWebMsgType.spatialtap,
144
- (data as SpatialTapMsg).detail,
145
- )
169
+ const evt = createSpatialEvent(SpatialWebMsgType.spatialtap, data.detail)
146
170
  this.dispatchEvent(evt)
147
171
  } else if (type === SpatialWebMsgType.spatialdragstart) {
148
172
  const evt = createSpatialEvent(
149
173
  SpatialWebMsgType.spatialdragstart,
150
- (data as SpatialDragMsg).detail,
174
+ data.detail,
151
175
  )
152
176
  this.dispatchEvent(evt)
153
177
  } else if (type === SpatialWebMsgType.spatialdrag) {
154
- const evt = createSpatialEvent(
155
- SpatialWebMsgType.spatialdrag,
156
- (data as SpatialDragMsg).detail,
157
- )
178
+ const evt = createSpatialEvent(SpatialWebMsgType.spatialdrag, data.detail)
158
179
  this.dispatchEvent(evt)
159
180
  } else if (type === SpatialWebMsgType.spatialdragend) {
160
181
  const evt = createSpatialEvent(
161
182
  SpatialWebMsgType.spatialdragend,
162
- (data as SpatialDragEndMsg).detail,
183
+ data.detail,
163
184
  )
164
185
  this.dispatchEvent(evt)
165
186
  }
@@ -167,13 +188,13 @@ export class SpatialEntity extends SpatialObject {
167
188
  else if (type === SpatialWebMsgType.spatialrotate) {
168
189
  const evt = createSpatialEvent(
169
190
  SpatialWebMsgType.spatialrotate,
170
- (data as SpatialRotateMsg).detail,
191
+ data.detail,
171
192
  )
172
193
  this.dispatchEvent(evt)
173
194
  } else if (type === SpatialWebMsgType.spatialrotateend) {
174
195
  const evt = createSpatialEvent(
175
196
  SpatialWebMsgType.spatialrotateend,
176
- (data as SpatialRotateEndMsg).detail,
197
+ data.detail,
177
198
  )
178
199
  this.dispatchEvent(evt)
179
200
  }
@@ -181,13 +202,13 @@ export class SpatialEntity extends SpatialObject {
181
202
  else if (type === SpatialWebMsgType.spatialmagnify) {
182
203
  const evt = createSpatialEvent(
183
204
  SpatialWebMsgType.spatialmagnify,
184
- (data as SpatialMagnifyMsg).detail,
205
+ data.detail,
185
206
  )
186
207
  this.dispatchEvent(evt)
187
208
  } else if (type === SpatialWebMsgType.spatialmagnifyend) {
188
209
  const evt = createSpatialEvent(
189
210
  SpatialWebMsgType.spatialmagnifyend,
190
- (data as SpatialMagnifyMsg).detail,
211
+ data.detail,
191
212
  )
192
213
  this.dispatchEvent(evt)
193
214
  }
@@ -200,7 +221,7 @@ export class SpatialEntity extends SpatialObject {
200
221
  }
201
222
  this.events[evt.type]?.(evt)
202
223
  if (evt.bubbles && !evt.cancelBubble) {
203
- if (this.parent && this.parent instanceof SpatialEntity) {
224
+ if (this.parent) {
204
225
  this.parent.dispatchEvent(evt)
205
226
  }
206
227
  }
@@ -1,3 +1,5 @@
1
+ import type { PhysicalMetricsValueShape } from '../physicalMetrics'
2
+
1
3
  declare global {
2
4
  declare const __WEBSPATIAL_CORE_SDK_VERSION__: string
3
5
 
@@ -28,16 +30,16 @@ declare global {
28
30
  'natvie-version'?: string
29
31
  'react-sdk-version'?: string
30
32
  'core-sdk-version'?: string
33
+ physicalMetrics?: PhysicalMetricsValueShape
31
34
  }
32
35
 
33
- innerDepth: number
34
- outerDepth: number
36
+ xrInnerDepth: number
37
+ xrOuterDepth: number
35
38
  }
36
39
 
37
40
  interface HTMLElement {
38
- offsetBack: number
39
- clientDepth: number
40
- getBoundingClientCube: () => CubeInfo | undefined
41
+ xrOffsetBack: number
42
+ xrClientDepth: number
41
43
  }
42
44
  }
43
45
 
@@ -80,6 +80,11 @@ export interface SpatializedElementProperties {
80
80
  enableRotateEndGesture: boolean
81
81
  enableMagnifyGesture: boolean
82
82
  enableMagnifyEndGesture: boolean
83
+ /**
84
+ * Optional world-space axis for spatial rotate gesture. Omitted or zero vector
85
+ * means unconstrained rotation (platform default).
86
+ */
87
+ rotateConstrainedToAxis?: Vec3
83
88
  }
84
89
 
85
90
  export interface Spatialized2DElementProperties
@@ -115,6 +120,16 @@ export interface SpatialSceneCreationOptions {
115
120
  baseplateVisibility?: BaseplateVisibilityType
116
121
  }
117
122
 
123
+ export type SpatialEntityEventType =
124
+ | 'spatialtap'
125
+ | 'spatialdragstart'
126
+ | 'spatialdrag'
127
+ | 'spatialdragend'
128
+ | 'spatialrotate'
129
+ | 'spatialrotateend'
130
+ | 'spatialmagnify'
131
+ | 'spatialmagnifyend'
132
+
118
133
  export const BaseplateVisibilityValues = [
119
134
  'automatic',
120
135
  'visible',
@@ -186,8 +201,6 @@ export interface SpatialEntityProperties {
186
201
  scale: Vec3
187
202
  }
188
203
 
189
- export type SpatialEntityEventType = 'spatialtap' //| 'drag' | 'rotate' | 'scale'
190
-
191
204
  export type SpatialGeometryType =
192
205
  | 'BoxGeometry'
193
206
  | 'PlaneGeometry'
@@ -391,6 +404,7 @@ export interface AttachmentEntityOptions {
391
404
  parentEntityId: string
392
405
  position?: [number, number, number]
393
406
  size: { width: number; height: number }
407
+ ownerViewId: string
394
408
  }
395
409
 
396
410
  export interface AttachmentEntityUpdateOptions {