@things-factory/figure-ui 10.1.161 → 10.1.162

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/figure-ui",
3
- "version": "10.1.161",
3
+ "version": "10.1.162",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "@things-factory:registry": "https://registry.npmjs.org"
@@ -47,5 +47,5 @@
47
47
  "@things-factory/setting-base": "^10.1.159",
48
48
  "three": "^0.185.1"
49
49
  },
50
- "gitHead": "d32fe0614ea40572787acd94e307cc18a681ab38"
50
+ "gitHead": "7be9e6436b89213940c684c0cdc60343e8148e73"
51
51
  }
@@ -698,7 +698,7 @@ describe('the catalogue preview', () => {
698
698
  product.forget()
699
699
  product.openDetail('a')
700
700
  })
701
- await press('figure-detail-dock >>> footer button', 'edit')
701
+ await press('figure-detail-dock >>> button[edit]', 'edit')
702
702
  await until(() => product.navigations().length, 'the modeller opened')
703
703
  assert.deepEqual(await read(() => product.navigations().map(({ path, detailShown }) => ({ path, detailShown }))), [{ path: '/figure-modeller/a', detailShown: false }])
704
704
  })
@@ -1,5 +1,5 @@
1
1
  /*
2
- * The modeller's screen in a real browser: its frame (three panes, the side's six tabs, the bar over the work), a new
2
+ * The modeller's screen in a real browser: its frame (three panes, the side's seven tabs, the bar over the work), a new
3
3
  * figure's first minute, the side panel as parts are chosen, the view tab, the preview, the verdict tab and the card's
4
4
  * picture -- each read as a person meets it, after doing what a person does (a click, a key, a narrower window).
5
5
  *
@@ -150,7 +150,7 @@ describe('the verdict tab over a figure, as the modeller page mounts it', () =>
150
150
  })
151
151
  })
152
152
 
153
- describe('the frame: parts · canvas · one side of six tabs, and the bar over the work', () => {
153
+ describe('the frame: parts · canvas · one side of seven tabs, and the bar over the work', () => {
154
154
  before(() => mount('box'))
155
155
 
156
156
  test('three panes: the parts list on the left, the canvas in the middle, the side on the right -- one side, nothing under the canvas', async () => {
@@ -185,13 +185,13 @@ describe('the frame: parts · canvas · one side of six tabs, and the bar over t
185
185
  assert.equal(await read(() => getComputedStyle(screenCheck.editor()).containerType), 'inline-size')
186
186
  })
187
187
 
188
- test('the side\'s six tabs, in order, each saying its name under its icon -- the name it is read by', async () => {
188
+ test('the side\'s seven tabs, in order, each saying its name under its icon -- the name it is read by', async () => {
189
189
  const tabs = await read(() =>
190
190
  [...screenCheck.editor().shadowRoot.querySelectorAll('aside button[tab]')].map(b => ({ label: b.getAttribute('aria-label'), shows: b.querySelector('span[tab-label]')?.textContent.trim() }))
191
191
  )
192
192
  assert.deepEqual(
193
193
  tabs.map(t => t.label),
194
- ['properties', 'capability', 'motion', 'verdict', 'view', 'history'].map(TAB)
194
+ ['properties', 'capability', 'motion', 'data', 'verdict', 'view', 'history'].map(TAB)
195
195
  )
196
196
  for (const tab of tabs) assert.equal(tab.shows, tab.label, `${tab.label} shows its name`)
197
197
  assert.equal(await read(() => screenCheck.editor().shadowRoot.querySelector('aside').firstElementChild.matches('div[tabs]')), true, 'the tabs are the side\'s first row')
@@ -200,15 +200,15 @@ describe('the frame: parts · canvas · one side of six tabs, and the bar over t
200
200
  test('a tab shows its panel and hides the others without taking them down -- the verdict keeps what it holds', async () => {
201
201
  const panels = () =>
202
202
  read(() => [...screenCheck.editor().shadowRoot.querySelectorAll('aside > div[panel]')].map(p => ({ seen: p.getBoundingClientRect().height > 0, connected: p.isConnected, verdict: !!p.querySelector('slot[name=verdict]') })))
203
- const tabs = ['properties', 'capability', 'motion', 'verdict', 'view', 'history']
203
+ const tabs = ['properties', 'capability', 'motion', 'data', 'verdict', 'view', 'history']
204
204
  for (const [at, tab] of tabs.entries()) {
205
205
  await pressTab(TAB(tab))
206
206
  await until(`${tab} on`, name => screenCheck.editor().shadowRoot.querySelector('button[tab][on]')?.getAttribute('aria-label') === name, TAB(tab))
207
207
  const now = await panels()
208
- assert.equal(now.length, 6, 'six panels, all standing')
208
+ assert.equal(now.length, 7, 'seven panels, all standing')
209
209
  assert.deepEqual(now.map(p => p.seen), tabs.map((_, i) => i === at), `${tab}: its panel seen, the rest hidden`)
210
210
  assert.ok(now.every(p => p.connected))
211
- assert.ok(now[3].verdict, 'the verdict panel holds the verdict slot whichever tab is on')
211
+ assert.ok(now[tabs.indexOf('verdict')].verdict, 'the verdict panel holds the verdict slot whichever tab is on')
212
212
  }
213
213
  await pressTab(TAB('properties'))
214
214
  })
@@ -13,7 +13,7 @@ import { editFigurePart, quadrupedRobotFigure, robotDogFigure } from '@hatiolab/
13
13
  import type { FigureAsset, FigureDataProperty } from '@hatiolab/figure-model'
14
14
 
15
15
  import { clipGroupsOf, freeGroupIdOf, groupEdit, undrivenOf } from '../client/modeller/clips.ts'
16
- import { connectionDraftOf, dataSampleOf, dataTargetsOf, freeDataIdOf, propertyDraftOf, sampleValueOf } from '../client/modeller/data-interface.ts'
16
+ import { connectionDraftOf, dataSampleOf, dataTargetsOf, freeDataIdOf, motionWrittenByData, propertyDraftOf, sampleValueOf } from '../client/modeller/data-interface.ts'
17
17
  import { editRefusalOf } from '../client/modeller/refusals.ts'
18
18
 
19
19
  const read = (path: string) => readFileSync(fileURLToPath(new URL(path, import.meta.url)), 'utf8')
@@ -105,6 +105,21 @@ test('데이터 · 동작 거절은 커널 문장이 아니라 reason 과 값으
105
105
  assert.match(refusalOf(() => editFigurePart(lone, { kind: 'set-driver', state: 'fl-knee', clip: 'run', duration: 1, keys: [{ seconds: 0, value: 0 }, { seconds: 1, value: 10 }] })), /^figure\.text\.driver-writers /)
106
106
  })
107
107
 
108
+ test('미리보기: 데이터가 정하는 그룹 필드는 그룹 칸이 아니라 데이터 값으로 움직인다 (2026-10-14, 로봇 개)', () => {
109
+ /* The dog's clip and speed are both written by data (mode, cadence): a clip chosen on the group moved nothing. */
110
+ assert.deepEqual(motionWrittenByData(DOG), { locomotion: { clip: true, speed: true } })
111
+ /* Only the speed connected: the clip stays the group's to choose. */
112
+ const speedOnly = { ...DOG, dataInterface: { ...DOG.dataInterface!, connections: DOG.dataInterface!.connections.filter(c => c.target.kind !== 'motion-clip') } }
113
+ assert.deepEqual(motionWrittenByData(speedOnly), { locomotion: { clip: false, speed: true } })
114
+ /* No data interface: every group field is the group's own. */
115
+ const { dataInterface: _gone, ...bare } = DOG
116
+ assert.deepEqual(motionWrittenByData(bare as FigureAsset), {})
117
+ const preview = read('../client/modeller/figure-preview.ts')
118
+ assert.match(preview, /tellInstances\('figureValues', this\.valuing\)/)
119
+ assert.match(preview, /byData\.clip\s*\? nothing/)
120
+ assert.match(preview, /byData\.speed\s*\? nothing/)
121
+ })
122
+
108
123
  test('데이터 탭 · 클립 패널 · 미리보기의 배선과 문구', () => {
109
124
  const editor = read('../client/modeller/editor.ts')
110
125
  assert.match(editor, /renderTab\('data'/)