@things-factory/figure-ui 10.1.76 → 10.1.78

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 (139) hide show
  1. package/client/modeller/editor.ts +42 -23
  2. package/dist-client/modeller/editor.d.ts +9 -0
  3. package/dist-client/modeller/editor.js +50 -24
  4. package/dist-client/modeller/editor.js.map +1 -1
  5. package/dist-client/tsconfig.tsbuildinfo +1 -1
  6. package/package.json +2 -2
  7. package/test/browser/modeller.check.mjs +18 -12
  8. package/test/links.test.ts +1 -1
  9. package/test/parts-wiring.test.ts +13 -0
  10. package/test/refusals.test.ts +6 -3
  11. package/test/theme.test.ts +2 -4
  12. package/translations/en.json +2 -2
  13. package/translations/ja.json +2 -2
  14. package/translations/ko.json +2 -2
  15. package/translations/ms.json +2 -2
  16. package/translations/zh.json +2 -2
  17. package/dist-client/graphql/v3-store.d.ts +0 -3
  18. package/dist-client/graphql/v3-store.js +0 -39
  19. package/dist-client/graphql/v3-store.js.map +0 -1
  20. package/dist-client/modeller/figure-v3-capability.d.ts +0 -22
  21. package/dist-client/modeller/figure-v3-capability.js +0 -141
  22. package/dist-client/modeller/figure-v3-capability.js.map +0 -1
  23. package/dist-client/modeller/figure-v3-clips.d.ts +0 -31
  24. package/dist-client/modeller/figure-v3-clips.js +0 -268
  25. package/dist-client/modeller/figure-v3-clips.js.map +0 -1
  26. package/dist-client/modeller/figure-v3-controls.d.ts +0 -36
  27. package/dist-client/modeller/figure-v3-controls.js +0 -226
  28. package/dist-client/modeller/figure-v3-controls.js.map +0 -1
  29. package/dist-client/modeller/figure-v3-first-save.d.ts +0 -20
  30. package/dist-client/modeller/figure-v3-first-save.js +0 -118
  31. package/dist-client/modeller/figure-v3-first-save.js.map +0 -1
  32. package/dist-client/modeller/figure-v3-links.d.ts +0 -77
  33. package/dist-client/modeller/figure-v3-links.js +0 -649
  34. package/dist-client/modeller/figure-v3-links.js.map +0 -1
  35. package/dist-client/modeller/figure-v3-material.d.ts +0 -39
  36. package/dist-client/modeller/figure-v3-material.js +0 -323
  37. package/dist-client/modeller/figure-v3-material.js.map +0 -1
  38. package/dist-client/modeller/figure-v3-parts.d.ts +0 -69
  39. package/dist-client/modeller/figure-v3-parts.js +0 -559
  40. package/dist-client/modeller/figure-v3-parts.js.map +0 -1
  41. package/dist-client/modeller/figure-v3-preview.d.ts +0 -34
  42. package/dist-client/modeller/figure-v3-preview.js +0 -346
  43. package/dist-client/modeller/figure-v3-preview.js.map +0 -1
  44. package/dist-client/modeller/figure-v3-properties.d.ts +0 -45
  45. package/dist-client/modeller/figure-v3-properties.js +0 -239
  46. package/dist-client/modeller/figure-v3-properties.js.map +0 -1
  47. package/dist-client/modeller/figure-v3-recipe-catalog.d.ts +0 -38
  48. package/dist-client/modeller/figure-v3-recipe-catalog.js +0 -399
  49. package/dist-client/modeller/figure-v3-recipe-catalog.js.map +0 -1
  50. package/dist-client/modeller/figure-v3-verdict.d.ts +0 -64
  51. package/dist-client/modeller/figure-v3-verdict.js +0 -787
  52. package/dist-client/modeller/figure-v3-verdict.js.map +0 -1
  53. package/dist-client/modeller/v3-ai-proposal.d.ts +0 -51
  54. package/dist-client/modeller/v3-ai-proposal.js +0 -64
  55. package/dist-client/modeller/v3-ai-proposal.js.map +0 -1
  56. package/dist-client/modeller/v3-align.d.ts +0 -30
  57. package/dist-client/modeller/v3-align.js +0 -75
  58. package/dist-client/modeller/v3-align.js.map +0 -1
  59. package/dist-client/modeller/v3-canvas.d.ts +0 -72
  60. package/dist-client/modeller/v3-canvas.js +0 -379
  61. package/dist-client/modeller/v3-canvas.js.map +0 -1
  62. package/dist-client/modeller/v3-clips.d.ts +0 -39
  63. package/dist-client/modeller/v3-clips.js +0 -58
  64. package/dist-client/modeller/v3-clips.js.map +0 -1
  65. package/dist-client/modeller/v3-controls.d.ts +0 -28
  66. package/dist-client/modeller/v3-controls.js +0 -28
  67. package/dist-client/modeller/v3-controls.js.map +0 -1
  68. package/dist-client/modeller/v3-editor.d.ts +0 -203
  69. package/dist-client/modeller/v3-editor.js +0 -1526
  70. package/dist-client/modeller/v3-editor.js.map +0 -1
  71. package/dist-client/modeller/v3-fields.d.ts +0 -45
  72. package/dist-client/modeller/v3-fields.js +0 -83
  73. package/dist-client/modeller/v3-fields.js.map +0 -1
  74. package/dist-client/modeller/v3-figure-facts.d.ts +0 -35
  75. package/dist-client/modeller/v3-figure-facts.js +0 -55
  76. package/dist-client/modeller/v3-figure-facts.js.map +0 -1
  77. package/dist-client/modeller/v3-first-save.d.ts +0 -17
  78. package/dist-client/modeller/v3-first-save.js +0 -26
  79. package/dist-client/modeller/v3-first-save.js.map +0 -1
  80. package/dist-client/modeller/v3-gizmo.d.ts +0 -72
  81. package/dist-client/modeller/v3-gizmo.js +0 -111
  82. package/dist-client/modeller/v3-gizmo.js.map +0 -1
  83. package/dist-client/modeller/v3-help.d.ts +0 -18
  84. package/dist-client/modeller/v3-help.js +0 -37
  85. package/dist-client/modeller/v3-help.js.map +0 -1
  86. package/dist-client/modeller/v3-joint-drag.d.ts +0 -19
  87. package/dist-client/modeller/v3-joint-drag.js +0 -97
  88. package/dist-client/modeller/v3-joint-drag.js.map +0 -1
  89. package/dist-client/modeller/v3-joint-fan.d.ts +0 -9
  90. package/dist-client/modeller/v3-joint-fan.js +0 -276
  91. package/dist-client/modeller/v3-joint-fan.js.map +0 -1
  92. package/dist-client/modeller/v3-keys.d.ts +0 -22
  93. package/dist-client/modeller/v3-keys.js +0 -26
  94. package/dist-client/modeller/v3-keys.js.map +0 -1
  95. package/dist-client/modeller/v3-links.d.ts +0 -267
  96. package/dist-client/modeller/v3-links.js +0 -480
  97. package/dist-client/modeller/v3-links.js.map +0 -1
  98. package/dist-client/modeller/v3-live.d.ts +0 -37
  99. package/dist-client/modeller/v3-live.js +0 -68
  100. package/dist-client/modeller/v3-live.js.map +0 -1
  101. package/dist-client/modeller/v3-maturity.d.ts +0 -120
  102. package/dist-client/modeller/v3-maturity.js +0 -313
  103. package/dist-client/modeller/v3-maturity.js.map +0 -1
  104. package/dist-client/modeller/v3-occupancy.d.ts +0 -18
  105. package/dist-client/modeller/v3-occupancy.js +0 -24
  106. package/dist-client/modeller/v3-occupancy.js.map +0 -1
  107. package/dist-client/modeller/v3-palette.d.ts +0 -7
  108. package/dist-client/modeller/v3-palette.js +0 -46
  109. package/dist-client/modeller/v3-palette.js.map +0 -1
  110. package/dist-client/modeller/v3-preview-model.d.ts +0 -47
  111. package/dist-client/modeller/v3-preview-model.js +0 -68
  112. package/dist-client/modeller/v3-preview-model.js.map +0 -1
  113. package/dist-client/modeller/v3-product-session.d.ts +0 -510
  114. package/dist-client/modeller/v3-product-session.js +0 -157
  115. package/dist-client/modeller/v3-product-session.js.map +0 -1
  116. package/dist-client/modeller/v3-recipe-source.d.ts +0 -8
  117. package/dist-client/modeller/v3-recipe-source.js +0 -14
  118. package/dist-client/modeller/v3-recipe-source.js.map +0 -1
  119. package/dist-client/modeller/v3-recipes.d.ts +0 -37
  120. package/dist-client/modeller/v3-recipes.js +0 -103
  121. package/dist-client/modeller/v3-recipes.js.map +0 -1
  122. package/dist-client/modeller/v3-refusals.d.ts +0 -68
  123. package/dist-client/modeller/v3-refusals.js +0 -142
  124. package/dist-client/modeller/v3-refusals.js.map +0 -1
  125. package/dist-client/modeller/v3-renderer.d.ts +0 -40
  126. package/dist-client/modeller/v3-renderer.js +0 -73
  127. package/dist-client/modeller/v3-renderer.js.map +0 -1
  128. package/dist-client/modeller/v3-stage-model.d.ts +0 -39
  129. package/dist-client/modeller/v3-stage-model.js +0 -147
  130. package/dist-client/modeller/v3-stage-model.js.map +0 -1
  131. package/dist-client/modeller/v3-thumbnail.d.ts +0 -3
  132. package/dist-client/modeller/v3-thumbnail.js +0 -75
  133. package/dist-client/modeller/v3-thumbnail.js.map +0 -1
  134. package/dist-client/pages/figure-v3-modeller-page.d.ts +0 -102
  135. package/dist-client/pages/figure-v3-modeller-page.js +0 -548
  136. package/dist-client/pages/figure-v3-modeller-page.js.map +0 -1
  137. package/dist-client/tsconfig.chk.tsbuildinfo +0 -1
  138. package/dist-client/tsconfig.fui.tsbuildinfo +0 -1
  139. package/dist-client/tsconfig.scratch-fm.tsbuildinfo +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/figure-ui",
3
- "version": "10.1.76",
3
+ "version": "10.1.78",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "@things-factory:registry": "https://registry.npmjs.org"
@@ -44,5 +44,5 @@
44
44
  "@things-factory/figure-service": "^10.1.76",
45
45
  "three": "^0.185.1"
46
46
  },
47
- "gitHead": "66a70458dfea95fe8c835067f0fd857b38736203"
47
+ "gitHead": "88661a25f49880139a5e05554861acdd691e44e5"
48
48
  }
@@ -432,6 +432,11 @@ describe('a figure made in the modeller is given its size from its parts (user 2
432
432
  assert.ok(await width.evaluate(s => !!s), 'the width can follow the figure size')
433
433
  await width.select('instance:x')
434
434
  await sleep(600)
435
+ /* Refusals and notices go to the app's notification: heard here as the event it is. */
436
+ await page().evaluate(() => {
437
+ window.__told = []
438
+ document.addEventListener('notify', e => window.__told.push(e.detail))
439
+ })
435
440
  const plus = await handle('input')
436
441
  assert.ok(await plus.evaluate(i => !!i), 'a following width offers its difference')
437
442
  await plus.evaluate(i => i.scrollIntoView({ block: 'center' }))
@@ -441,15 +446,15 @@ describe('a figure made in the modeller is given its size from its parts (user 2
441
446
  await sleep(600)
442
447
  /* The declared sizes start at 1 mm: less 50 the box would be gone below 50, so the link is refused -- with the way on. */
443
448
  assert.match((await state()).error, /figure.text.link-needs-smaller-range/, 'refused, said in words')
444
- const narrow = await page().evaluateHandle(() => {
445
- const deep = (root = document) => [...root.querySelectorAll('button')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
446
- return deep().find(b => b.textContent.includes('figure.button.narrow-range-from')) ?? null
449
+ const told = await read(() => {
450
+ const last = window.__told.at(-1)
451
+ return last && { level: last.level, message: last.message, label: last.option?.action?.label }
447
452
  })
448
- assert.ok(await narrow.evaluate(b => !!b), 'the same link is offered with the sizes narrowed')
449
- await click(await narrow.evaluate(b => {
450
- const r = b.getBoundingClientRect()
451
- return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
452
- }))
453
+ assert.equal(told?.level, 'error', 'said in the app\'s notification')
454
+ assert.match(told?.message ?? '', /figure.text.link-needs-smaller-range/)
455
+ assert.match(told?.label ?? '', /figure.button.narrow-range-from/, 'the same link is offered with the sizes narrowed')
456
+ await read(() => window.__told.at(-1).option.action.callback())
457
+ await sleep(600)
453
458
  assert.equal((await state()).error, '', 'taken')
454
459
  assert.equal((await body('box')).dimensions.width, 350, 'the figure\'s 400 less 50')
455
460
  })
@@ -658,16 +663,17 @@ describe('a part follows the figure size in one step (follow-size, 2026-09-26)',
658
663
  return { x: r.left + r.width / 2, y: r.top + r.height / 2 }
659
664
  })
660
665
  assert.ok(at, 'the button is offered for a box that follows nothing')
666
+ await page().evaluate(() => {
667
+ window.__told = []
668
+ document.addEventListener('notify', e => window.__told.push(e.detail))
669
+ })
661
670
  const start = await state()
662
671
  await click(at)
663
672
  const after = await state()
664
673
  assert.equal(after.error, '', 'not refused')
665
674
  assert.equal(after.revision, start.revision + 1, 'one step')
666
675
  assert.deepEqual(await read(() => probe.fields('size.')), { 'size.x': 400, 'size.y': 400, 'size.z': 400 }, 'the size is the box as it stands')
667
- const notice = await page().evaluate(() => {
668
- const deep = (root = document) => [...root.querySelectorAll('.notice')].concat(...[...root.querySelectorAll('*')].filter(e => e.shadowRoot).map(e => deep(e.shadowRoot)))
669
- return deep()[0]?.textContent?.trim() ?? null
670
- })
676
+ const notice = await read(() => (window.__told ?? []).filter(n => n.level === 'info').at(-1)?.message ?? null)
671
677
  assert.ok(notice?.includes('figure.text.size-declared-to-follow'), `a line says so: ${notice}`)
672
678
  const grown = await read(() => {
673
679
  const editor = document.querySelector('figure-editor')
@@ -614,7 +614,7 @@ test('치수 칸 — 「도형 크기를 따라감」 단추는 follow-size 한
614
614
  assert.ok(dims.includes("followOf(asset, this.partId, axis).of === 'instance'"), '다 따르면 단추가 없다')
615
615
  const editor = read('../client/modeller/editor.ts')
616
616
  assert.ok(editor.includes("if (action.kind === 'follow-size' && !hadSize)"), '크기를 대신 정했을 때만')
617
- assert.ok(editor.includes('<div role="status" class="notice">'))
617
+ assert.ok(editor.includes("notify({ level: 'info', message: this.notice })"), '그 한 줄은 앱의 알림으로')
618
618
  const ko = JSON.parse(read('../translations/ko.json'))
619
619
  assert.match(ko['figure.text.size-declared-to-follow'], /\{x\} × \{y\} × \{z\} mm/)
620
620
  })
@@ -72,3 +72,16 @@ test('부품 이름은 figure-model 의 rename-part 로 바꾸고, 거절은 까
72
72
  for (const key of ['figure.text.part-name-taken', 'figure.text.part-name-invalid']) assert.ok(table[key]?.includes('{name}'), `${lang}: ${key}`)
73
73
  }
74
74
  })
75
+
76
+ test('거절은 앱의 알림(notify)이나 확인 창(OxPrompt)으로 — 모델러가 따로 그리는 띠는 없다 (2026-09-27)', () => {
77
+ assert.doesNotMatch(editor, /role="alert" class="error"/, '따로 그리던 빨간 띠가 없다')
78
+ assert.doesNotMatch(editor, /class="notice"/, '따로 그리던 알림 줄이 없다')
79
+ const tell = editor.slice(editor.indexOf(' private tell('), editor.indexOf(' protected willUpdate('))
80
+ assert.match(tell, /else notify\(\{ level: 'error', message: this\.error \}\)/, '길이 없는 거절은 error 알림 — 닫을 때까지 남는다')
81
+ assert.match(tell, /if \(retry\) this\.ask\(this\.error, retry\)/, '이어 할 길이 있으면 묻는다')
82
+ const ask = editor.slice(editor.indexOf(' private async ask('), editor.indexOf(' protected willUpdate('))
83
+ assert.match(ask, /OxPrompt\.open\(\{[\s\S]*confirmButton: \{ text: retry\.label \}/, '앱의 확인 창에서, 그 길의 이름으로')
84
+ assert.match(ask, /if \(!agreed\) return[\s\S]*retry\.run\(\)/, '확인하면 바로 진행한다')
85
+ assert.match(tell, /notify\(\{ level: 'info', message: this\.notice \}\)/, '지우며 풀어 준 것은 info 로')
86
+ assert.match(editor, /updated\(changed: PropertyValues<this>\) \{\s*super\.updated\?\.\(changed\)\s*this\.tell\(changed\)/)
87
+ })
@@ -98,7 +98,7 @@ test('지우기는 매단 것 · 따라 움직이는 것을 풀어 남기고 그
98
98
  assert.doesNotMatch(unknown, /something in English/)
99
99
  })
100
100
 
101
- test('공간 선언에 든 부품 — 지우고 공간을 남은 부품에 맞추는 길을 한 번에 권한다(차단기의 떠도는 상자, 09-25)', async () => {
101
+ test('공간 선언에 든 부품 — 한 번 눌러 지우고, 공간은 남은 부품에 다시 맞추며 그렇다고 한 줄 말한다(묻지 않는다, 2026-09-27)', async () => {
102
102
  const { inDeclaredRoom } = await import('../client/modeller/refusals.ts')
103
103
  const fm = await import('@hatiolab/figure-model')
104
104
  let a = fm.addFigureBox(fm.createFigureAsset('Gate'), { id: 'housing', size: { x: 300, y: 1000, z: 300 }, position: { x: 0, y: 0, z: 0 } })
@@ -118,8 +118,11 @@ test('공간 선언에 든 부품 — 지우고 공간을 남은 부품에 맞
118
118
  assert.equal(after.document.model.nodes.some(n => n.id === 'stray'), false)
119
119
  assert.ok(after.occupancy?.bounds, '공간은 남은 부품으로 다시 선언된다')
120
120
  const editor = readFileSync(fileURLToPath(new URL('../client/modeller/editor.ts', import.meta.url)), 'utf8')
121
- assert.ok(editor.includes("if (!refitRoom && inDeclaredRoom(error)) this.retry = { label: i18next.t('figure.button.remove-and-refit-room', { part: id }), run: () => this.removePart(id, true) }"))
122
- assert.ok(ko['figure.button.remove-and-refit-room'].includes('{part}'))
121
+ const remove = editor.slice(editor.indexOf(' private removePart('), editor.indexOf(' private toggleHiddenPart('))
122
+ assert.match(remove, /if \(!refitRoom && inDeclaredRoom\(error\)\) \{\s*this\.removePart\(id, true\)/, '거절 없이 곧바로 다시 맞춰 지운다')
123
+ assert.match(remove, /figure\.text\.removal-space-refitted/, '그렇다고 한 줄')
124
+ assert.doesNotMatch(remove, /this\.retry = \{/, '묻거나 권하지 않는다')
125
+ assert.ok(ko['figure.text.removal-space-refitted'])
123
126
  })
124
127
 
125
128
  test('배치를 바꿀 수 없는 까닭 — 평면에 붙은 부품과 새 평면이 받는 면을 말한다(벽 배치, 09-25)', async () => {
@@ -30,14 +30,12 @@ test('캔버스 바탕은 작업대 회색 — 판의 하늘색이라 환경 탭
30
30
  assert.match(read('../client/modeller/figure-view.ts'), /skyColor: WORKBENCH/)
31
31
  })
32
32
 
33
- test('눌린 단추와 오류 칸은 셸이 두 테마 대비를 맞춘 칸을 쓴다', () => {
33
+ test('눌린 단추는 셸이 두 테마 대비를 맞춘 칸을 쓴다 — 오류는 앱의 알림이 그린다 (2026-09-27)', () => {
34
34
  const pressed = editor.slice(editor.indexOf("button[aria-pressed='true']"), editor.indexOf('}', editor.indexOf("button[aria-pressed='true']")))
35
35
  assert.match(pressed, /--md-sys-color-secondary-container\)/)
36
36
  assert.match(pressed, /--md-sys-color-on-secondary-container\)/)
37
37
  assert.match(pressed, /border-color: var\(--md-sys-color-primary\)/)
38
- const error = editor.slice(editor.indexOf('.error {'), editor.indexOf('}', editor.indexOf('.error {')))
39
- assert.match(error, /--md-sys-color-error-container\)/)
40
- assert.match(error, /--md-sys-color-on-error-container\)/)
38
+ assert.equal(editor.includes('.error {'), false, '모델러가 따로 칠하는 오류 칸이 없다')
41
39
  })
42
40
 
43
41
  test('편집기는 창이 아니라 자기 폭으로 접는다 — 레일이 폭을 먼저 가져간다', () => {
@@ -640,7 +640,6 @@
640
640
  "figure.text.proportion-to-follow": "{count} lengths still have their own number. made to follow, they grow by the figure's ratio.",
641
641
  "figure.button.follow-size-in-proportion": "make every part follow",
642
642
  "figure.text.proportion-kept": "keeps its own number: {what}",
643
- "figure.button.remove-and-refit-room": "delete {part} and fit the space to the parts left",
644
643
  "figure.text.preview-clamped": "{axis} {asked} → {used}",
645
644
  "figure.label.placement-wall": "wall (wall)",
646
645
  "figure.text.placement-plane-refused": "{parts} cannot go on a {placement} by the face fastened to the mounting plane. detach it or fasten it by its {faces} first.",
@@ -663,5 +662,6 @@
663
662
  "figure.text.removal-mounting-face": "The mounting face is released.",
664
663
  "figure.text.edit-refused": "This edit cannot be made. ({code})",
665
664
  "figure.text.edit-refused-node-limit": "The figure is too large to add more. It holds up to {max} nodes.",
666
- "figure.text.recipe-step-refused": "Stopped at step {step} of the recipe. {why} ({code})"
665
+ "figure.text.recipe-step-refused": "Stopped at step {step} of the recipe. {why} ({code})",
666
+ "figure.text.removal-space-refitted": "The space is fitted to the parts left."
667
667
  }
@@ -640,7 +640,6 @@
640
640
  "figure.text.proportion-to-follow": "長さ {count} 個がまだ固有の数値です。追従させると図形と同じ比率で大きくなります。",
641
641
  "figure.button.follow-size-in-proportion": "すべての部品を追従させる",
642
642
  "figure.text.proportion-kept": "固有の数値のまま: {what}",
643
- "figure.button.remove-and-refit-room": "「{part}」を削除し、空間を残りの部品に合わせる",
644
643
  "figure.text.preview-clamped": "{axis} {asked} → {used}",
645
644
  "figure.label.placement-wall": "壁基準 (wall)",
646
645
  "figure.text.placement-plane-refused": "{parts} は取付平面に付いている面では {placement} に付けられません。外すか {faces} 面で付け直してから変更してください。",
@@ -663,5 +662,6 @@
663
662
  "figure.text.removal-mounting-face": "取付面が外れました。",
664
663
  "figure.text.edit-refused": "この編集はできません。({code})",
665
664
  "figure.text.edit-refused-node-limit": "図形が大きいためこれ以上追加できません。ノードは {max} 個までです。",
666
- "figure.text.recipe-step-refused": "レシピの {step} 番目の手順で止まりました。{why} ({code})"
665
+ "figure.text.recipe-step-refused": "レシピの {step} 番目の手順で止まりました。{why} ({code})",
666
+ "figure.text.removal-space-refitted": "空間を残りの部品に合わせ直しました。"
667
667
  }
@@ -640,7 +640,6 @@
640
640
  "figure.text.proportion-to-follow": "길이 {count}개가 아직 자기 숫자입니다. 따라가게 하면 도형과 같은 비율로 커집니다.",
641
641
  "figure.button.follow-size-in-proportion": "모든 부품이 따라가게",
642
642
  "figure.text.proportion-kept": "자기 숫자로 남는 것: {what}",
643
- "figure.button.remove-and-refit-room": "'{part}' 지우고 공간을 남은 부품에 맞추기",
644
643
  "figure.text.preview-clamped": "{axis} {asked} → {used}",
645
644
  "figure.label.placement-wall": "벽 기반 (wall)",
646
645
  "figure.text.placement-plane-refused": "{parts} 가 장착 평면에 붙은 면으로는 {placement} 에 붙일 수 없습니다. 떼거나 {faces} 면으로 다시 붙인 뒤 바꾸세요.",
@@ -663,5 +662,6 @@
663
662
  "figure.text.removal-mounting-face": "장착면이 풀렸습니다.",
664
663
  "figure.text.edit-refused": "이 편집은 할 수 없습니다. ({code})",
665
664
  "figure.text.edit-refused-node-limit": "도형이 커서 더 넣을 수 없습니다. 노드는 {max} 개까지입니다.",
666
- "figure.text.recipe-step-refused": "레시피의 {step} 번째 단계에서 막혔습니다. {why} ({code})"
665
+ "figure.text.recipe-step-refused": "레시피의 {step} 번째 단계에서 막혔습니다. {why} ({code})",
666
+ "figure.text.removal-space-refitted": "공간을 남은 부품에 다시 맞췄습니다."
667
667
  }
@@ -640,7 +640,6 @@
640
640
  "figure.text.proportion-to-follow": "{count} panjang masih mempunyai nombor sendiri. apabila dijadikan mengikut, ia membesar mengikut nisbah rajah.",
641
641
  "figure.button.follow-size-in-proportion": "jadikan semua bahagian mengikut",
642
642
  "figure.text.proportion-kept": "kekal dengan nombor sendiri: {what}",
643
- "figure.button.remove-and-refit-room": "padam '{part}' dan muatkan ruang pada bahagian yang tinggal",
644
643
  "figure.text.preview-clamped": "{axis} {asked} → {used}",
645
644
  "figure.label.placement-wall": "dinding (wall)",
646
645
  "figure.text.placement-plane-refused": "{parts} tidak boleh dipasang pada {placement} dengan muka yang dipasang pada satah pemasangan. tanggalkan atau pasang semula dengan muka {faces} dahulu.",
@@ -663,5 +662,6 @@
663
662
  "figure.text.removal-mounting-face": "Muka pemasangan dilepaskan.",
664
663
  "figure.text.edit-refused": "Suntingan ini tidak boleh dibuat. ({code})",
665
664
  "figure.text.edit-refused-node-limit": "Rajah terlalu besar untuk ditambah. Ia memuatkan sehingga {max} nod.",
666
- "figure.text.recipe-step-refused": "Terhenti pada langkah {step} resipi. {why} ({code})"
665
+ "figure.text.recipe-step-refused": "Terhenti pada langkah {step} resipi. {why} ({code})",
666
+ "figure.text.removal-space-refitted": "Ruang dipadankan semula dengan bahagian yang tinggal."
667
667
  }
@@ -640,7 +640,6 @@
640
640
  "figure.text.proportion-to-follow": "还有 {count} 个长度是自身数值。设为跟随后,将按图形的同一比例变大。",
641
641
  "figure.button.follow-size-in-proportion": "让所有部件跟随",
642
642
  "figure.text.proportion-kept": "保留自身数值: {what}",
643
- "figure.button.remove-and-refit-room": "删除 '{part}' 并使空间适配剩余部件",
644
643
  "figure.text.preview-clamped": "{axis} {asked} → {used}",
645
644
  "figure.label.placement-wall": "墙面基准 (wall)",
646
645
  "figure.text.placement-plane-refused": "{parts} 贴在安装平面上的面不能用于 {placement}。请先拆下或改用 {faces} 面贴合后再更改。",
@@ -663,5 +662,6 @@
663
662
  "figure.text.removal-mounting-face": "安装面已解除。",
664
663
  "figure.text.edit-refused": "无法进行此编辑。({code})",
665
664
  "figure.text.edit-refused-node-limit": "图形过大,无法再添加。节点上限为 {max} 个。",
666
- "figure.text.recipe-step-refused": "配方在第 {step} 步停止。{why} ({code})"
665
+ "figure.text.recipe-step-refused": "配方在第 {step} 步停止。{why} ({code})",
666
+ "figure.text.removal-space-refitted": "空间已重新贴合剩余部件。"
667
667
  }
@@ -1,3 +0,0 @@
1
- import type { V3ProductStore } from '../modeller/v3-product-session.js';
2
- /** V3 source is sent intact. No conversion into the old parts/base authoring representation. */
3
- export declare const v3ProductStore: V3ProductStore;
@@ -1,39 +0,0 @@
1
- import gql from 'graphql-tag';
2
- import { client } from '@operato/graphql';
3
- import { fetchFigure } from './index.js';
4
- /** V3 source is sent intact. No conversion into the old parts/base authoring representation. */
5
- export const v3ProductStore = {
6
- async load(id) {
7
- const record = await fetchFigure(id);
8
- if (!record?.source)
9
- throw Error('Figure 정본이 없습니다.');
10
- return { id: record.id, source: record.source };
11
- },
12
- async save(request) {
13
- const response = await client.mutate({
14
- mutation: gql `
15
- mutation SaveFigureV3($source: String!, $id: String, $expectedSource: String, $creationId: String, $thumbnail: String, $type: String) {
16
- saveFigureV3(
17
- source: $source
18
- id: $id
19
- expectedSource: $expectedSource
20
- creationId: $creationId
21
- thumbnail: $thumbnail
22
- type: $type
23
- ) {
24
- id
25
- source
26
- }
27
- }
28
- `,
29
- variables: request
30
- });
31
- if (response.errors?.length)
32
- throw Error(response.errors.map(e => e.message).join('\n'));
33
- const result = response.data?.saveFigureV3;
34
- if (!result?.id || !result.source)
35
- throw Error('서버가 저장 결과를 돌려주지 않았습니다.');
36
- return result;
37
- }
38
- };
39
- //# sourceMappingURL=v3-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"v3-store.js","sourceRoot":"","sources":["../../client/graphql/v3-store.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGxC,gGAAgG;AAChG,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,KAAK,CAAC,IAAI,CAAC,EAAE;QACX,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,MAAM,EAAE,MAAM;YAAE,MAAM,KAAK,CAAC,kBAAkB,CAAC,CAAA;QACpD,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;IACjD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,OAAO;QAChB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;;;;;;;;OAcZ;YACD,SAAS,EAAE,OAAO;SACnB,CAAC,CAAA;QACF,IAAI,QAAQ,CAAC,MAAM,EAAE,MAAM;YAAE,MAAM,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACxF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,YAA2C,CAAA;QACzE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,MAAM,KAAK,CAAC,wBAAwB,CAAC,CAAA;QACxE,OAAO,MAAM,CAAA;IACf,CAAC;CACF,CAAA","sourcesContent":["import gql from 'graphql-tag'\nimport { client } from '@operato/graphql'\nimport { fetchFigure } from './index.js'\nimport type { V3ProductStore, V3ProductRecord } from '../modeller/v3-product-session.js'\n\n/** V3 source is sent intact. No conversion into the old parts/base authoring representation. */\nexport const v3ProductStore: V3ProductStore = {\n async load(id) {\n const record = await fetchFigure(id)\n if (!record?.source) throw Error('Figure 정본이 없습니다.')\n return { id: record.id, source: record.source }\n },\n async save(request) {\n const response = await client.mutate({\n mutation: gql`\n mutation SaveFigureV3($source: String!, $id: String, $expectedSource: String, $creationId: String, $thumbnail: String, $type: String) {\n saveFigureV3(\n source: $source\n id: $id\n expectedSource: $expectedSource\n creationId: $creationId\n thumbnail: $thumbnail\n type: $type\n ) {\n id\n source\n }\n }\n `,\n variables: request\n })\n if (response.errors?.length) throw Error(response.errors.map(e => e.message).join('\\n'))\n const result = response.data?.saveFigureV3 as V3ProductRecord | undefined\n if (!result?.id || !result.source) throw Error('서버가 저장 결과를 돌려주지 않았습니다.')\n return result\n }\n}\n"]}
@@ -1,22 +0,0 @@
1
- import { LitElement, nothing } from 'lit';
2
- import type { V3Asset } from '@hatiolab/figure-model';
3
- type Rung = 'none' | 'hold' | 'flow';
4
- /** Which rung a set of names is, or null when it is none of them -- not invented. */
5
- export declare function rungOf(names: readonly string[] | undefined): Rung | null;
6
- declare const FigureV3Capability_base: (new (...args: any[]) => LitElement) & typeof LitElement;
7
- /**
8
- * 자리·능력 — what the figure does on a board: the ladder from drawing only, to
9
- * holding things, to passing them on, and every place things are held, whatever part it is on. A place is not drawn,
10
- * so the list is the only way to find one; choosing a row selects its part, where the place is edited.
11
- */
12
- export declare class FigureV3Capability extends FigureV3Capability_base {
13
- static styles: import("lit").CSSResult[];
14
- asset?: V3Asset;
15
- /** The selected part, whose places are marked. */
16
- selected: string;
17
- locked: boolean;
18
- render(): import("lit-html").TemplateResult<1> | typeof nothing;
19
- private setRung;
20
- private pick;
21
- }
22
- export {};
@@ -1,141 +0,0 @@
1
- import { __decorate, __metadata } from "tslib";
2
- import { css, html, LitElement, nothing } from 'lit';
3
- import { customElement, property } from 'lit/decorators.js';
4
- import { live } from 'lit/directives/live.js';
5
- import { i18next, localize } from '@operato/i18n';
6
- import { v3BindingsOf } from '@hatiolab/figure-model';
7
- import { SidePanelStyles } from './side-panel-styles.js';
8
- const RUNGS = Object.freeze({
9
- none: [],
10
- hold: ['Capacity', 'Holdable'],
11
- flow: ['FlowNode']
12
- });
13
- /** Which rung a set of names is, or null when it is none of them -- not invented. */
14
- export function rungOf(names) {
15
- const has = new Set(names ?? []);
16
- if (has.size === 0)
17
- return 'none';
18
- for (const [rung, wanted] of Object.entries(RUNGS))
19
- if (wanted.length === has.size && wanted.every(name => has.has(name)))
20
- return rung;
21
- return null;
22
- }
23
- /**
24
- * 자리·능력 — what the figure does on a board: the ladder from drawing only, to
25
- * holding things, to passing them on, and every place things are held, whatever part it is on. A place is not drawn,
26
- * so the list is the only way to find one; choosing a row selects its part, where the place is edited.
27
- */
28
- let FigureV3Capability = class FigureV3Capability extends localize(i18next)(LitElement) {
29
- constructor() {
30
- super(...arguments);
31
- /** The selected part, whose places are marked. */
32
- this.selected = '';
33
- this.locked = false;
34
- }
35
- static { this.styles = [SidePanelStyles, css `
36
- :host {
37
- display: block;
38
- }
39
- label[rung] {
40
- display: flex;
41
- gap: 8px;
42
- align-items: flex-start;
43
- padding: 8px;
44
- border: 1px solid var(--md-sys-color-outline-variant);
45
- border-radius: 8px;
46
- cursor: pointer;
47
- }
48
- label[rung][on] {
49
- border-color: var(--md-sys-color-primary);
50
- background: var(--md-sys-color-secondary-container);
51
- color: var(--md-sys-color-on-secondary-container);
52
- }
53
- label[rung] span {
54
- display: grid;
55
- gap: 2px;
56
- }
57
- label[rung] em,
58
- p[hint],
59
- small {
60
- font-style: normal;
61
- margin: 0;
62
- color: var(--md-sys-color-on-surface-variant);
63
- }
64
- button[place] {
65
- display: grid;
66
- gap: 2px;
67
- text-align: left;
68
- padding: 6px 8px;
69
- font: inherit;
70
- color: inherit;
71
- border: 1px solid var(--md-sys-color-outline-variant);
72
- border-radius: 8px;
73
- background: none;
74
- cursor: pointer;
75
- }
76
- button[place][on] {
77
- border-color: var(--md-sys-color-primary);
78
- }
79
- code {
80
- font-size: 0.9em;
81
- color: var(--md-sys-color-on-surface-variant);
82
- }
83
- `]; }
84
- render() {
85
- const asset = this.asset;
86
- if (!asset)
87
- return nothing;
88
- const names = asset.document.capabilities.names ?? [];
89
- const rung = rungOf(names);
90
- const places = v3BindingsOf(asset);
91
- return html `<section>
92
- <h3>${i18next.t('figure.label.what-this-does')}</h3>
93
- ${['none', 'hold', 'flow'].map(which => html `<label rung ?on=${rung === which}>
94
- <input type="radio" name="rung" .checked=${live(rung === which)} ?disabled=${this.locked} @change=${() => this.setRung(which)} />
95
- <span>
96
- <strong>${i18next.t(`figure.label.rung-${which}`)}</strong>
97
- <em>${i18next.t(`figure.text.rung-${which}-note`)}</em>
98
- </span>
99
- </label>`)}
100
- ${rung === null ? html `<p hint>${i18next.t('figure.text.capability-hand-written')}</p>` : nothing}
101
- ${names.length ? html `<code>${names.join(' · ')}</code>` : nothing}
102
- </section>
103
- <section>
104
- <h3>${i18next.t('figure.label.places')}</h3>
105
- ${places.length
106
- ? places.map(one => html `<button place ?on=${one.part === this.selected} @click=${() => one.part && this.pick(one.part)}>
107
- <strong>${one.label}</strong>
108
- <small>
109
- ${i18next.t('figure.label.place-' + one.role)} · ${one.part ?? '?'}${one.face ? ' · ' + i18next.t('figure.label.face-' + one.face) : ''} ·
110
- ${one.accepts.join(', ')} · ${i18next.t('figure.label.place-holds', { count: one.capacity })}
111
- </small>
112
- </button>`)
113
- : html `<p hint>${i18next.t('figure.text.no-places')}</p>`}
114
- ${places.length && rung === 'none' ? html `<p hint>${i18next.t('figure.text.place-needs-holding')}</p>` : nothing}
115
- </section>
116
- `;
117
- }
118
- setRung(which) {
119
- this.dispatchEvent(new CustomEvent('v3-part-edit', { detail: { edit: { kind: 'set-capabilities', names: [...RUNGS[which]] } }, bubbles: true, composed: true }));
120
- }
121
- pick(part) {
122
- this.dispatchEvent(new CustomEvent('select-part', { detail: { id: part }, bubbles: true, composed: true }));
123
- }
124
- };
125
- __decorate([
126
- property({ attribute: false }),
127
- __metadata("design:type", Object)
128
- ], FigureV3Capability.prototype, "asset", void 0);
129
- __decorate([
130
- property(),
131
- __metadata("design:type", Object)
132
- ], FigureV3Capability.prototype, "selected", void 0);
133
- __decorate([
134
- property({ type: Boolean }),
135
- __metadata("design:type", Object)
136
- ], FigureV3Capability.prototype, "locked", void 0);
137
- FigureV3Capability = __decorate([
138
- customElement('figure-v3-capability')
139
- ], FigureV3Capability);
140
- export { FigureV3Capability };
141
- //# sourceMappingURL=figure-v3-capability.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"figure-v3-capability.js","sourceRoot":"","sources":["../../client/modeller/figure-v3-capability.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { v3BindingsOf } from '@hatiolab/figure-model'\nimport type { FigureCapability, V3Asset } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\n\n/*\n One rung per set of the format's capability names. Typed as the format's names, so a misspelt rung does not pass\n (ADR-0051 ④).\n*/\ntype Rung = 'none' | 'hold' | 'flow'\nconst RUNGS: Readonly<Record<Rung, readonly FigureCapability[]>> = Object.freeze({\n none: [],\n hold: ['Capacity', 'Holdable'],\n flow: ['FlowNode']\n})\n\n/** Which rung a set of names is, or null when it is none of them -- not invented. */\nexport function rungOf(names: readonly string[] | undefined): Rung | null {\n const has = new Set(names ?? [])\n if (has.size === 0) return 'none'\n for (const [rung, wanted] of Object.entries(RUNGS) as [Rung, readonly FigureCapability[]][])\n if (wanted.length === has.size && wanted.every(name => has.has(name))) return rung\n return null\n}\n\n/**\n * 자리·능력 — what the figure does on a board: the ladder from drawing only, to\n * holding things, to passing them on, and every place things are held, whatever part it is on. A place is not drawn,\n * so the list is the only way to find one; choosing a row selects its part, where the place is edited.\n */\n@customElement('figure-v3-capability')\nexport class FigureV3Capability extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n label[rung] {\n display: flex;\n gap: 8px;\n align-items: flex-start;\n padding: 8px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n cursor: pointer;\n }\n label[rung][on] {\n border-color: var(--md-sys-color-primary);\n background: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n label[rung] span {\n display: grid;\n gap: 2px;\n }\n label[rung] em,\n p[hint],\n small {\n font-style: normal;\n margin: 0;\n color: var(--md-sys-color-on-surface-variant);\n }\n button[place] {\n display: grid;\n gap: 2px;\n text-align: left;\n padding: 6px 8px;\n font: inherit;\n color: inherit;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 8px;\n background: none;\n cursor: pointer;\n }\n button[place][on] {\n border-color: var(--md-sys-color-primary);\n }\n code {\n font-size: 0.9em;\n color: var(--md-sys-color-on-surface-variant);\n }\n `]\n\n @property({ attribute: false }) asset?: V3Asset\n /** The selected part, whose places are marked. */\n @property() selected = ''\n @property({ type: Boolean }) locked = false\n\n render() {\n const asset = this.asset\n if (!asset) return nothing\n const names = asset.document.capabilities.names ?? []\n const rung = rungOf(names)\n const places = v3BindingsOf(asset)\n return html`<section>\n <h3>${i18next.t('figure.label.what-this-does')}</h3>\n ${(['none', 'hold', 'flow'] as const).map(\n which => html`<label rung ?on=${rung === which}>\n <input type=\"radio\" name=\"rung\" .checked=${live(rung === which)} ?disabled=${this.locked} @change=${() => this.setRung(which)} />\n <span>\n <strong>${i18next.t(`figure.label.rung-${which}`)}</strong>\n <em>${i18next.t(`figure.text.rung-${which}-note`)}</em>\n </span>\n </label>`\n )}\n ${rung === null ? html`<p hint>${i18next.t('figure.text.capability-hand-written')}</p>` : nothing}\n ${names.length ? html`<code>${names.join(' · ')}</code>` : nothing}\n </section>\n <section>\n <h3>${i18next.t('figure.label.places')}</h3>\n ${places.length\n ? places.map(\n one => html`<button place ?on=${one.part === this.selected} @click=${() => one.part && this.pick(one.part)}>\n <strong>${one.label}</strong>\n <small>\n ${i18next.t('figure.label.place-' + one.role)} · ${one.part ?? '?'}${one.face ? ' · ' + i18next.t('figure.label.face-' + one.face) : ''} ·\n ${one.accepts.join(', ')} · ${i18next.t('figure.label.place-holds', { count: one.capacity })}\n </small>\n </button>`\n )\n : html`<p hint>${i18next.t('figure.text.no-places')}</p>`}\n ${places.length && rung === 'none' ? html`<p hint>${i18next.t('figure.text.place-needs-holding')}</p>` : nothing}\n </section>\n `\n }\n\n private setRung(which: Rung) {\n this.dispatchEvent(new CustomEvent('v3-part-edit', { detail: { edit: { kind: 'set-capabilities', names: [...RUNGS[which]] } }, bubbles: true, composed: true }))\n }\n\n private pick(part: string) {\n this.dispatchEvent(new CustomEvent('select-part', { detail: { id: part }, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -1,31 +0,0 @@
1
- import '@material/web/icon/icon.js';
2
- import { LitElement, nothing } from 'lit';
3
- import type { V3Asset } from '@hatiolab/figure-model';
4
- declare const FigureV3Clips_base: (new (...args: any[]) => LitElement) & typeof LitElement;
5
- /**
6
- * 클립 — controls played by keys over time. A clip is one clock; each track plays one
7
- * control. Scrubbing a clip shows its pose at that moment (one batch of input changes, not saved as the default); a key
8
- * is added at the scrubbed moment with the control's value there, so a pose is made with the controls' sliders and
9
- * then kept as a key. Every change is one part edit.
10
- */
11
- export declare class FigureV3Clips extends FigureV3Clips_base {
12
- private readonly help;
13
- static styles: import("lit").CSSResult[];
14
- asset?: V3Asset;
15
- /** The values the editor holds now, by input id -- what a new key takes. */
16
- values: Record<string, number>;
17
- locked: boolean;
18
- /** Where each clip is scrubbed to, in seconds. */
19
- private at;
20
- private adding;
21
- render(): import("lit-html").TemplateResult<1> | typeof nothing;
22
- private renderClip;
23
- private renderTrack;
24
- private renderAdd;
25
- /** Show the clip's pose at a moment: one batch of input changes, not saved as the default. */
26
- /** Show the clip's pose at a moment. While the bar is dragged the pose is live (drawn, not recorded); the release
27
- * applies it as one batch of input changes. */
28
- private scrub;
29
- private edit;
30
- }
31
- export {};