@things-factory/figure-ui 10.1.142 → 10.1.146
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/client/modeller/refusals.ts +16 -7
- package/dist-client/modeller/refusals.js +18 -8
- package/dist-client/modeller/refusals.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/test/align.test.ts +2 -2
- package/test/browser/modeller-page.js +3 -3
- package/test/browser/modeller.check.mjs +93 -51
- package/test/cavity-drawing.test.ts +1 -1
- package/test/clip-consent.test.ts +1 -1
- package/test/clips.test.ts +1 -1
- package/test/command-preview.test.ts +5 -5
- package/test/controls.test.ts +1 -1
- package/test/fasten-drawing.test.ts +1 -1
- package/test/fields.test.ts +1 -1
- package/test/figure-assistant-surface.test.ts +1 -1
- package/test/first-minute.test.ts +1 -1
- package/test/first-save-suggest.test.ts +1 -1
- package/test/gizmo.test.ts +1 -1
- package/test/layout.test.ts +2 -2
- package/test/links.test.ts +1 -1
- package/test/maturity.test.ts +1 -1
- package/test/parts-wiring.test.ts +3 -3
- package/test/properties.test.ts +1 -1
- package/test/proposal-record.test.ts +1 -1
- package/test/refusals.test.ts +40 -0
- package/test/release-entry.test.ts +1 -1
- package/test/revert-choice.test.ts +1 -1
- package/test/revert-keeping.test.ts +1 -1
- package/test/revision.test.ts +7 -4
- package/test/save-landed.test.ts +2 -2
- package/test/section-editor.test.ts +3 -3
- package/test/section-panel.test.ts +1 -1
- package/test/verdict.test.ts +1 -1
- package/translations/en.json +4 -1
- package/translations/es.json +991 -0
- package/translations/ja.json +3 -0
- package/translations/ko.json +3 -0
- package/translations/ms.json +3 -0
- package/translations/zh.json +3 -0
package/test/refusals.test.ts
CHANGED
|
@@ -23,6 +23,46 @@ test('위쪽 끝도, 부품 이름이 없어도', () => {
|
|
|
23
23
|
assert.ok(said.includes('Y 축을') && said.includes('15,000 mm'), said)
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
+
/*
|
|
27
|
+
* 범위가 지금 크기를 담지 않는 거절도 숫자와 함께 (Figure 레인 인계, 2026-10-11: 400 mm 상자에 가장 작은 크기 500 을
|
|
28
|
+
* 넣으면 「이 편집은 할 수 없습니다. (INSTANCE_SIZE_RANGE)」만 떴다 — 이 문장은 below/above 꼴이 아니라 읽히지 않았다).
|
|
29
|
+
*/
|
|
30
|
+
test('범위가 지금 크기를 담지 않으면 축 · 지금 크기 · 범위를 말한다', () => {
|
|
31
|
+
const said = sizeRangeRefusalOf(refusal('INSTANCE_SIZE_RANGE: size.x: the authored size 400 mm must lie within 500…4000 mm'), say)!
|
|
32
|
+
assert.ok(said.includes('X') && said.includes('400 mm') && said.includes('500') && said.includes('4,000'), said)
|
|
33
|
+
assert.ok(!/authored|lie within/.test(said), said)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('가장 작은 크기가 0 이하이면 그렇게 말한다', () => {
|
|
37
|
+
const said = sizeRangeRefusalOf(refusal('INSTANCE_SIZE_RANGE: size.z: the smallest size must be above zero; got 0'), say)!
|
|
38
|
+
assert.ok(said.includes('Z') && said.includes('0 보다'), said)
|
|
39
|
+
assert.ok(!/smallest|above zero/.test(said), said)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('지금 크기에서 이미 길이가 없는 부품이 있으면 그렇게 말한다', () => {
|
|
43
|
+
const said = sizeRangeRefusalOf(refusal('INSTANCE_SIZE_RANGE: size.y: the figure is authored at 120 mm, where a length is already gone; change the link or the size'), say)!
|
|
44
|
+
assert.ok(said.includes('Y') && said.includes('120 mm'), said)
|
|
45
|
+
assert.ok(!/authored|already gone/.test(said), said)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('INSTANCE_SIZE_RANGE 의 문장 넷이 모두 사람의 말이 된다 — 커널이 쓰는 꼴 그대로', async () => {
|
|
49
|
+
const { addFigureBox, applyFigureAuthoring, createFigureAsset } = await import('@hatiolab/figure-model')
|
|
50
|
+
const box = addFigureBox(createFigureAsset('Box'), { id: 'box', size: { x: 400, y: 300, z: 200 }, position: { x: 0, y: 150, z: 0 } })
|
|
51
|
+
const refused = (action: unknown) => {
|
|
52
|
+
try {
|
|
53
|
+
applyFigureAuthoring(box, action as never)
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return e
|
|
56
|
+
}
|
|
57
|
+
assert.fail('the kernel took it')
|
|
58
|
+
}
|
|
59
|
+
const range = (x: { min: number; max: number }) => ({ kind: 'declare-instance-size', range: { x, y: { min: 100, max: 3000 }, z: { min: 100, max: 3000 } } })
|
|
60
|
+
for (const error of [refused(range({ min: 500, max: 4000 })), refused(range({ min: 0, max: 4000 }))]) {
|
|
61
|
+
assert.equal((error as { code?: string }).code, 'INSTANCE_SIZE_RANGE', String(error))
|
|
62
|
+
assert.ok(sizeRangeRefusalOf(error, say), `not read: ${(error as Error).message}`)
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
|
|
26
66
|
test('다른 거절은 건드리지 않는다', () => {
|
|
27
67
|
assert.equal(sizeRangeRefusalOf(Object.assign(new Error('x'), { code: 'EDIT_TARGET' }), say), null)
|
|
28
68
|
})
|
|
@@ -21,7 +21,7 @@ test('beside the save: the release state, and a way to the release section that
|
|
|
21
21
|
assert.ok(page.includes('.releaseState=${this.row}'), 'as the server gave it')
|
|
22
22
|
const ko = JSON.parse(read('../translations/ko.json'))
|
|
23
23
|
assert.equal(ko['figure.text.release-state-draft-over'].replace('{version}', '3'), '초안 · 보드는 v3 사용')
|
|
24
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
24
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
25
25
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
26
26
|
for (const key of ['figure.text.release-state-released', 'figure.text.release-state-draft-over', 'figure.text.release-state-draft-new', 'figure.button.release']) assert.ok(t[key], `${lang} ${key}`)
|
|
27
27
|
}
|
|
@@ -32,7 +32,7 @@ test('saved first, and known to be -- or nothing is reverted', () => {
|
|
|
32
32
|
const save = page.slice(page.indexOf(' private saveBeforeRevert'), page.indexOf(' private reverted'))
|
|
33
33
|
assert.ok(save.includes("return this.session.read().dirty ? { ok: false } : { ok: true }"), 'ok only when the session says saved')
|
|
34
34
|
assert.ok(save.includes("code === 'SAVE_UNREACHABLE' ? i18next.t('figure.text.save-unreachable')"), 'not known is said as not known')
|
|
35
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
35
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
36
36
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
37
37
|
for (const key of ['figure.text.revert-with-unsaved', 'figure.button.revert-saving', 'figure.button.revert-discarding', 'figure.text.revert-held-unsaved']) assert.ok(t[key], `${lang} ${key}`)
|
|
38
38
|
}
|
|
@@ -81,7 +81,7 @@ test('the screen: reverting is on, sends the confirmed source and a request id,
|
|
|
81
81
|
assert.ok(panel.includes('<section kept>'), 'kept drafts in their own section, not among the releases')
|
|
82
82
|
assert.ok(panel.includes('${newest && !kept.restoredAt'), 'only the newest, not yet brought back, is offered')
|
|
83
83
|
assert.ok(read('../client/pages/figure-modeller-page.ts').includes('.serverSource=${() => this.session.confirmedSource()}'))
|
|
84
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) {
|
|
84
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) {
|
|
85
85
|
const t = JSON.parse(read(`../translations/${lang}.json`))
|
|
86
86
|
for (const key of [
|
|
87
87
|
'figure.label.kept-drafts',
|
package/test/revision.test.ts
CHANGED
|
@@ -118,8 +118,11 @@ test('the ends of a motion evaluate the figure at its two ends, and nothing is c
|
|
|
118
118
|
assert.deepEqual([work.compiles, work.evaluations], [0, 2])
|
|
119
119
|
})
|
|
120
120
|
|
|
121
|
-
/*
|
|
122
|
-
|
|
121
|
+
/*
|
|
122
|
+
* A compile also evaluates the figure once (its stored values must run): two compiles, and the read's evaluation. The
|
|
123
|
+
* version's asset is the one the edit installed (ADR-0119 decision 6), not compiled again.
|
|
124
|
+
*/
|
|
125
|
+
test('a value changed, read again as the next version, is two compiles at most: the edit and its read', async () => {
|
|
123
126
|
const session = await opened(arm)
|
|
124
127
|
const field = session.revisionNow()!.fields.find(f => f.role !== 'state')!
|
|
125
128
|
const parts = session.revisionNow()!.geometry.map(g => g.id).filter(id => !id.startsWith('['))
|
|
@@ -129,8 +132,8 @@ test('a value changed, read again as the next version, is three compiles at most
|
|
|
129
132
|
for (const part of parts) selectionQuestions(session.revisionNow()!, part)
|
|
130
133
|
session.source()
|
|
131
134
|
})
|
|
132
|
-
assert.ok(work.compiles <=
|
|
133
|
-
assert.ok(work.evaluations <=
|
|
135
|
+
assert.ok(work.compiles <= 2, `compiles ${work.compiles}`)
|
|
136
|
+
assert.ok(work.evaluations <= 3, `evaluations ${work.evaluations}`)
|
|
134
137
|
})
|
|
135
138
|
|
|
136
139
|
/*
|
package/test/save-landed.test.ts
CHANGED
|
@@ -105,7 +105,7 @@ test('not reached is said as not known, not as not saved', () => {
|
|
|
105
105
|
assert.ok(write.includes("return new GraphQLError(message, { extensions: { code: 'FIGURE_SOURCE_CONFLICT' } })"), 'the server says a conflict is one')
|
|
106
106
|
const editor = read('../client/modeller/editor.ts')
|
|
107
107
|
assert.ok(editor.includes("=== 'SAVE_UNREACHABLE' ? i18next.t('figure.text.save-unreachable')"))
|
|
108
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) assert.ok(JSON.parse(read(`../translations/${lang}.json`))['figure.text.save-unreachable'], lang)
|
|
108
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) assert.ok(JSON.parse(read(`../translations/${lang}.json`))['figure.text.save-unreachable'], lang)
|
|
109
109
|
assert.match(JSON.parse(read('../translations/ko.json'))['figure.text.save-unreachable'], /저장됐는지 확인하지 못했습니다/)
|
|
110
110
|
})
|
|
111
111
|
|
|
@@ -116,7 +116,7 @@ test('a released figure changed back to a draft is told, whichever change did it
|
|
|
116
116
|
assert.ok(page.includes('if (this.session.read().id === id) this.toldDemoted(this.row.state, figure.state)'), 'after a save, read back')
|
|
117
117
|
assert.ok(page.includes('this.toldDemoted(this.row.state, result.figure.state)'), 'after category or tags')
|
|
118
118
|
assert.ok(page.includes("...(result.figure.state ? { state: result.figure.state as 'draft' | 'released' } : {})"), 'and the badge follows')
|
|
119
|
-
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh']) assert.ok(JSON.parse(read(`../translations/${lang}.json`))['figure.text.released-now-draft'], lang)
|
|
119
|
+
for (const lang of ['en', 'ja', 'ko', 'ms', 'zh', 'es']) assert.ok(JSON.parse(read(`../translations/${lang}.json`))['figure.text.released-now-draft'], lang)
|
|
120
120
|
})
|
|
121
121
|
|
|
122
122
|
/* Designer 2026-09-27: read back, every field the save writes must match; a refusal on grounds is never a save. */
|
|
@@ -148,7 +148,7 @@ test('편집 창의 말은 다섯 말 모두에 있다', () => {
|
|
|
148
148
|
const drawing = read('../client/modeller/section-drawing.ts')
|
|
149
149
|
keys.push(...new Set([...drawing.matchAll(/'(figure\.text\.section-issue-[\w-]+)'/g)].map(m => m[1]!)))
|
|
150
150
|
assert.ok(keys.length >= 20, String(keys.length))
|
|
151
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
151
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
152
152
|
const words = JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>
|
|
153
153
|
for (const key of keys) assert.ok(words[key], `${lang} has ${key}`)
|
|
154
154
|
}
|
|
@@ -296,7 +296,7 @@ test('ADR-0100 A5-1: 팔레트의 뿔은 삼각 단면과 꼭짓점 하나 —
|
|
|
296
296
|
assert.deepEqual(s.loft!.apex, { x: 0, z: 0 })
|
|
297
297
|
assert.equal(startingShapeKindOf(node as never), 'pyramid')
|
|
298
298
|
assert.equal(startingShapeKindOfBody(s as never), 'pyramid')
|
|
299
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
299
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
300
300
|
const t = JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>
|
|
301
301
|
for (const key of ['figure.label.primitive-pyramid', 'figure.label.section-editor-apex', 'figure.label.section-editor-apex-at', 'figure.label.section-editor-apex-mark', 'figure.text.section-editor-apex-blocked', 'figure.label.section-editor-height'])
|
|
302
302
|
assert.ok(t[key], `${lang} ${key}`)
|
|
@@ -349,7 +349,7 @@ test('닫기 전에 묻는다 (사용자 10-01): 바꾼 것이 있으면 적용
|
|
|
349
349
|
has(choice, /\$\{this\.held\s*\? nothing/)
|
|
350
350
|
/* Picking another part while changes wait keeps the part selected until the editor closed. */
|
|
351
351
|
has(editor, /this\.selected = editing\s*editor\.requestClose\(\)\.then\(closed => \{\s*if \(closed && this\.selected === editing\) this\.selected = next/)
|
|
352
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
352
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
353
353
|
const t = JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>
|
|
354
354
|
for (const key of ['figure.text.section-editor-close-changed', 'figure.text.section-editor-close-invalid', 'figure.button.section-editor-apply-and-close', 'figure.button.section-editor-discard', 'figure.button.keep-editing'])
|
|
355
355
|
assert.ok(t[key], `${lang} ${key}`)
|
|
@@ -93,7 +93,7 @@ test('편집기는 단면 칸의 명령을 세션의 이력 하나로 돌리고,
|
|
|
93
93
|
has(editor, /<figure-section-panel[\s\S]*?@command=\$\{\(e: CustomEvent\) => this\.linkCommand\(e\.detail\.action\)\}/)
|
|
94
94
|
has(editor, /@command-live=\$\{\(e: CustomEvent\) => this\.holdAction\(e\.detail\.action\)\}/)
|
|
95
95
|
has(editor, /private linkCommand\(action: FigureAuthoringAction, from: 'section-editor' \| 'panel' = 'panel'\): boolean \{\s*\/\*[^*]*\*\/\s*this\.holdAction\(null\)/, 'a command sent lets go of the one in hand')
|
|
96
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
96
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
97
97
|
const words = JSON.parse(read(`../translations/${lang}.json`)) as Record<string, string>
|
|
98
98
|
const used = [...new Set([...(panel + drawing).matchAll(/'(figure\.(?:label|text)\.section-[\w-]+|figure\.label\.group-section)'/g)].map(m => m[1]!))]
|
|
99
99
|
/* The keys the panel builds: each result, and the wall's and the ends' over-limit words. */
|
package/test/verdict.test.ts
CHANGED
|
@@ -74,7 +74,7 @@ test('판정 탭의 말은 다섯 말 모두에 있다 — 없으면 키가 그
|
|
|
74
74
|
const maturity = read('../client/modeller/maturity.ts')
|
|
75
75
|
const keys = [...(verdict + maturity).matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1])
|
|
76
76
|
assert.ok(keys.includes('figure.text.verdict-asking'), '전제: 삼항 안의 키도 센다')
|
|
77
|
-
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms']) {
|
|
77
|
+
for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
|
|
78
78
|
const table = JSON.parse(read(`../translations/${lang}.json`))
|
|
79
79
|
for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
|
|
80
80
|
}
|
package/translations/en.json
CHANGED
|
@@ -71,6 +71,9 @@
|
|
|
71
71
|
"figure.text.instance-range-can-widen": "measured again with the parts as they are: {ranges} mm.",
|
|
72
72
|
"figure.text.size-range-too-small": "below {mm} mm on {axis} the {part} part has no length, yet the figure is declared to be placed smaller, so this change cannot be made. use \"measure again\" under the sizes it can be placed at.",
|
|
73
73
|
"figure.text.size-range-too-large": "above {mm} mm on {axis} the {part} part cannot be drawn, yet the figure is declared to be placed larger, so this change cannot be made. use \"measure again\" under the sizes it can be placed at.",
|
|
74
|
+
"figure.text.size-range-leaves-out-size": "the {axis} range of the sizes it can be placed at, {min}–{max} mm, does not hold its size now, {at} mm. change the range so that it does.",
|
|
75
|
+
"figure.text.size-range-from-zero": "the smallest {axis} size it can be placed at must be above 0.",
|
|
76
|
+
"figure.text.size-range-part-gone": "at its size now, {at} mm on {axis}, a part already has no length. change the link or the size.",
|
|
74
77
|
"figure.text.input-range-size": "figure size {axis} can only be set between {min} and {max} mm. value given: {value} mm",
|
|
75
78
|
"figure.text.input-range": "this value can only be set between {min} and {max}. value given: {value}",
|
|
76
79
|
"figure.text.mounting-face-needs-space": "declare the space first to choose one.",
|
|
@@ -958,7 +961,7 @@
|
|
|
958
961
|
"figure.text.summary-size-part-share-plus": "Its {axes} is {pct}% of {other}'s {measureWord} plus {plus} mm.",
|
|
959
962
|
"figure.text.summary-size-input": "Its {axes} follows {field}.",
|
|
960
963
|
"figure.text.summary-size-worked-out": "Its {axes} is worked out from other measures.",
|
|
961
|
-
"figure.text.held-by-fastening": "It is fastened: its place and turn are set in
|
|
964
|
+
"figure.text.held-by-fastening": "It is fastened: its place and turn are set in Fastening.",
|
|
962
965
|
"figure.text.held-worked-out": "{group} are worked out from other values and cannot be typed here.",
|
|
963
966
|
"figure.text.face-mapping-lost": "Faces this takes away: {faces}. Choose where what stands on them goes.",
|
|
964
967
|
"figure.label.face-mapping-to": "Where {part} goes",
|