@things-factory/figure-ui 10.1.250 → 10.1.253

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.250",
3
+ "version": "10.1.253",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "@things-factory:registry": "https://registry.npmjs.org"
@@ -27,8 +27,8 @@
27
27
  "clean": "npm run clean:server && npm run clean:client"
28
28
  },
29
29
  "dependencies": {
30
- "@hatiolab/figure-model": "^0.12.2",
31
- "@hatiolab/things-scene": "^10.1.109",
30
+ "@hatiolab/figure-model": "^0.12.3",
31
+ "@hatiolab/things-scene": "^10.1.110",
32
32
  "@material/web": "^2.0.0",
33
33
  "@operato/data-grist": "^10.29.43",
34
34
  "@operato/graphql": "^10.29.43",
@@ -43,10 +43,10 @@
43
43
  "@operato/styles": "^10.0.0",
44
44
  "@operato/utils": "^10.29.43",
45
45
  "@things-factory/ai-assistant": "^10.1.250",
46
- "@things-factory/figure-service": "^10.1.250",
47
- "@things-factory/figure-viewer": "^10.1.247",
46
+ "@things-factory/figure-service": "^10.1.253",
47
+ "@things-factory/figure-viewer": "^10.1.253",
48
48
  "@things-factory/setting-base": "^10.1.250",
49
49
  "three": "^0.185.1"
50
50
  },
51
- "gitHead": "4fdb6d24ef24f43d9e158d4c6d6396ad3097bb9e"
51
+ "gitHead": "e3d26f853d258ada1a2e77c7f533cc8f5b2528fb"
52
52
  }
@@ -337,7 +337,8 @@ test('데이터 탭 · 클립 패널 · 미리보기의 배선과 문구', () =>
337
337
  ...['not-a-record', 'not-a-list', 'type-mismatch', 'out-of-range', 'value-unknown', 'json-unreadable', 'item-id-missing'].map(k => `figure.text.data-value-${k}`)
338
338
  ]
339
339
  for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
340
- const table = JSON.parse(read(`../translations/${lang}.json`))
340
+ /* What the app loads: these words and figure-viewer's, which say a figure's data on every screen. */
341
+ const table = { ...JSON.parse(read(`../translations/${lang}.json`)), ...JSON.parse(read(`../../figure-viewer/translations/${lang}.json`)) }
341
342
  for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
342
343
  for (const key of made) assert.ok(table[key], `${lang}: ${key}`)
343
344
  }
@@ -355,3 +355,121 @@ test('기본 자세가 새 범위 밖이라 옮겨지면 한 줄로 말한다
355
355
  assert.equal(defaultsMovedOf(knee(0, 140, 20), knee(0, 140, 60), say), '', '범위 안에서 작성자가 바꾼 값')
356
356
  assert.equal(defaultsMovedOf(knee(0, 140, 20), knee(0, 140, 20), say), '')
357
357
  })
358
+
359
+ /*
360
+ * What a person reaches from the modeller's fields came as 「이 편집은 할 수 없습니다. (CODE)」, the author left with nothing
361
+ * to go on (user, 2026-11-05: 「갑자기 발생한 이 오류가 무슨 오류인지 알 수 없는 것이 문제」). Each is a real refusal.
362
+ */
363
+ const dog = () => structuredClone(fm.robotDogFigure().asset) as FigureAsset
364
+ const elbow = () =>
365
+ fm.applyFigureProposal(fm.createFigureAsset('Elbow'), [{ kind: 'add-part', part: { id: 'pipe', kind: 'pipe', dimensions: { radius: 40, wall: 4 }, path: [{ line: 500 }, { bend: { radius: 120, angle: 90, toward: 90 } }, { line: 400 }] } }] as never)
366
+ const edit = (asset: FigureAsset, e: object) => fm.editFigurePart(asset, e as never)
367
+
368
+ test('색 거절은 어느 색이 왜인지 — 이미 다른 값인 색, 팔레트에 없는 부품 색·발광 색, #rrggbb 가 아닌 값', () => {
369
+ const asset = dog()
370
+ const [token, has] = Object.entries(asset.palette)[0]!
371
+ const target = asset.appearance[0]!.target
372
+ assert.equal(
373
+ editRefusalOf(refusalOf(() => edit(asset, { kind: 'set-material', target, material: { token }, palette: { [token]: '#000000' } })), say),
374
+ `'${token}' 색은 이 피규어에서 이미 ${has} 입니다. #000000 로 바꾸면 이 색을 쓰는 다른 부품도 모두 바뀌므로 바꾸지 않았습니다. 이 부품만 바꾸려면 색 패널에서 색 값을 직접 골라 주세요.`
375
+ )
376
+ const missing = dog()
377
+ missing.appearance[0]!.material = { ...missing.appearance[0]!.material, token: 'gone' }
378
+ assert.equal(editRefusalOf(refusalOf(() => fm.compileFigureAsset(missing)), keyed), `figure.text.palette-colour-missing ${JSON.stringify({ token: 'gone', part: target })}`)
379
+ const glow = dog()
380
+ glow.appearance[0]!.material = { ...glow.appearance[0]!.material, emissive: { token: 'gone', intensity: 1, on: true } }
381
+ assert.equal(editRefusalOf(refusalOf(() => fm.compileFigureAsset(glow)), keyed), `figure.text.palette-glow-missing ${JSON.stringify({ token: 'gone', part: target })}`)
382
+ const bad = dog()
383
+ bad.palette[token] = '#fff'
384
+ assert.equal(editRefusalOf(refusalOf(() => fm.compileFigureAsset(bad)), keyed), `figure.text.palette-colour-invalid ${JSON.stringify({ token, colour: '#fff' })}`)
385
+ })
386
+
387
+ test('모델러 칸에서 닿는 거절은 그 칸의 값으로 — 조작값 범위·이름, 클립 이름·길이, 반복 간격, 관 조각, 떼기, 데이터 연결', () => {
388
+ const said = (run: () => unknown) => editRefusalOf(refusalOf(run), say)
389
+ assert.equal(said(() => edit(dog(), { kind: 'set-state-input', input: 'fl-hip', min: 10, max: 20 })), "조작값 'fl-hip' 은 관절의 값이라 범위에 0(기본 자세)이 들어가야 합니다. 지금 10 ~ 20 입니다.")
390
+ assert.equal(said(() => edit(dog(), { kind: 'set-state-input', input: 'fl-hip', min: 20, max: 10 })), "조작값 'fl-hip' 의 범위는 작은 값에서 큰 값 순서여야 합니다. 지금 20 ~ 10 입니다.")
391
+ assert.equal(said(() => edit(dog(), { kind: 'set-state-input', input: 'fl-hip', sweep: -1 })), "조작값 'fl-hip' 이 끝에서 끝까지 움직이는 시간은 0초 이상이어야 합니다. 지금 -1 입니다.")
392
+ assert.equal(said(() => edit(dog(), { kind: 'set-clip', clip: 'walk', name: 'run' })), "'run' 은 이미 다른 클립의 이름입니다. 클립 'walk' 에 다른 이름을 적어 주세요.")
393
+ assert.equal(said(() => edit(dog(), { kind: 'set-clip', clip: 'walk', name: ' ' })), "클립 'walk' 의 이름이 비어 있습니다. 이름을 적어 주세요.")
394
+ assert.equal(said(() => edit(dog(), { kind: 'set-clip', clip: 'walk', duration: 0 })), "클립 'walk' 의 길이는 0초보다 길어야 합니다. 지금 0 입니다.")
395
+ const group = { id: 'locomotion', label: '보행', clips: ['idle', 'walk', 'run'], defaultClip: 'idle', transitionSeconds: -1 }
396
+ assert.equal(said(() => edit(dog(), { kind: 'set-motion-group', group })), "동작 그룹 'locomotion' 의 전환 시간은 0초 이상이어야 합니다. 지금 -1 입니다.")
397
+ assert.equal(said(() => act(dog(), { kind: 'add-design-input', id: 'fl-hip', unit: 'mm', min: 0, max: 10, value: 5 })), "'fl-hip' 은 이 피규어에 이미 있는 값의 이름입니다. 다른 이름을 적어 주세요.")
398
+ const named = dog()
399
+ named.document.model.inputs.push({ id: '무릎', unit: 'deg', min: 0, max: 1, role: 'design' } as never)
400
+ assert.equal(said(() => fm.compileFigureAsset(named)), "'무릎' 은 조작값 이름으로 쓸 수 없습니다. 영문자로 시작하고 영문자·숫자·_ . - 만 써 주세요.")
401
+ const lift = act(box(), { kind: 'add-design-input', id: 'lift', unit: 'mm', min: 0, max: 10, value: 5 })
402
+ assert.equal(said(() => act(lift, { kind: 'set-input-range', input: 'lift', min: 8, max: 8 })), "값 'lift' 의 범위는 가장 작은 값이 가장 큰 값보다 작아야 합니다. 지금 8 ~ 8 입니다.")
403
+ assert.equal(said(() => act(conveyor(), { kind: 'set-repeat', part: 'roller', pitch: 0 })), "부품 'roller' 반복 배열의 간격은 0 mm 보다 커야 합니다. 지금 0 mm 입니다.")
404
+ assert.equal(said(() => act(elbow(), { kind: 'set-pipe', part: 'pipe', path: [{ line: 0 }] })), '관의 1번째 조각(직선)의 길이는 0 mm 보다 커야 합니다. 지금 0 mm 입니다.')
405
+ assert.equal(said(() => act(elbow(), { kind: 'set-pipe', part: 'pipe', path: [{ line: 100 }, { bend: { radius: 0, angle: 90, toward: 0 } }] })), '관의 2번째 조각(굽힘)의 반지름은 0 mm 보다 커야 합니다. 지금 0 mm 입니다.')
406
+ assert.equal(said(() => act(elbow(), { kind: 'set-pipe', part: 'pipe', path: [{ line: 100 }, { bend: { radius: 100, angle: 0, toward: 0 } }] })), '관의 2번째 굽힘 각도는 1° ~ 359° 사이의 정수여야 합니다. 지금 0° 입니다.')
407
+ assert.equal(said(() => act(elbow(), { kind: 'set-pipe', part: 'pipe', wall: -1 })), '관의 벽 두께는 0 mm 이상이어야 합니다. 지금 -1 mm 입니다.')
408
+ let turned = fm.addFigureBox(fm.createFigureAsset('Turned'), { id: 'base', size: { x: 200, y: 100, z: 200 }, position: { x: 0, y: 50, z: 0 } })
409
+ turned = fm.addFigureBox(turned, { id: 'arm', size: { x: 20, y: 200, z: 20 }, position: { x: 0, y: 200, z: 0 } })
410
+ turned = act(turned, { kind: 'attach', part: 'base', to: { plane: 'mounting-plane' } })
411
+ turned = act(turned, { kind: 'attach', part: 'arm', to: { part: 'base', face: 'top' } })
412
+ turned = act(turned, { kind: 'set-joint', joint: { ...turned.joints!.find(j => j.body1.part === 'base')!, origin: { childRotate: { y: 30 } } } })
413
+ assert.equal(said(() => act(turned, { kind: 'detach', part: 'arm' })), "부품 'arm' 은 'base' 에 붙어 함께 돌아가 있어서, 떼면 자세가 바뀝니다. 그래서 떼지 않았습니다.")
414
+ let grip = fm.addFigureBox(fm.createFigureAsset('Gripper'), { id: 'tip', size: { x: 60, y: 20, z: 20 }, position: { x: 0, y: 10, z: 0 } })
415
+ grip = fm.addFigureBox(grip, { id: 'left', size: { x: 10, y: 40, z: 10 }, position: { x: -15, y: 40, z: 0 } })
416
+ grip = fm.addFigureBox(grip, { id: 'right', size: { x: 10, y: 40, z: 10 }, position: { x: 15, y: 40, z: 0 } })
417
+ grip = act(grip, { kind: 'set-joint', joint: { id: 'grip', body0: { part: 'tip', face: 'top' }, body1: { part: 'left' }, origin: { align: { x: 'min' } }, motion: [{ type: 'prismatic', axis: 'X', lowerLimit: 0, upperLimit: 10 }] } })
418
+ grip = act(grip, { kind: 'set-joint', joint: { id: 'grip-mirror', body0: { part: 'tip', face: 'top' }, body1: { part: 'right' }, origin: { align: { x: 'max' } }, motion: [{ type: 'prismatic', axis: 'X', mimic: { motion: 'grip', multiplier: -1 } }] } })
419
+ assert.equal(said(() => act(grip, { kind: 'remove-joint', id: 'grip' })), "관절 grip-mirror 이 관절 'grip' 의 움직임을 따르고 있어, 그 움직임을 없애거나 멈출 수 없습니다. 따르는 관절을 먼저 바꿔 주세요.")
420
+ const roll = act(
421
+ fm.addFigurePrimitive(fm.createFigureAsset('Roll'), { id: 'wound', kind: 'cylinder', dimensions: { radius: 450, length: 270 }, position: { x: 0, y: 500, z: 0 }, rotation: { x: 0, y: 0, z: 90 } }),
422
+ { kind: 'set-cavity', part: 'wound', cavity: { wall: 350, start: 'open', end: 'open' } }
423
+ )
424
+ assert.equal(
425
+ said(() => act(roll, { kind: 'size-by-state', part: 'wound', axis: 'x', state: { id: 'fill', unit: 'percent', min: 0, max: 100 }, at: { min: 80, max: 450 } })),
426
+ "부품 'wound' 은 속이 비어 있어(안쪽 100 mm), 조작값으로 정하는 크기는 양 끝 모두 100 mm 보다 커야 합니다."
427
+ )
428
+ assert.equal(said(() => act(panel(), followsWidth(-500))), '이 값이면 부품의 크기가 0 이하가 됩니다. 이 값을 따르는 부품이 있다면 그 비율·차이를 확인해 주세요.')
429
+ const pose = { id: 'at-x', accessor: 'position.x', behavior: { kind: 'pose', axis: 'x', seconds: -1 }, rule: 'value' }
430
+ assert.equal(said(() => edit(dog(), { kind: 'set-data-binding', binding: pose })), "데이터 연결 'at-x' 의 이동 시간은 0초 이상이어야 합니다. 지금 -1 입니다.")
431
+ })
432
+
433
+ test('레시피 단계 안의 거절도 같은 말로 — 단계 번호와 함께', () => {
434
+ const refused = refusalOf(() => fm.applyFigureProposal(box(), [{ kind: 'add-design-input', id: 'lift', unit: 'mm', min: 10, max: 0, value: 5 }] as never))
435
+ assert.match(recipeRefusalOf(refused, say)!, /1 번째 단계에서 막혔습니다\. 값 'lift' 의 범위는 가장 작은 값이 가장 큰 값보다 작아야 합니다/)
436
+ })
437
+
438
+ test('새 거절 문장은 여섯 언어 모두, 받는 값을 다 쓴다', () => {
439
+ const keys: Record<string, string[]> = {
440
+ 'palette-colour-taken': ['token', 'has', 'asked'],
441
+ 'palette-colour-missing': ['token', 'part'],
442
+ 'palette-glow-missing': ['token', 'part'],
443
+ 'palette-colour-invalid': ['token', 'colour'],
444
+ 'state-range-order': ['input', 'min', 'max'],
445
+ 'state-range-holds-zero': ['input', 'min', 'max'],
446
+ 'state-range-endless': ['input'],
447
+ 'state-range-start': ['input', 'min', 'max'],
448
+ 'state-sweep': ['input', 'sweep'],
449
+ 'clip-name-empty': ['clip'],
450
+ 'clip-name-taken': ['clip', 'name'],
451
+ 'clip-duration': ['clip', 'duration'],
452
+ 'group-blend': ['group', 'seconds'],
453
+ 'id-invalid': ['id'],
454
+ 'id-in-use': ['id'],
455
+ 'range-order': ['input', 'min', 'max'],
456
+ 'row-pitch-not-positive': ['part', 'pitch'],
457
+ 'axis-followed': ['joint', 'followers'],
458
+ 'detach-would-turn': ['part', 'parent'],
459
+ 'state-size-inside': ['part', 'least'],
460
+ 'pipe-length-not-positive': ['piece', 'value'],
461
+ 'pipe-radius-not-positive': ['piece', 'value'],
462
+ 'pipe-bend-angle': ['piece', 'angle'],
463
+ 'pipe-wall-negative': ['wall'],
464
+ 'pose-seconds': ['binding', 'seconds'],
465
+ 'section-size-not-positive': []
466
+ }
467
+ for (const lang of ['ko', 'en', 'ja', 'zh', 'es', 'ms']) {
468
+ const words = wordsOf(lang)
469
+ for (const [key, params] of Object.entries(keys)) {
470
+ const text = words[`figure.text.${key}`]
471
+ assert.ok(text, `${lang}: ${key}`)
472
+ for (const p of params) assert.ok(text.includes(`{${p}}`), `${lang}: ${key} {${p}}`)
473
+ }
474
+ }
475
+ })
@@ -361,17 +361,6 @@
361
361
  "figure.label.tools": "align",
362
362
  "figure.label.transparent": "transparent",
363
363
  "figure.label.triangles": "triangles",
364
- "figure.label.unit-none": "no unit",
365
- "figure.label.unit-type-it": "type it…",
366
- "figure.label.unit-mm": "mm",
367
- "figure.label.unit-cm": "cm",
368
- "figure.label.unit-m": "m",
369
- "figure.label.unit-rad": "radians",
370
- "figure.label.unit-deg": "degrees",
371
- "figure.label.unit-s": "seconds",
372
- "figure.label.unit-ratio": "ratio",
373
- "figure.label.unit-percent": "%",
374
- "figure.label.unit-count": "count",
375
364
  "figure.label.weight": "drawing cost",
376
365
  "figure.text.accepts-anything": "empty means anything",
377
366
  "figure.text.accepts-explained": "types allowed in this slot. separate with commas; leave empty for no limit.",
@@ -407,7 +396,7 @@
407
396
  "figure.text.center-on-z": "moves it to the figure's Z centre (Z=0).",
408
397
  "figure.text.clip-is-time-running": "plays over time. the value an instance gives is the playback speed.",
409
398
  "figure.text.clip-name-is-how-it-is-driven": "an instance gives a speed by this name.",
410
- "figure.text.clip-name-taken": "another clip already has this name. enter a different name.",
399
+ "figure.text.clip-name-taken": "'{name}' is already the name of another clip. Give clip '{clip}' another name.",
411
400
  "figure.text.clip-needs-a-name": "enter a name.",
412
401
  "figure.text.collapse-parts-pane": "collapse the part list",
413
402
  "figure.text.collapse-properties-pane": "collapse the property pane",
@@ -1043,8 +1032,6 @@
1043
1032
  "figure.text.motion-groups-explained": "Clips of one group play one at a time. For several clips to move the same control, put them in one group. A change of clip blends from the pose left to the new clip over the transition time.",
1044
1033
  "figure.label.tab-data": "data",
1045
1034
  "figure.label.data-kind": "kind",
1046
- "figure.label.data-kind-number": "number",
1047
- "figure.label.data-kind-boolean": "true/false",
1048
1035
  "figure.label.data-unit": "unit",
1049
1036
  "figure.label.data-min": "min",
1050
1037
  "figure.label.data-max": "max",
@@ -1069,9 +1056,6 @@
1069
1056
  "figure.label.data-label": "display name",
1070
1057
  "figure.label.data-json": "also read as JSON text",
1071
1058
  "figure.label.data-item-kind": "element kind",
1072
- "figure.label.data-kind-text": "text",
1073
- "figure.label.data-kind-object": "object",
1074
- "figure.label.data-kind-list": "list",
1075
1059
  "figure.button.add-data-field": "add a field",
1076
1060
  "figure.button.add-data-child": "add a field under {parent}",
1077
1061
  "figure.button.remove-data-field": "remove the field",
@@ -1081,21 +1065,9 @@
1081
1065
  "figure.text.data-guessed": "{count} fields found.",
1082
1066
  "figure.text.data-guessed-replaces": "{count} fields found. Using them replaces the current data shape.",
1083
1067
  "figure.button.data-use-guessed": "use this shape",
1084
- "figure.label.data-bindings": "bindings",
1085
1068
  "figure.text.data-bindings-explained": "Connects data values to the figure. A number shows as it is, scaled, or by a range table; a text or true/false by a code table. A table sets, per value, parts' colour and effect, controls and a motion group at once. Select a row to edit it.",
1086
1069
  "figure.label.data-field": "data value",
1087
1070
  "figure.label.data-behavior": "behaviour",
1088
- "figure.label.data-behavior-state": "control {name}",
1089
- "figure.label.data-behavior-palette": "colour {name}",
1090
- "figure.label.data-behavior-motion-clip": "{name} clip",
1091
- "figure.label.data-behavior-motion-speed": "{name} speed",
1092
- "figure.label.data-behavior-motion-paused": "{name} paused",
1093
- "figure.label.data-behavior-pose": "position {name}",
1094
- "figure.label.data-behavior-items": "items {name}",
1095
- "figure.label.data-axis-x": "X",
1096
- "figure.label.data-axis-y": "height (Y)",
1097
- "figure.label.data-axis-z": "Z",
1098
- "figure.label.data-axis-heading": "heading",
1099
1071
  "figure.label.data-seconds": "travel time (s)",
1100
1072
  "figure.label.data-paused": "paused",
1101
1073
  "figure.label.data-playing": "playing",
@@ -1105,11 +1077,6 @@
1105
1077
  "figure.button.add-data-range": "add an interval",
1106
1078
  "figure.button.data-set-result": "set",
1107
1079
  "figure.button.remove-data-range": "remove the interval",
1108
- "figure.label.data-item-id": "item id",
1109
- "figure.label.data-item-type": "item type",
1110
- "figure.label.data-item-place": "place",
1111
- "figure.label.data-item-stack": "stack order",
1112
- "figure.label.data-item-status": "status",
1113
1080
  "figure.label.data-item-slot": "default place",
1114
1081
  "figure.text.data-no-behavior": "Nothing of this figure can be moved by this value.",
1115
1082
  "figure.button.add-data-binding": "add a binding",
@@ -1265,10 +1232,6 @@
1265
1232
  "figure.label.data-legend-role": "Legend role",
1266
1233
  "figure.label.data-legend-swappable": "A board may swap it for a legend",
1267
1234
  "figure.label.data-method": "Method",
1268
- "figure.label.data-method-code": "Code table",
1269
- "figure.label.data-method-ranges": "Range table",
1270
- "figure.label.data-method-scale": "Scaled",
1271
- "figure.label.data-method-value": "As it is",
1272
1235
  "figure.label.data-new-field": "New field",
1273
1236
  "figure.label.data-sample-legend": "Sample legend",
1274
1237
  "figure.label.data-table-legend": "legend {name}",
@@ -1391,5 +1354,30 @@
1391
1354
  "figure.label.pipe-end-width": "end width (X)",
1392
1355
  "figure.label.pipe-end-depth": "end depth (Z)",
1393
1356
  "figure.text.pipe-end-same": "The same as the start. Changed, it reaches that size at its end, evenly along its length.",
1357
+ "figure.text.palette-colour-invalid": "The colour '{token}' in this figure's palette is {colour}, not a six-digit #rrggbb colour, so the edit cannot be made.",
1358
+ "figure.text.palette-colour-missing": "The colour '{token}' of part '{part}' is not in this figure's palette, so the edit cannot be made. Pick that part's colour again.",
1359
+ "figure.text.palette-colour-taken": "The colour '{token}' is already {has} in this figure. Changing it to {asked} would change every other part that uses it, so it was not changed. To change only this part, pick a colour value in the colour panel.",
1360
+ "figure.text.palette-glow-missing": "The glow colour '{token}' of part '{part}' is not in this figure's palette, so the edit cannot be made. Pick that part's glow colour again.",
1361
+ "figure.text.state-range-order": "The range of control '{input}' must run from a smaller value to a larger one. It is now {min} to {max}.",
1362
+ "figure.text.state-range-holds-zero": "Control '{input}' moves a joint, so its range must include 0 (the rest pose). It is now {min} to {max}.",
1363
+ "figure.text.state-range-endless": "Control '{input}' turns a joint without end, so it has no range to set.",
1364
+ "figure.text.state-range-start": "The starting value of control '{input}' must lie within its range, {min} to {max}.",
1365
+ "figure.text.state-sweep": "The time control '{input}' takes from end to end must be 0 seconds or more. It is now {sweep}.",
1366
+ "figure.text.clip-name-empty": "The name of clip '{clip}' is empty. Enter a name.",
1367
+ "figure.text.clip-duration": "Clip '{clip}' must run for more than 0 seconds. It is now {duration}.",
1368
+ "figure.text.group-blend": "The blend time of motion group '{group}' must be 0 seconds or more. It is now {seconds}.",
1369
+ "figure.text.id-invalid": "'{id}' cannot be a control name. Start with a letter and use only letters, digits and _ . -",
1370
+ "figure.text.id-in-use": "'{id}' is already the name of a value in this figure. Enter another name.",
1371
+ "figure.text.range-order": "The least of value '{input}' must be below its most. It is now {min} to {max}.",
1372
+ "figure.text.row-pitch-not-positive": "The gap of part '{part}''s row must be more than 0 mm. It is now {pitch} mm.",
1373
+ "figure.text.axis-followed": "Joint {followers} follows the motion of joint '{joint}', so that motion cannot be removed or stopped. Change the following joint first.",
1374
+ "figure.text.detach-would-turn": "Part '{part}' stands turned with '{parent}', which it is fastened to; detaching it would change its pose, so it was not detached.",
1375
+ "figure.text.state-size-inside": "Part '{part}' is hollow ({least} mm inside), so the size a control sets must be more than {least} mm at both ends.",
1376
+ "figure.text.pipe-length-not-positive": "The length of piece {piece} of the pipe (a straight run) must be more than 0 mm. It is now {value} mm.",
1377
+ "figure.text.pipe-radius-not-positive": "The radius of piece {piece} of the pipe (a bend) must be more than 0 mm. It is now {value} mm.",
1378
+ "figure.text.pipe-bend-angle": "The angle of bend {piece} of the pipe must be a whole number from 1° to 359°. It is now {angle}°.",
1379
+ "figure.text.pipe-wall-negative": "The pipe's wall must be 0 mm or more. It is now {wall} mm.",
1380
+ "figure.text.pose-seconds": "The move time of data binding '{binding}' must be 0 seconds or more. It is now {seconds}.",
1381
+ "figure.text.section-size-not-positive": "With this value a part's size comes to 0 or less. If a part follows this value, check its share or difference.",
1394
1382
  "figure.button.pipe-end-same": "one size all along"
1395
1383
  }
@@ -361,17 +361,6 @@
361
361
  "figure.label.tools": "alinear",
362
362
  "figure.label.transparent": "transparente",
363
363
  "figure.label.triangles": "triángulos",
364
- "figure.label.unit-none": "sin unidad",
365
- "figure.label.unit-type-it": "escribirla…",
366
- "figure.label.unit-mm": "mm",
367
- "figure.label.unit-cm": "cm",
368
- "figure.label.unit-m": "m",
369
- "figure.label.unit-rad": "radianes",
370
- "figure.label.unit-deg": "grados",
371
- "figure.label.unit-s": "segundos",
372
- "figure.label.unit-ratio": "proporción",
373
- "figure.label.unit-percent": "%",
374
- "figure.label.unit-count": "unidades",
375
364
  "figure.label.weight": "coste de dibujo",
376
365
  "figure.text.accepts-anything": "vacío significa cualquiera",
377
366
  "figure.text.accepts-explained": "tipos permitidos en esta ranura. sepárelos con comas; déjelo vacío para no limitar.",
@@ -407,7 +396,7 @@
407
396
  "figure.text.center-on-z": "la mueve al centro Z de la figura (Z=0).",
408
397
  "figure.text.clip-is-time-running": "se reproduce en el tiempo. el valor que da una instancia es la velocidad de reproducción.",
409
398
  "figure.text.clip-name-is-how-it-is-driven": "una instancia indica una velocidad con este nombre.",
410
- "figure.text.clip-name-taken": "otro clip ya tiene este nombre. introduzca un nombre diferente.",
399
+ "figure.text.clip-name-taken": "'{name}' ya es el nombre de otro clip. Dé otro nombre al clip '{clip}'.",
411
400
  "figure.text.clip-needs-a-name": "introduzca un nombre.",
412
401
  "figure.text.collapse-parts-pane": "contraer la lista de piezas",
413
402
  "figure.text.collapse-properties-pane": "contraer el panel de propiedades",
@@ -1043,8 +1032,6 @@
1043
1032
  "figure.text.motion-groups-explained": "Los clips de un grupo se reproducen de uno en uno. Para que varios clips muevan el mismo control, póngalos en un grupo. Al cambiar de clip, se pasa de la postura anterior al nuevo clip durante el tiempo de transición.",
1044
1033
  "figure.label.tab-data": "datos",
1045
1034
  "figure.label.data-kind": "tipo",
1046
- "figure.label.data-kind-number": "número",
1047
- "figure.label.data-kind-boolean": "verdadero/falso",
1048
1035
  "figure.label.data-unit": "unidad",
1049
1036
  "figure.label.data-min": "mín.",
1050
1037
  "figure.label.data-max": "máx.",
@@ -1069,9 +1056,6 @@
1069
1056
  "figure.label.data-label": "nombre visible",
1070
1057
  "figure.label.data-json": "leer también como texto JSON",
1071
1058
  "figure.label.data-item-kind": "tipo de elemento",
1072
- "figure.label.data-kind-text": "texto",
1073
- "figure.label.data-kind-object": "objeto",
1074
- "figure.label.data-kind-list": "lista",
1075
1059
  "figure.button.add-data-field": "añadir campo",
1076
1060
  "figure.button.add-data-child": "añadir campo bajo {parent}",
1077
1061
  "figure.button.remove-data-field": "quitar el campo",
@@ -1081,21 +1065,9 @@
1081
1065
  "figure.text.data-guessed": "Se encontraron {count} campos.",
1082
1066
  "figure.text.data-guessed-replaces": "Se encontraron {count} campos. Usarlos reemplaza la forma de datos actual.",
1083
1067
  "figure.button.data-use-guessed": "usar esta forma",
1084
- "figure.label.data-bindings": "vínculos",
1085
1068
  "figure.text.data-bindings-explained": "Conecta valores de datos con la figura. Un número se muestra tal cual, escalado o con una tabla de rangos; un texto o verdadero/falso, con una tabla de códigos. Una tabla fija por valor el color y efecto de las piezas, los controles y un grupo de movimiento. Seleccione una fila para editarla.",
1086
1069
  "figure.label.data-field": "valor de datos",
1087
1070
  "figure.label.data-behavior": "comportamiento",
1088
- "figure.label.data-behavior-state": "control {name}",
1089
- "figure.label.data-behavior-palette": "color {name}",
1090
- "figure.label.data-behavior-motion-clip": "clip de {name}",
1091
- "figure.label.data-behavior-motion-speed": "velocidad de {name}",
1092
- "figure.label.data-behavior-motion-paused": "{name} en pausa",
1093
- "figure.label.data-behavior-pose": "posición {name}",
1094
- "figure.label.data-behavior-items": "artículos {name}",
1095
- "figure.label.data-axis-x": "X",
1096
- "figure.label.data-axis-y": "altura (Y)",
1097
- "figure.label.data-axis-z": "Z",
1098
- "figure.label.data-axis-heading": "orientación",
1099
1071
  "figure.label.data-seconds": "tiempo de desplazamiento (s)",
1100
1072
  "figure.label.data-paused": "en pausa",
1101
1073
  "figure.label.data-playing": "en marcha",
@@ -1105,11 +1077,6 @@
1105
1077
  "figure.button.add-data-range": "añadir intervalo",
1106
1078
  "figure.button.data-set-result": "fijar",
1107
1079
  "figure.button.remove-data-range": "quitar el intervalo",
1108
- "figure.label.data-item-id": "id del artículo",
1109
- "figure.label.data-item-type": "tipo de artículo",
1110
- "figure.label.data-item-place": "lugar",
1111
- "figure.label.data-item-stack": "orden en la pila",
1112
- "figure.label.data-item-status": "estado",
1113
1080
  "figure.label.data-item-slot": "lugar predeterminado",
1114
1081
  "figure.text.data-no-behavior": "Ningún comportamiento de esta figura puede moverse con este valor.",
1115
1082
  "figure.button.add-data-binding": "añadir vínculo",
@@ -1265,10 +1232,6 @@
1265
1232
  "figure.label.data-legend-role": "Rol de leyenda",
1266
1233
  "figure.label.data-legend-swappable": "Quien lo usa puede cambiarla por una leyenda",
1267
1234
  "figure.label.data-method": "Método",
1268
- "figure.label.data-method-code": "Tabla de códigos",
1269
- "figure.label.data-method-ranges": "Tabla de rangos",
1270
- "figure.label.data-method-scale": "Escalado",
1271
- "figure.label.data-method-value": "Tal cual",
1272
1235
  "figure.label.data-new-field": "Campo nuevo",
1273
1236
  "figure.label.data-sample-legend": "Leyenda de ejemplo",
1274
1237
  "figure.label.data-table-legend": "leyenda {name}",
@@ -1391,5 +1354,30 @@
1391
1354
  "figure.label.pipe-end-width": "ancho final (X)",
1392
1355
  "figure.label.pipe-end-depth": "fondo final (Z)",
1393
1356
  "figure.text.pipe-end-same": "Igual que el inicio. Si cambia, llega a ese tamaño al final, de forma uniforme a lo largo.",
1357
+ "figure.text.palette-colour-invalid": "El color '{token}' de la paleta de esta figura es {colour}, que no es un color #rrggbb de seis dígitos, así que no se puede editar.",
1358
+ "figure.text.palette-colour-missing": "El color '{token}' de la pieza '{part}' no está en la paleta de esta figura, así que no se puede editar. Vuelva a elegir el color de esa pieza.",
1359
+ "figure.text.palette-colour-taken": "El color '{token}' ya es {has} en esta figura. Cambiarlo a {asked} cambiaría todas las demás piezas que lo usan, así que no se cambió. Para cambiar solo esta pieza, elija un valor de color en el panel de color.",
1360
+ "figure.text.palette-glow-missing": "El color de brillo '{token}' de la pieza '{part}' no está en la paleta de esta figura, así que no se puede editar. Vuelva a elegir el color de brillo de esa pieza.",
1361
+ "figure.text.state-range-order": "El rango del control '{input}' debe ir de un valor menor a uno mayor. Ahora es {min} a {max}.",
1362
+ "figure.text.state-range-holds-zero": "El control '{input}' mueve una articulación, así que su rango debe incluir 0 (la pose de reposo). Ahora es {min} a {max}.",
1363
+ "figure.text.state-range-endless": "El control '{input}' gira una articulación sin fin, así que no tiene rango que fijar.",
1364
+ "figure.text.state-range-start": "El valor inicial del control '{input}' debe estar dentro de su rango, {min} a {max}.",
1365
+ "figure.text.state-sweep": "El tiempo que tarda el control '{input}' de un extremo a otro debe ser 0 segundos o más. Ahora es {sweep}.",
1366
+ "figure.text.clip-name-empty": "El nombre del clip '{clip}' está vacío. Escriba un nombre.",
1367
+ "figure.text.clip-duration": "El clip '{clip}' debe durar más de 0 segundos. Ahora es {duration}.",
1368
+ "figure.text.group-blend": "El tiempo de transición del grupo de movimiento '{group}' debe ser 0 segundos o más. Ahora es {seconds}.",
1369
+ "figure.text.id-invalid": "'{id}' no puede ser el nombre de un control. Empiece con una letra y use solo letras, dígitos y _ . -",
1370
+ "figure.text.id-in-use": "'{id}' ya es el nombre de un valor de esta figura. Escriba otro nombre.",
1371
+ "figure.text.range-order": "El mínimo del valor '{input}' debe ser menor que su máximo. Ahora es {min} a {max}.",
1372
+ "figure.text.row-pitch-not-positive": "La separación de la fila de la pieza '{part}' debe ser mayor que 0 mm. Ahora es {pitch} mm.",
1373
+ "figure.text.axis-followed": "La articulación {followers} sigue el movimiento de la articulación '{joint}', así que ese movimiento no se puede quitar ni detener. Cambie primero la articulación que lo sigue.",
1374
+ "figure.text.detach-would-turn": "La pieza '{part}' está girada junto con '{parent}', a la que está fijada; soltarla cambiaría su postura, así que no se soltó.",
1375
+ "figure.text.state-size-inside": "La pieza '{part}' es hueca ({least} mm por dentro), así que el tamaño que fija un control debe superar {least} mm en ambos extremos.",
1376
+ "figure.text.pipe-length-not-positive": "La longitud del tramo {piece} del tubo (un tramo recto) debe ser mayor que 0 mm. Ahora es {value} mm.",
1377
+ "figure.text.pipe-radius-not-positive": "El radio del tramo {piece} del tubo (una curva) debe ser mayor que 0 mm. Ahora es {value} mm.",
1378
+ "figure.text.pipe-bend-angle": "El ángulo de la curva {piece} del tubo debe ser un número entero de 1° a 359°. Ahora es {angle}°.",
1379
+ "figure.text.pipe-wall-negative": "La pared del tubo debe ser de 0 mm o más. Ahora es {wall} mm.",
1380
+ "figure.text.pose-seconds": "El tiempo de movimiento del enlace de datos '{binding}' debe ser 0 segundos o más. Ahora es {seconds}.",
1381
+ "figure.text.section-size-not-positive": "Con este valor, el tamaño de una pieza queda en 0 o menos. Si alguna pieza sigue este valor, revise su proporción o diferencia.",
1394
1382
  "figure.button.pipe-end-same": "un solo grosor"
1395
1383
  }
@@ -361,17 +361,6 @@
361
361
  "figure.label.tools": "align",
362
362
  "figure.label.transparent": "transparent",
363
363
  "figure.label.triangles": "triangles",
364
- "figure.label.unit-none": "単位なし",
365
- "figure.label.unit-type-it": "直接入力…",
366
- "figure.label.unit-mm": "mm",
367
- "figure.label.unit-cm": "cm",
368
- "figure.label.unit-m": "m",
369
- "figure.label.unit-rad": "ラジアン",
370
- "figure.label.unit-deg": "度",
371
- "figure.label.unit-s": "秒",
372
- "figure.label.unit-ratio": "比率",
373
- "figure.label.unit-percent": "%",
374
- "figure.label.unit-count": "個",
375
364
  "figure.label.weight": "描画コスト",
376
365
  "figure.text.accepts-anything": "空欄なら何でも可",
377
366
  "figure.text.accepts-explained": "このスロットに置けるタイプです。カンマで区切り、空欄なら制限はありません。",
@@ -407,7 +396,7 @@
407
396
  "figure.text.center-on-z": "図形の Z 中央 (Z=0) へ移動します。",
408
397
  "figure.text.clip-is-time-running": "時間に沿って再生されます。インスタンスが渡す値は再生速度です。",
409
398
  "figure.text.clip-name-is-how-it-is-driven": "インスタンスはこの名前で再生速度を渡します。",
410
- "figure.text.clip-name-taken": "同じ名前の clip がすでにあります。別の名前を入力してください。",
399
+ "figure.text.clip-name-taken": "'{name}' はすでに別のクリップの名前です。クリップ '{clip}' に別の名前を付けてください。",
411
400
  "figure.text.clip-needs-a-name": "名前を入力してください。",
412
401
  "figure.text.collapse-parts-pane": "collapse the part list",
413
402
  "figure.text.collapse-properties-pane": "collapse the property pane",
@@ -1043,8 +1032,6 @@
1043
1032
  "figure.text.motion-groups-explained": "同じグループのクリップは一度に一つだけ再生します。同じ操作値を複数のクリップで動かすには、それらを一つのグループに入れます。クリップを切り替えると、切替時間のあいだ直前の姿勢から新しいクリップへつながります。",
1044
1033
  "figure.label.tab-data": "データ",
1045
1034
  "figure.label.data-kind": "種類",
1046
- "figure.label.data-kind-number": "数値",
1047
- "figure.label.data-kind-boolean": "真偽",
1048
1035
  "figure.label.data-unit": "単位",
1049
1036
  "figure.label.data-min": "最小",
1050
1037
  "figure.label.data-max": "最大",
@@ -1069,9 +1056,6 @@
1069
1056
  "figure.label.data-label": "表示名",
1070
1057
  "figure.label.data-json": "JSON文字列でも読む",
1071
1058
  "figure.label.data-item-kind": "要素の種類",
1072
- "figure.label.data-kind-text": "文字",
1073
- "figure.label.data-kind-object": "オブジェクト",
1074
- "figure.label.data-kind-list": "リスト",
1075
1059
  "figure.button.add-data-field": "フィールドを追加",
1076
1060
  "figure.button.add-data-child": "{parent} の下にフィールドを追加",
1077
1061
  "figure.button.remove-data-field": "フィールドを削除",
@@ -1081,21 +1065,9 @@
1081
1065
  "figure.text.data-guessed": "フィールドが {count} 件見つかりました。",
1082
1066
  "figure.text.data-guessed-replaces": "フィールドが {count} 件見つかりました。置き換えると今のデータ形式に代わります。",
1083
1067
  "figure.button.data-use-guessed": "この形式に置き換える",
1084
- "figure.label.data-bindings": "連結",
1085
1068
  "figure.text.data-bindings-explained": "データの値をフィギュアにつなぎます。数値はそのまま・倍率・区間表で、文字と真偽はコード表で表示します。表は値ごとに部品の色と効果、操作値、動作グループをまとめて決めます。行を選ぶと編集できます。",
1086
1069
  "figure.label.data-field": "データ値",
1087
1070
  "figure.label.data-behavior": "動作",
1088
- "figure.label.data-behavior-state": "操作値 {name}",
1089
- "figure.label.data-behavior-palette": "色 {name}",
1090
- "figure.label.data-behavior-motion-clip": "{name} クリップ",
1091
- "figure.label.data-behavior-motion-speed": "{name} 倍速",
1092
- "figure.label.data-behavior-motion-paused": "{name} 一時停止",
1093
- "figure.label.data-behavior-pose": "位置 {name}",
1094
- "figure.label.data-behavior-items": "アイテム {name}",
1095
- "figure.label.data-axis-x": "X",
1096
- "figure.label.data-axis-y": "高さ(Y)",
1097
- "figure.label.data-axis-z": "Z",
1098
- "figure.label.data-axis-heading": "向き",
1099
1071
  "figure.label.data-seconds": "移動時間 (秒)",
1100
1072
  "figure.label.data-paused": "一時停止",
1101
1073
  "figure.label.data-playing": "再生",
@@ -1105,11 +1077,6 @@
1105
1077
  "figure.button.add-data-range": "区間を追加",
1106
1078
  "figure.button.data-set-result": "決める",
1107
1079
  "figure.button.remove-data-range": "区間を削除",
1108
- "figure.label.data-item-id": "アイテムID",
1109
- "figure.label.data-item-type": "アイテムタイプ",
1110
- "figure.label.data-item-place": "位置",
1111
- "figure.label.data-item-stack": "積み順",
1112
- "figure.label.data-item-status": "状態",
1113
1080
  "figure.label.data-item-slot": "既定の位置",
1114
1081
  "figure.text.data-no-behavior": "この値で動かせる動作がありません。",
1115
1082
  "figure.button.add-data-binding": "連結を追加",
@@ -1265,10 +1232,6 @@
1265
1232
  "figure.label.data-legend-role": "凡例の役割",
1266
1233
  "figure.label.data-legend-swappable": "使う側が凡例に置き換えられる",
1267
1234
  "figure.label.data-method": "方法",
1268
- "figure.label.data-method-code": "コード表",
1269
- "figure.label.data-method-ranges": "区間表",
1270
- "figure.label.data-method-scale": "倍率",
1271
- "figure.label.data-method-value": "そのまま",
1272
1235
  "figure.label.data-new-field": "新しいフィールド",
1273
1236
  "figure.label.data-sample-legend": "サンプル凡例",
1274
1237
  "figure.label.data-table-legend": "凡例 {name}",
@@ -1391,5 +1354,30 @@
1391
1354
  "figure.label.pipe-end-width": "終端の幅(X)",
1392
1355
  "figure.label.pipe-end-depth": "終端の奥行(Z)",
1393
1356
  "figure.text.pipe-end-same": "始端と同じ大きさです。変えると終端に向かってその大きさになります。",
1357
+ "figure.text.palette-colour-invalid": "このフィギュアのパレットの色 '{token}' の値 {colour} は #rrggbb の6桁の色ではないため、編集できません。",
1358
+ "figure.text.palette-colour-missing": "部品 '{part}' の色 '{token}' がこのフィギュアのパレットにないため、編集できません。その部品の色を選び直してください。",
1359
+ "figure.text.palette-colour-taken": "色 '{token}' はこのフィギュアですでに {has} です。{asked} に変えるとこの色を使う他の部品もすべて変わるため、変更しませんでした。この部品だけを変えるには、色パネルで色の値を直接選んでください。",
1360
+ "figure.text.palette-glow-missing": "部品 '{part}' の発光色 '{token}' がこのフィギュアのパレットにないため、編集できません。その部品の発光色を選び直してください。",
1361
+ "figure.text.state-range-order": "操作値 '{input}' の範囲は小さい値から大きい値の順でなければなりません。現在 {min} ~ {max} です。",
1362
+ "figure.text.state-range-holds-zero": "操作値 '{input}' は関節の値なので、範囲に 0(基本姿勢)を含める必要があります。現在 {min} ~ {max} です。",
1363
+ "figure.text.state-range-endless": "操作値 '{input}' は無限に回る関節の値なので、範囲は設定できません。",
1364
+ "figure.text.state-range-start": "操作値 '{input}' の開始値は範囲 {min} ~ {max} の中でなければなりません。",
1365
+ "figure.text.state-sweep": "操作値 '{input}' が端から端まで動く時間は 0 秒以上でなければなりません。現在 {sweep} です。",
1366
+ "figure.text.clip-name-empty": "クリップ '{clip}' の名前が空です。名前を入力してください。",
1367
+ "figure.text.clip-duration": "クリップ '{clip}' の長さは 0 秒より長くなければなりません。現在 {duration} です。",
1368
+ "figure.text.group-blend": "動作グループ '{group}' の切り替え時間は 0 秒以上でなければなりません。現在 {seconds} です。",
1369
+ "figure.text.id-invalid": "'{id}' は操作値の名前に使えません。英字で始め、英字・数字・_ . - のみを使ってください。",
1370
+ "figure.text.id-in-use": "'{id}' はこのフィギュアにすでにある値の名前です。別の名前を入力してください。",
1371
+ "figure.text.range-order": "値 '{input}' の範囲は最小値が最大値より小さくなければなりません。現在 {min} ~ {max} です。",
1372
+ "figure.text.row-pitch-not-positive": "部品 '{part}' の繰り返し配列の間隔は 0 mm より大きくなければなりません。現在 {pitch} mm です。",
1373
+ "figure.text.axis-followed": "関節 {followers} が関節 '{joint}' の動きに従っているため、その動きを削除・停止できません。先に従っている関節を変更してください。",
1374
+ "figure.text.detach-would-turn": "部品 '{part}' は '{parent}' に付いて一緒に回転しているため、外すと姿勢が変わります。そのため外しませんでした。",
1375
+ "figure.text.state-size-inside": "部品 '{part}' は中空(内側 {least} mm)なので、操作値で決めるサイズは両端とも {least} mm より大きくなければなりません。",
1376
+ "figure.text.pipe-length-not-positive": "管の {piece} 番目の区間(直線)の長さは 0 mm より大きくなければなりません。現在 {value} mm です。",
1377
+ "figure.text.pipe-radius-not-positive": "管の {piece} 番目の区間(曲げ)の半径は 0 mm より大きくなければなりません。現在 {value} mm です。",
1378
+ "figure.text.pipe-bend-angle": "管の {piece} 番目の曲げ角度は 1° ~ 359° の整数でなければなりません。現在 {angle}° です。",
1379
+ "figure.text.pipe-wall-negative": "管の壁の厚さは 0 mm 以上でなければなりません。現在 {wall} mm です。",
1380
+ "figure.text.pose-seconds": "データ連携 '{binding}' の移動時間は 0 秒以上でなければなりません。現在 {seconds} です。",
1381
+ "figure.text.section-size-not-positive": "この値では部品のサイズが 0 以下になります。この値に従う部品があれば、その比率・差を確認してください。",
1394
1382
  "figure.button.pipe-end-same": "太さを一定に"
1395
1383
  }