@things-factory/figure-ui 10.1.65 → 10.1.67
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/graphql/index.ts +0 -45
- package/client/modeller/figure-history-panel.ts +6 -145
- package/client/modeller/figure-v3-capability.ts +2 -2
- package/client/modeller/figure-v3-clips.ts +2 -2
- package/client/modeller/figure-v3-controls.ts +2 -2
- package/client/modeller/figure-v3-first-save.ts +1 -1
- package/client/modeller/figure-v3-links.ts +6 -6
- package/client/modeller/figure-v3-material.ts +11 -11
- package/client/modeller/figure-v3-parts.ts +7 -10
- package/client/modeller/figure-v3-preview.ts +3 -3
- package/client/modeller/figure-v3-properties.ts +5 -6
- package/client/modeller/figure-v3-verdict.ts +9 -10
- package/client/modeller/figure-view.ts +2 -3
- package/client/modeller/modeller-route.ts +2 -3
- package/client/modeller/scene-budget.ts +0 -14
- package/client/modeller/side-panel-styles.ts +5 -5
- package/client/modeller/v3-align.ts +16 -6
- package/client/modeller/v3-canvas.ts +12 -12
- package/client/modeller/v3-clips.ts +1 -1
- package/client/modeller/v3-controls.ts +1 -1
- package/client/modeller/v3-editor.ts +21 -21
- package/client/modeller/v3-fields.ts +1 -1
- package/client/modeller/v3-figure-facts.ts +1 -1
- package/client/modeller/v3-first-save.ts +3 -4
- package/client/modeller/v3-gizmo.ts +5 -5
- package/client/modeller/v3-help.ts +1 -1
- package/client/modeller/v3-joint-fan.ts +4 -5
- package/client/modeller/v3-keys.ts +1 -1
- package/client/modeller/v3-links.ts +9 -12
- package/client/modeller/v3-maturity.ts +22 -26
- package/client/modeller/v3-refusals.ts +17 -0
- package/client/modeller/v3-renderer.ts +1 -1
- package/client/modeller/v3-stage-model.ts +2 -2
- package/client/modeller/v3-thumbnail.ts +1 -3
- package/client/pages/figure-v3-modeller-page.ts +8 -11
- package/client/types.ts +2 -31
- package/dist-client/graphql/index.js +0 -45
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +4 -37
- package/dist-client/modeller/figure-history-panel.js +8 -154
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-v3-capability.d.ts +1 -1
- package/dist-client/modeller/figure-v3-capability.js +1 -1
- package/dist-client/modeller/figure-v3-capability.js.map +1 -1
- package/dist-client/modeller/figure-v3-clips.d.ts +2 -2
- package/dist-client/modeller/figure-v3-clips.js +2 -2
- package/dist-client/modeller/figure-v3-clips.js.map +1 -1
- package/dist-client/modeller/figure-v3-controls.d.ts +1 -1
- package/dist-client/modeller/figure-v3-controls.js +2 -2
- package/dist-client/modeller/figure-v3-controls.js.map +1 -1
- package/dist-client/modeller/figure-v3-first-save.d.ts +1 -1
- package/dist-client/modeller/figure-v3-first-save.js +1 -1
- package/dist-client/modeller/figure-v3-first-save.js.map +1 -1
- package/dist-client/modeller/figure-v3-links.d.ts +5 -5
- package/dist-client/modeller/figure-v3-links.js +6 -6
- package/dist-client/modeller/figure-v3-links.js.map +1 -1
- package/dist-client/modeller/figure-v3-material.d.ts +6 -6
- package/dist-client/modeller/figure-v3-material.js +11 -11
- package/dist-client/modeller/figure-v3-material.js.map +1 -1
- package/dist-client/modeller/figure-v3-parts.d.ts +6 -8
- package/dist-client/modeller/figure-v3-parts.js +1 -1
- package/dist-client/modeller/figure-v3-parts.js.map +1 -1
- package/dist-client/modeller/figure-v3-preview.js.map +1 -1
- package/dist-client/modeller/figure-v3-properties.d.ts +4 -5
- package/dist-client/modeller/figure-v3-properties.js +5 -6
- package/dist-client/modeller/figure-v3-properties.js.map +1 -1
- package/dist-client/modeller/figure-v3-verdict.d.ts +2 -2
- package/dist-client/modeller/figure-v3-verdict.js +9 -10
- package/dist-client/modeller/figure-v3-verdict.js.map +1 -1
- package/dist-client/modeller/figure-view.d.ts +2 -3
- package/dist-client/modeller/figure-view.js.map +1 -1
- package/dist-client/modeller/modeller-route.d.ts +2 -3
- package/dist-client/modeller/modeller-route.js +2 -3
- package/dist-client/modeller/modeller-route.js.map +1 -1
- package/dist-client/modeller/scene-budget.d.ts +0 -11
- package/dist-client/modeller/scene-budget.js +0 -12
- package/dist-client/modeller/scene-budget.js.map +1 -1
- package/dist-client/modeller/side-panel-styles.d.ts +2 -2
- package/dist-client/modeller/side-panel-styles.js +5 -5
- package/dist-client/modeller/side-panel-styles.js.map +1 -1
- package/dist-client/modeller/v3-align.d.ts +9 -5
- package/dist-client/modeller/v3-align.js +13 -3
- package/dist-client/modeller/v3-align.js.map +1 -1
- package/dist-client/modeller/v3-canvas.d.ts +4 -4
- package/dist-client/modeller/v3-canvas.js +9 -9
- package/dist-client/modeller/v3-canvas.js.map +1 -1
- package/dist-client/modeller/v3-clips.js.map +1 -1
- package/dist-client/modeller/v3-controls.js.map +1 -1
- package/dist-client/modeller/v3-editor.d.ts +13 -13
- package/dist-client/modeller/v3-editor.js +19 -18
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-fields.js.map +1 -1
- package/dist-client/modeller/v3-figure-facts.d.ts +1 -1
- package/dist-client/modeller/v3-figure-facts.js +1 -1
- package/dist-client/modeller/v3-figure-facts.js.map +1 -1
- package/dist-client/modeller/v3-first-save.js +3 -4
- package/dist-client/modeller/v3-first-save.js.map +1 -1
- package/dist-client/modeller/v3-gizmo.d.ts +5 -5
- package/dist-client/modeller/v3-gizmo.js +1 -1
- package/dist-client/modeller/v3-gizmo.js.map +1 -1
- package/dist-client/modeller/v3-help.d.ts +1 -1
- package/dist-client/modeller/v3-help.js +1 -1
- package/dist-client/modeller/v3-help.js.map +1 -1
- package/dist-client/modeller/v3-joint-fan.js +4 -5
- package/dist-client/modeller/v3-joint-fan.js.map +1 -1
- package/dist-client/modeller/v3-keys.js.map +1 -1
- package/dist-client/modeller/v3-links.d.ts +5 -7
- package/dist-client/modeller/v3-links.js +6 -8
- package/dist-client/modeller/v3-links.js.map +1 -1
- package/dist-client/modeller/v3-maturity.d.ts +7 -7
- package/dist-client/modeller/v3-maturity.js +20 -24
- package/dist-client/modeller/v3-maturity.js.map +1 -1
- package/dist-client/modeller/v3-refusals.d.ts +6 -0
- package/dist-client/modeller/v3-refusals.js +18 -0
- package/dist-client/modeller/v3-refusals.js.map +1 -1
- package/dist-client/modeller/v3-renderer.js +1 -1
- package/dist-client/modeller/v3-renderer.js.map +1 -1
- package/dist-client/modeller/v3-stage-model.js.map +1 -1
- package/dist-client/modeller/v3-thumbnail.js +1 -3
- package/dist-client/modeller/v3-thumbnail.js.map +1 -1
- package/dist-client/pages/figure-v3-modeller-page.d.ts +4 -5
- package/dist-client/pages/figure-v3-modeller-page.js +8 -11
- package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.fui.tsbuildinfo +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +2 -29
- package/dist-client/types.js +3 -0
- package/dist-client/types.js.map +1 -1
- package/docs/v3-product-cutover.md +14 -24
- package/package.json +5 -5
- package/test/browser/modeller-page.js +6 -7
- package/test/browser/modeller.check.mjs +9 -16
- package/test/fixtures/joint-column.json +1138 -0
- package/test/fixtures/joint-roller.json +872 -0
- package/test/fixtures/robot-arm-6dof.json +2776 -0
- package/test/i18n-prefix-guard.test.ts +0 -31
- package/test/modeller-route.test.ts +2 -3
- package/test/v3-align.test.ts +9 -4
- package/test/v3-clips.test.ts +1 -1
- package/test/v3-controls.test.ts +1 -1
- package/test/v3-fields.test.ts +4 -4
- package/test/v3-first-save.test.ts +1 -1
- package/test/v3-gizmo.test.ts +5 -5
- package/test/v3-joint-fan.test.ts +12 -42
- package/test/v3-keys.test.ts +1 -1
- package/test/v3-layout.test.ts +4 -5
- package/test/v3-links.test.ts +3 -3
- package/test/v3-material.test.ts +3 -3
- package/test/v3-maturity.test.ts +8 -9
- package/test/v3-parts-wiring.test.ts +3 -3
- package/test/v3-preview.test.ts +1 -1
- package/test/v3-product-write.test.ts +1 -1
- package/test/v3-properties.test.ts +3 -3
- package/test/v3-refusals.test.ts +12 -0
- package/test/v3-release-panel.test.ts +5 -5
- package/test/v3-scene-canvas.test.ts +2 -2
- package/test/v3-theme.test.ts +2 -2
- package/test/v3-verdict.test.ts +2 -2
- package/test/v3-view.test.ts +2 -2
- package/translations/en.json +2 -143
- package/translations/ja.json +2 -143
- package/translations/ko.json +2 -143
- package/translations/ms.json +2 -143
- package/translations/zh.json +2 -143
- package/client/modeller/figure-source.ts +0 -437
- package/client/modeller/part-edits.ts +0 -312
- package/dist-client/modeller/figure-source.d.ts +0 -238
- package/dist-client/modeller/figure-source.js +0 -193
- package/dist-client/modeller/figure-source.js.map +0 -1
- package/dist-client/modeller/part-edits.d.ts +0 -89
- package/dist-client/modeller/part-edits.js +0 -270
- package/dist-client/modeller/part-edits.js.map +0 -1
- package/test/expect.ts +0 -57
- package/test/figure-source.test.ts +0 -511
- package/test/part-edits.test.ts +0 -350
|
@@ -18,16 +18,12 @@ import { capacityFrom } from './scene-budget.js'
|
|
|
18
18
|
import type { FigureCapacity } from './scene-budget.js'
|
|
19
19
|
|
|
20
20
|
/*
|
|
21
|
-
* The
|
|
22
|
-
* else — 「왜 이렇게 너프되었는지」)
|
|
21
|
+
* The modeller's maturity report, read off the evaluated asset (user, 2026-09-24: the tab showed a count and nothing
|
|
22
|
+
* else — 「왜 이렇게 너프되었는지」):
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* grade A–D and its scale V2's bounds, over figure-model's `v3ScoreOf`: one screen's target shared per instance
|
|
29
|
-
* (groups, triangles) and V2's declared rule limits (independent, transparent)
|
|
30
|
-
* radar · axes V2's six axes; the two V2 could not measure stay unmeasured, detail has no V3 field
|
|
24
|
+
* grade A–D and its scale figure-model's `v3ScoreOf`: one screen's target shared per instance (groups,
|
|
25
|
+
* triangles) and the declared rule limits (independent, transparent)
|
|
26
|
+
* radar · axes six axes; reactive is not measured, and detail has no V3 field
|
|
31
27
|
* drawing cost triangles, distinct shapes, distinct materials
|
|
32
28
|
* trend what grows with the instance count and what the instances share
|
|
33
29
|
*
|
|
@@ -35,14 +31,14 @@ import type { FigureCapacity } from './scene-budget.js'
|
|
|
35
31
|
*
|
|
36
32
|
* ## What the V3 renderer shares, which is what the numbers count
|
|
37
33
|
*
|
|
38
|
-
* things-scene draws one mesh per drawn body (`figure-v3-rig.ts`) — no merging by material
|
|
34
|
+
* things-scene draws one mesh per drawn body (`figure-v3-rig.ts`) — no merging by material — so a body is
|
|
39
35
|
* a draw call. It shares geometry by provider, dimensions, segments and profile (`figure-v3-geometry.ts`, `keyOf`)
|
|
40
36
|
* and materials by token, preset, transparency and light (`materialFor`). The keys below are those keys; a
|
|
41
37
|
* distinct-shape count that used another key would describe a renderer that does not exist.
|
|
42
38
|
*
|
|
43
39
|
* ## Budgets V3 does not have
|
|
44
40
|
*
|
|
45
|
-
*
|
|
41
|
+
* Two budgets are not measured here, and are not shown as zero:
|
|
46
42
|
* - off-palette surfaces: the V3 material has no surface field, so there is nothing to count;
|
|
47
43
|
* - part count by detail level: V3 does not carry the detail level (chief architect, 2026-09-24, ADR-0087).
|
|
48
44
|
*/
|
|
@@ -56,7 +52,7 @@ export type { V3BudgetKey, V3BudgetUse, V3Score }
|
|
|
56
52
|
|
|
57
53
|
export type V3AxisKey = 'scale' | 'consistency' | 'reuse' | 'detail' | 'reactive' | 'readable'
|
|
58
54
|
|
|
59
|
-
/** One maturity axis.
|
|
55
|
+
/** One maturity axis. */
|
|
60
56
|
export interface V3MaturityAxis {
|
|
61
57
|
key: V3AxisKey
|
|
62
58
|
/** 0–1. Absent when the axis cannot be measured. */
|
|
@@ -83,7 +79,7 @@ export interface V3Report {
|
|
|
83
79
|
cost: V3Cost
|
|
84
80
|
capacity: FigureCapacity
|
|
85
81
|
score: V3Score
|
|
86
|
-
/** Distinct shapes and materials among the drawn bodies —
|
|
82
|
+
/** Distinct shapes and materials among the drawn bodies — the 「그리는 비용」 rows. */
|
|
87
83
|
distinctShapes: number
|
|
88
84
|
distinctMaterials: number
|
|
89
85
|
reuse: { distinct: number; parts: number; saved: number }
|
|
@@ -91,7 +87,7 @@ export interface V3Report {
|
|
|
91
87
|
curve: V3CurvePoint[]
|
|
92
88
|
}
|
|
93
89
|
|
|
94
|
-
/**
|
|
90
|
+
/** The board target for the scale axis: 500 assets on one drawing (design document §4.15). */
|
|
95
91
|
export const BOARD_ASSETS = 500
|
|
96
92
|
|
|
97
93
|
/**
|
|
@@ -187,11 +183,11 @@ export function materialKeyOf(body: V3AssetGeometry): string {
|
|
|
187
183
|
}
|
|
188
184
|
|
|
189
185
|
/**
|
|
190
|
-
* Does it look like one set —
|
|
186
|
+
* Does it look like one set — baselines 3 and 4, three checks.
|
|
191
187
|
*
|
|
192
188
|
* Two of them hold by construction in V3: the material has no surface field, so there is no texture, and the
|
|
193
|
-
* format refuses a segment count outside the presets (`compileV3Asset`, `QUALITY`). They are still counted,
|
|
194
|
-
*
|
|
189
|
+
* format refuses a segment count outside the presets (`compileV3Asset`, `QUALITY`). They are still counted, so
|
|
190
|
+
* the ratio stays one fault in three.
|
|
195
191
|
*/
|
|
196
192
|
function consistency(asset: V3Asset, distinctMaterials: number): V3MaturityAxis {
|
|
197
193
|
const textures = 0
|
|
@@ -205,17 +201,17 @@ function consistency(asset: V3Asset, distinctMaterials: number): V3MaturityAxis
|
|
|
205
201
|
}
|
|
206
202
|
}
|
|
207
203
|
|
|
208
|
-
/** Seen from above, these do not fill their footprint box
|
|
204
|
+
/** Seen from above, these do not fill their footprint box, by V3 provider name. */
|
|
209
205
|
const ROUND_FOOTPRINT: readonly string[] = ['cylinder', 'ellipticCylinder', 'frustum', 'sphere']
|
|
210
206
|
|
|
211
207
|
/**
|
|
212
|
-
* Can it be told apart from above —
|
|
208
|
+
* Can it be told apart from above — baseline 1.
|
|
213
209
|
*
|
|
214
210
|
* Viewpoint entropy (Vázquez et al.) over the top view, counted by colour region rather than by face so that a
|
|
215
211
|
* finer segment count does not raise it: H = −Σ p·log p over each material's share of the footprint plus the
|
|
216
212
|
* background, divided by log(`LIMITS.distinctMaterials` + 1), the most this measure can reach.
|
|
217
213
|
*
|
|
218
|
-
* The footprint is
|
|
214
|
+
* The footprint is the base box: the space the asset declares when it declares one (ADR-0044's box is V3's
|
|
219
215
|
* occupancy), otherwise the drawn bodies' own extent. Bodies are their axis-aligned world boxes
|
|
220
216
|
* (`v3WorldBoxesOf`, the gate's reading); the higher one covers the lower.
|
|
221
217
|
*/
|
|
@@ -262,7 +258,7 @@ function readable(
|
|
|
262
258
|
}
|
|
263
259
|
}
|
|
264
260
|
|
|
265
|
-
/** Which material shows from above, on a 64 × 64 grid over the footprint
|
|
261
|
+
/** Which material shows from above, on a 64 × 64 grid over the footprint, over world boxes. */
|
|
266
262
|
function topViewRegions(
|
|
267
263
|
placed: { body: V3AssetGeometry; box: ReturnType<typeof v3WorldBoxesOf>[number] }[],
|
|
268
264
|
frame: { x0: number; x1: number; z0: number; z1: number }
|
|
@@ -284,7 +280,7 @@ function topViewRegions(
|
|
|
284
280
|
|
|
285
281
|
for (let y = y0; y < y1; y++) {
|
|
286
282
|
for (let x = x0; x < x1; x++) {
|
|
287
|
-
// A round body does not fill its box; the corners are cut
|
|
283
|
+
// A round body does not fill its box; the corners are cut.
|
|
288
284
|
if (round) {
|
|
289
285
|
const cx = (x + 0.5 - (x0 + x1) / 2) / ((x1 - x0) / 2 || 1)
|
|
290
286
|
const cy = (y + 0.5 - (y0 + y1) / 2) / ((y1 - y0) / 2 || 1)
|
|
@@ -309,7 +305,7 @@ function topViewRegions(
|
|
|
309
305
|
return { byMaterial, total: covered, background: GRID * GRID - covered, footprint: GRID * GRID }
|
|
310
306
|
}
|
|
311
307
|
|
|
312
|
-
/** 1 · 2 · 5 per decade
|
|
308
|
+
/** 1 · 2 · 5 per decade: even steps on a log axis. */
|
|
313
309
|
export function decadeSteps(from: number, to: number): number[] {
|
|
314
310
|
const out: number[] = []
|
|
315
311
|
for (let decade = 1; decade <= to; decade *= 10) {
|
|
@@ -327,7 +323,7 @@ export function decadeSteps(from: number, to: number): number[] {
|
|
|
327
323
|
export type V3CurveSeries = 'triangles' | 'drawCalls' | 'geometries' | 'materials'
|
|
328
324
|
|
|
329
325
|
/**
|
|
330
|
-
* The four lines
|
|
326
|
+
* The four lines: two that grow with the instance count and two that stay flat — the flat ones are what the
|
|
331
327
|
* instances share. Materials are dashed and drawn last, so a line under them still shows where the two meet.
|
|
332
328
|
*/
|
|
333
329
|
export const CURVE_SERIES: readonly { key: V3CurveSeries; flat: boolean; dashed?: boolean }[] = [
|
|
@@ -337,7 +333,7 @@ export const CURVE_SERIES: readonly { key: V3CurveSeries; flat: boolean; dashed?
|
|
|
337
333
|
{ key: 'materials', flat: true, dashed: true }
|
|
338
334
|
]
|
|
339
335
|
|
|
340
|
-
/**
|
|
336
|
+
/** The chart's size. The chart is shown at this size in a sheet; shrunk into the side, its words are unreadable. */
|
|
341
337
|
export const CURVE_SIZE = { width: 680, height: 380, pad: { top: 52, right: 208, bottom: 44, left: 62 } } as const
|
|
342
338
|
|
|
343
339
|
export interface V3CurveLayout {
|
|
@@ -357,7 +353,7 @@ export function shortNumber(v: number): string {
|
|
|
357
353
|
return String(v)
|
|
358
354
|
}
|
|
359
355
|
|
|
360
|
-
/** Where
|
|
356
|
+
/** Where the chart puts each line and tick. Both axes are logarithmic. */
|
|
361
357
|
export function curveLayout(points: V3CurvePoint[]): V3CurveLayout | undefined {
|
|
362
358
|
if (points.length === 0) return undefined
|
|
363
359
|
const { width, height, pad } = CURVE_SIZE
|
|
@@ -74,3 +74,20 @@ export function partRefusalOf(error: unknown, part: string, say: Say): string |
|
|
|
74
74
|
if (follows) return say('figure.text.part-is-followed', { part, others: follows[1]! })
|
|
75
75
|
return say('figure.text.part-is-referenced', { part })
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A value outside the range its input declares, with the range and the value given -- the kernel says
|
|
80
|
+
* 「size.x: 6021100 outside [300,1200]」. The figure's own size is named by its axis; any other input by its range only.
|
|
81
|
+
* The repeat pitch's refusal shares the code and has its own words (`figure.text.repeat-pitch-too-small`).
|
|
82
|
+
*/
|
|
83
|
+
export function inputRangeRefusalOf(error: unknown, say: Say): string | null {
|
|
84
|
+
if ((error as { code?: string })?.code !== 'INPUT_RANGE') return null
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
86
|
+
const found = /([\w.-]+): (-?[\d.e+]+) outside \[(-?[\d.e+]+),\s*(-?[\d.e+]+)\]/.exec(message)
|
|
87
|
+
if (!found) return null
|
|
88
|
+
const [, input, value, min, max] = found
|
|
89
|
+
const number = (text: string) => Number(text).toLocaleString(undefined, { maximumFractionDigits: 3 })
|
|
90
|
+
const axis = /^size\.([xyz])$/.exec(input!)?.[1]
|
|
91
|
+
const params = { value: number(value!), min: number(min!), max: number(max!) }
|
|
92
|
+
return axis ? say('figure.text.input-range-size', { ...params, axis: axis.toUpperCase() }) : say('figure.text.input-range', params)
|
|
93
|
+
}
|
|
@@ -28,7 +28,7 @@ export function drawableV3Parts(geometry: readonly unknown[]): V3RenderPart[] {
|
|
|
28
28
|
/**
|
|
29
29
|
* The mesh for one body — things-scene's builder, not a copy of it.
|
|
30
30
|
*
|
|
31
|
-
* The modeller kept its own provider switch and it knew four providers;
|
|
31
|
+
* The modeller kept its own provider switch and it knew four providers; figures use eleven. A body the
|
|
32
32
|
* board drew was refused here with "Unsupported V3 provider: roundedBoxXZ" (2026-09-24, the first canvas check of
|
|
33
33
|
* ADR-0087 step 4). One builder, and the canvas an author looks at shows what a board will show.
|
|
34
34
|
*/
|
|
@@ -5,9 +5,9 @@ import type { GizmoResult } from './v3-gizmo.js'
|
|
|
5
5
|
import type { V3RenderPart } from './v3-renderer.js'
|
|
6
6
|
|
|
7
7
|
/*
|
|
8
|
-
* V3 bodies <-> scene components, in one place
|
|
8
|
+
* V3 bodies <-> scene components, in one place.
|
|
9
9
|
*
|
|
10
|
-
* The modeller stands on a things-scene board in editing mode
|
|
10
|
+
* The modeller stands on a things-scene board in editing mode: each body figure-model evaluates is a
|
|
11
11
|
* `figure-v3-part` component, and what the scene's gizmo writes back comes here to become a change of the part's
|
|
12
12
|
* inputs. The two vocabularies:
|
|
13
13
|
*
|
|
@@ -5,8 +5,6 @@ import type { V3RenderPart } from './v3-renderer.js'
|
|
|
5
5
|
/**
|
|
6
6
|
* 카드에 쓸 그림을 찍는다 — **숨은 자리에 V3 캔버스를 하나 더 세워서.**
|
|
7
7
|
*
|
|
8
|
-
* V2 의 `figure-thumbnail.ts` 와 같은 규율이고, 까닭도 같다.
|
|
9
|
-
*
|
|
10
8
|
* ## 왜 보이는 캔버스를 찍지 않나
|
|
11
9
|
*
|
|
12
10
|
* 저작면의 캔버스는 저작자가 돌려 둔 각도와 확대로 서 있다. 그것을 찍으면 카드마다 프레임이
|
|
@@ -27,7 +25,7 @@ import type { V3RenderPart } from './v3-renderer.js'
|
|
|
27
25
|
* 간다. 정본이 그림보다 중하고, 그림은 다음에 저작면을 열 때 다시 채운다.
|
|
28
26
|
*/
|
|
29
27
|
|
|
30
|
-
/** 카드는 4:3 이다.
|
|
28
|
+
/** 카드는 4:3 이다. */
|
|
31
29
|
const WIDTH = 480
|
|
32
30
|
const HEIGHT = 360
|
|
33
31
|
|
|
@@ -43,8 +43,7 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
43
43
|
min-height: 0;
|
|
44
44
|
/*
|
|
45
45
|
The page must fit the width it is given. Without this the shell sizes it to its content — the editor's
|
|
46
|
-
660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).
|
|
47
|
-
V2 page does the same.
|
|
46
|
+
660 plus the 320 release panel — and the panel runs off the right edge (seen at 945 px, 2026-09-24).
|
|
48
47
|
*/
|
|
49
48
|
overflow: hidden;
|
|
50
49
|
}
|
|
@@ -100,8 +99,7 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
100
99
|
*
|
|
101
100
|
* 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서
|
|
102
101
|
* `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 V3 도형은 공간을 선언하기 전에는 전부
|
|
103
|
-
* 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인).
|
|
104
|
-
* V2 화면의 패널을 그대로 건다.
|
|
102
|
+
* 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.
|
|
105
103
|
*/
|
|
106
104
|
@state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {
|
|
107
105
|
version: 0
|
|
@@ -273,8 +271,8 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
273
271
|
}
|
|
274
272
|
}
|
|
275
273
|
/**
|
|
276
|
-
* Category and tags — the catalogue's, kept on the row, not in the asset. Written at once
|
|
277
|
-
*
|
|
274
|
+
* Category and tags — the catalogue's, kept on the row, not in the asset. Written at once; the server's
|
|
275
|
+
* refusal is shown as it gave it.
|
|
278
276
|
*/
|
|
279
277
|
private onCatalog = async (event: Event) => {
|
|
280
278
|
const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }
|
|
@@ -344,8 +342,8 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
344
342
|
if (view.saving) return false
|
|
345
343
|
if (!view.dirty) return true
|
|
346
344
|
/*
|
|
347
|
-
The shell asks this while the edited model is still on screen. The app's own prompt, as
|
|
348
|
-
|
|
345
|
+
The shell asks this while the edited model is still on screen. The app's own prompt, as every operato screen
|
|
346
|
+
asks -- not the browser's confirm box (user, 2026-09-24).
|
|
349
347
|
*/
|
|
350
348
|
this.discardApproved = !!(await OxPrompt.open({
|
|
351
349
|
type: 'warning',
|
|
@@ -402,7 +400,7 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
402
400
|
waiting.settle({ applied: false, code: (error as { code?: string })?.code, error: error instanceof Error ? error.message : String(error) })
|
|
403
401
|
}
|
|
404
402
|
}
|
|
405
|
-
/** The first save's question, over the names this domain already uses
|
|
403
|
+
/** The first save's question, over the names this domain already uses. */
|
|
406
404
|
private askFirstSave = async (name: string): Promise<FirstSave | undefined> => {
|
|
407
405
|
const taken = await fetchFigureTypeNames().catch(() => [] as string[])
|
|
408
406
|
return openPopup<FirstSave>(html`<figure-v3-first-save .type=${this.row.type ?? ''} .name=${name} .taken=${taken}></figure-v3-first-save>`, {
|
|
@@ -418,7 +416,7 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
418
416
|
/*
|
|
419
417
|
No heading line: the canvas is what this page is for, and the name is already in the properties' first section
|
|
420
418
|
(and the browser tab). The title stays, not emptied, because the MDI tab reads it too -- the heading is switched
|
|
421
|
-
off where it is drawn (`ox-page-title-bar`).
|
|
419
|
+
off where it is drawn (`ox-page-title-bar`).
|
|
422
420
|
*/
|
|
423
421
|
heading: false
|
|
424
422
|
}
|
|
@@ -462,7 +460,6 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
462
460
|
></figure-v3-verdict>
|
|
463
461
|
<figure-history-panel
|
|
464
462
|
slot="history"
|
|
465
|
-
format="v3"
|
|
466
463
|
.figureId=${view.id}
|
|
467
464
|
.figureState=${this.row.state}
|
|
468
465
|
.figureVersion=${this.row.version}
|
package/client/types.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 화면이 다루는 모양.
|
|
3
|
-
*
|
|
4
|
-
* 정본 형식(`FigureSource`)의 타입은 **여기서 다시 정의하지 않는다** —
|
|
5
|
-
* `@hatiolab/figure-model` 에서 가져온다. 두 벌이면 갈리고, 갈리면 화면이 서버가
|
|
6
|
-
* 거절할 것을 만들어 낸다.
|
|
2
|
+
* 화면이 다루는 모양. 도형 자산의 타입은 `@hatiolab/figure-model` 이 정한다 — 여기서 다시 적지 않는다.
|
|
7
3
|
*/
|
|
8
|
-
export type { FigureSource, FigureBlueprint, FigurePart, ErrorCode, ViolationCode } from '@hatiolab/figure-model'
|
|
9
4
|
|
|
10
5
|
/** 서버가 돌려주는 Figure 한 벌. `source` 는 저작면에서만 받는다. */
|
|
11
6
|
export interface Figure {
|
|
@@ -27,9 +22,7 @@ export interface Figure {
|
|
|
27
22
|
thumbnailUpdatedAt?: string
|
|
28
23
|
updatedAt?: string
|
|
29
24
|
updater?: { id: string; name: string }
|
|
30
|
-
/**
|
|
31
|
-
format?: 'v2' | 'v3' | null
|
|
32
|
-
/** `FigureSource` JSON 문자열. 목록 질의에는 실려 오지 않는다. */
|
|
25
|
+
/** 도형 자산 JSON 문자열. 목록 질의에는 실려 오지 않는다. */
|
|
33
26
|
source?: string
|
|
34
27
|
properties?: string
|
|
35
28
|
version?: number
|
|
@@ -109,25 +102,6 @@ export interface FigureFinding {
|
|
|
109
102
|
* 발행 판정은 막는 것과 알리는 것을 함께 내므로 어느 쪽인지를 값이 스스로 말한다 — 코드를 보고
|
|
110
103
|
* 화면이 다시 분류하면 규칙이 두 벌이 된다.
|
|
111
104
|
*/
|
|
112
|
-
/** One reversible anchor edit the sizing gate has measured against this exact source. */
|
|
113
|
-
export interface FigureGateFix {
|
|
114
|
-
findingWay: string
|
|
115
|
-
part: string
|
|
116
|
-
axis: 'x' | 'y' | 'z'
|
|
117
|
-
from?: string
|
|
118
|
-
to: 'min' | 'center' | 'max' | 'span'
|
|
119
|
-
resolved: number
|
|
120
|
-
/** All blocking sizing cases clear after this exact edit was remeasured server-side. */
|
|
121
|
-
safe: boolean
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** A server-remeasured set of draft-only changes that clears every blocking sizing split. */
|
|
125
|
-
export interface FigureGateFixPlan {
|
|
126
|
-
fixes: FigureGateFix[]
|
|
127
|
-
resolved: number
|
|
128
|
-
safe: boolean
|
|
129
|
-
}
|
|
130
|
-
|
|
131
105
|
export interface FigureGateFinding {
|
|
132
106
|
code: string
|
|
133
107
|
message: string
|
|
@@ -137,9 +111,6 @@ export interface FigureGateFinding {
|
|
|
137
111
|
how?: string
|
|
138
112
|
/** 갈라진 부품 이름들, 또는 정본 안의 경로. */
|
|
139
113
|
at?: string
|
|
140
|
-
/** Candidate edits; the UI must preview and explicitly apply these, never auto-save them. */
|
|
141
|
-
fixes?: FigureGateFix[]
|
|
142
|
-
fixPlan?: FigureGateFixPlan
|
|
143
114
|
blocking: boolean
|
|
144
115
|
}
|
|
145
116
|
|
|
@@ -43,7 +43,6 @@ const FIGURE_LIST_FIELDS = `
|
|
|
43
43
|
score
|
|
44
44
|
triangles
|
|
45
45
|
groups
|
|
46
|
-
format
|
|
47
46
|
thumbnailUpdatedAt
|
|
48
47
|
updatedAt
|
|
49
48
|
updater { id name }
|
|
@@ -202,28 +201,6 @@ export async function inspectFigure(id) {
|
|
|
202
201
|
why
|
|
203
202
|
how
|
|
204
203
|
at
|
|
205
|
-
fixes {
|
|
206
|
-
findingWay
|
|
207
|
-
part
|
|
208
|
-
axis
|
|
209
|
-
from
|
|
210
|
-
to
|
|
211
|
-
resolved
|
|
212
|
-
safe
|
|
213
|
-
}
|
|
214
|
-
fixPlan {
|
|
215
|
-
resolved
|
|
216
|
-
safe
|
|
217
|
-
fixes {
|
|
218
|
-
findingWay
|
|
219
|
-
part
|
|
220
|
-
axis
|
|
221
|
-
from
|
|
222
|
-
to
|
|
223
|
-
resolved
|
|
224
|
-
safe
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
204
|
blocking
|
|
228
205
|
}
|
|
229
206
|
}
|
|
@@ -253,28 +230,6 @@ export async function inspectFigureDraft(source, id) {
|
|
|
253
230
|
why
|
|
254
231
|
how
|
|
255
232
|
at
|
|
256
|
-
fixes {
|
|
257
|
-
findingWay
|
|
258
|
-
part
|
|
259
|
-
axis
|
|
260
|
-
from
|
|
261
|
-
to
|
|
262
|
-
resolved
|
|
263
|
-
safe
|
|
264
|
-
}
|
|
265
|
-
fixPlan {
|
|
266
|
-
resolved
|
|
267
|
-
safe
|
|
268
|
-
fixes {
|
|
269
|
-
findingWay
|
|
270
|
-
part
|
|
271
|
-
axis
|
|
272
|
-
from
|
|
273
|
-
to
|
|
274
|
-
resolved
|
|
275
|
-
safe
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
233
|
blocking
|
|
279
234
|
}
|
|
280
235
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/graphql/index.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { i18next } from '@operato/i18n'\nimport { client } from '@operato/graphql'\n\nimport type {\n Figure,\n FigureFinding,\n FigureInspection,\n FigureListResult,\n FigurePatch,\n FigureVersion,\n NewFigure\n} from '../types.js'\n\n/**\n * 응답에서 값을 꺼낸다. **서버가 말한 사유를 그대로 올린다.**\n *\n * 전에는 `response.data.figures` 처럼 바로 꺼냈다. 서버가 오류를 돌려주면 `data` 가\n * 없으므로 `Cannot read properties of undefined (reading 'figures')` 가 났고, 화면에는\n * 그 문장이 그대로 떴다. 사용자는 자기가 무엇을 잘못했는지 알 수 없다 — 실제로 AI\n * 요청에서 그렇게 났다.\n *\n * 값이 `null` 인 것은 오류가 아니다. 지운 것을 물었을 때처럼 없는 것이 답인 경우가 있다.\n * 여기서 막는 것은 **`data` 자체가 없는 것**이다.\n */\nfunction unwrap<T>(response: { data?: Record<string, unknown>; errors?: readonly { message: string }[] }, field: string): T {\n const said = response.errors?.[0]?.message\n if (said) throw new Error(said)\n\n if (!response.data) {\n /* The field name stays off the screen because the user cannot act on it; it goes to the console. */\n console.error(`[figure-ui] the server returned no data for ${field}`)\n throw new Error(i18next.t('figure.text.server-returned-no-data'))\n }\n\n return response.data[field] as T\n}\n\n\n/**\n * 목록에 필요한 필드만 가져온다 — `source` 는 무겁고 목록에서 쓰지 않는다.\n *\n * **`thumbnail` 도 여기 없다.** base64 문자열이라 목록 응답에 실으면 카탈로그를 열 때마다 그림\n * 전부가 다시 오고(표본 14개 227KB), data URL 은 브라우저가 캐시하지 못한다. 그림은 주소로\n * 부른다(`figure-thumbnail` 라우트). 대신 `thumbnailUpdatedAt` 을 받는다 — 그림이 있나 없나를\n * 그것으로 알고, 판을 가리는 값으로도 쓴다.\n */\nconst FIGURE_LIST_FIELDS = `\n id\n type\n name\n description\n category\n tags\n state\n version\n score\n triangles\n groups\n format\n thumbnailUpdatedAt\n updatedAt\n updater { id name }\n`\n\n/**\n * 목록을 가져온다.\n *\n * 걸러 보기·정렬·쪽 나누기를 **그대로 넘긴다.** 화면이 `search`·`state` 같은 이름을 따로 만들어\n * filter 로 옮기던 것을 그만두었다 — 그 이름들은 격자(ox-grist)가 이미 컬럼 설정에서 만들어 주고,\n * 중간에 한 벌 더 두면 격자가 아는 조건과 서버에 가는 조건이 어긋난다.\n */\nexport async function fetchFigureList(params: {\n page?: number\n limit?: number\n filters?: unknown[]\n sortings?: { name: string; desc?: boolean }[]\n}): Promise<FigureListResult> {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n figures(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items { ${FIGURE_LIST_FIELDS} }\n total\n }\n }\n `,\n variables: {\n filters: params.filters ?? [],\n pagination: { page: params.page ?? 1, limit: params.limit ?? 30 },\n sortings: params.sortings?.length ? params.sortings : [{ name: 'updatedAt', desc: true }]\n },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureListResult>(response, 'figures')\n}\n\n/** 저작면에 필요한 전부 — `source` 를 포함한다. */\nexport async function fetchFigure(id: string): Promise<Figure> {\n const response = await client.query({\n query: gql`\n query ($id: String!) {\n figure(id: $id) {\n ${FIGURE_LIST_FIELDS}\n source\n properties\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<Figure>(response, 'figure')\n}\n\n/**\n * 이미 쓰이고 있는 타입 이름.\n *\n * `type` 은 만든 뒤에 고칠 수 없으므로 **만들기 전에** 알려 줘야 한다. 저장을 눌러\n * 거절당하고 나서 아는 것은 늦다.\n */\nexport async function fetchFigureTypeNames(): Promise<string[]> {\n const response = await client.query({\n query: gql`\n query {\n figureTypeNames\n }\n `,\n fetchPolicy: 'network-only'\n })\n\n return unwrap<string[] | null>(response, 'figureTypeNames') ?? []\n}\n\nexport interface SaveResult {\n figure: Figure\n /** 막지 않는 발견. 저장은 됐지만 화면이 보여 줘야 한다. */\n violations: FigureFinding[]\n}\n\nexport async function createFigure(figure: NewFigure): Promise<SaveResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($figure: NewFigure!) {\n createFigure(figure: $figure) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n }\n }\n `,\n variables: { figure }\n })\n\n return unwrap<SaveResult>(response, 'createFigure')\n}\n\nexport async function updateFigure(id: string, patch: FigurePatch): Promise<SaveResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: FigurePatch!) {\n updateFigure(id: $id, patch: $patch) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n }\n }\n `,\n variables: { id, patch }\n })\n\n return unwrap<SaveResult>(response, 'updateFigure')\n}\n\nexport async function deleteFigure(id: string): Promise<boolean> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteFigure(id: $id)\n }\n `,\n variables: { id }\n })\n\n return unwrap<boolean>(response, 'deleteFigure')\n}\n\n/**\n * 발행한다 — 판 번호가 오르고, 그 순간이 판본으로 남는다.\n *\n * 이미 발행된 것을 다시 발행하면 서버가 거절한다. 고쳐서 다시 내려면 저장이 먼저이고, 저장은\n * 발행을 푼다(초안으로 돌아온다).\n */\nexport async function releaseFigure(id: string, comment?: string): Promise<Figure> {\n const response = await client.mutate({\n mutation: gql`\n mutation ReleaseFigure($id: String!, $comment: String) {\n releaseFigure(id: $id, comment: $comment) {\n id\n version\n state\n updatedAt\n }\n }\n `,\n variables: { id, comment }\n })\n\n return unwrap<Figure>(response, 'releaseFigure')\n}\n\n/** 옛 판을 초안으로 되살린다. 정본·속성·그림이 돌아오고 이름·설명은 그대로다. */\nexport async function revertFigureVersion(id: string, version: number): Promise<SaveResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation RevertFigureVersion($id: String!, $version: Float!) {\n revertFigureVersion(id: $id, version: $version) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n }\n }\n `,\n variables: { id, version }\n })\n\n return unwrap<SaveResult>(response, 'revertFigureVersion')\n}\n\n/**\n * 발행해도 되나 — **누르기 전에** 묻는다.\n *\n * 막는 것은 서버의 `releaseFigure` 이고 이것은 이유를 미리 보여 주기 위한 것이다. 허가증이\n * 아니므로 이 답이 통과라 해도 발행은 다시 판정을 받는다.\n */\nexport async function inspectFigure(id: string): Promise<FigureInspection> {\n const response = await client.query({\n query: gql`\n query InspectFigure($id: String!) {\n inspectFigure(id: $id) {\n blocked\n findings {\n code\n message\n why\n how\n at\n fixes {\n findingWay\n part\n axis\n from\n to\n resolved\n safe\n }\n fixPlan {\n resolved\n safe\n fixes {\n findingWay\n part\n axis\n from\n to\n resolved\n safe\n }\n }\n blocking\n }\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureInspection>(response, 'inspectFigure')\n}\n\n/**\n * 편집 중인 원본의 발행 판정 — 저장하지 않은 것을 잰다.\n *\n * 발행 패널의 `inspectFigure` 는 저장된 원본을 재므로 고친 결과를 저장한 뒤에야 보여 준다. 편집하면서 막히는\n * 것을 보려면 편집 중인 원본을 보내야 한다. 판정은 서버의 같은 문(`inspectAnySource`)이 하고 문장도 같다 —\n * 화면이 제 판정을 따로 가지면 두 자리가 다른 말을 한다. `id` 가 있으면 서버가 그 행의 타입과 직전 발행판을 읽는다.\n */\nexport async function inspectFigureDraft(source: string, id?: string): Promise<FigureInspection> {\n const response = await client.query({\n query: gql`\n query InspectFigureDraft($source: String!, $id: String) {\n inspectFigureDraft(source: $source, id: $id) {\n blocked\n findings {\n code\n message\n why\n how\n at\n fixes {\n findingWay\n part\n axis\n from\n to\n resolved\n safe\n }\n fixPlan {\n resolved\n safe\n fixes {\n findingWay\n part\n axis\n from\n to\n resolved\n safe\n }\n }\n blocking\n }\n }\n }\n `,\n variables: { source, id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureInspection>(response, 'inspectFigureDraft')\n}\n\n/** 발행된 판들 — 최근 것부터 열 개. */\n/**\n * 직전 발행판의 정본. 한 번도 발행 안 했으면 `undefined`.\n *\n * 저작 중에 「이 고침이 이미 놓인 것을 깨나」를 그 자리에서 보여 주려면 견줄 것이 필요하다. 저장\n * 전 초안은 서버가 모르므로 판정도 화면에서 한다 — 형식의 `contractChanges` 를 같이 쓴다.\n */\nexport async function fetchReleasedSource(id: string): Promise<string | undefined> {\n const response = await client.query({\n query: gql`\n query FigureReleased($id: String!) {\n figureReleased(id: $id) {\n version\n source\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<{ source?: string }>(response, 'figureReleased')?.source ?? undefined\n}\n\nexport async function fetchFigureVersions(id: string): Promise<FigureVersion[]> {\n const response = await client.query({\n query: gql`\n query FigureVersions($id: String!) {\n figureVersions(id: $id) {\n version\n comment\n state\n updatedAt\n updater { id name }\n score\n triangles\n groups\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureVersion[]>(response, 'figureVersions') ?? []\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../client/graphql/index.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { i18next } from '@operato/i18n'\nimport { client } from '@operato/graphql'\n\nimport type {\n Figure,\n FigureFinding,\n FigureInspection,\n FigureListResult,\n FigurePatch,\n FigureVersion,\n NewFigure\n} from '../types.js'\n\n/**\n * 응답에서 값을 꺼낸다. **서버가 말한 사유를 그대로 올린다.**\n *\n * 전에는 `response.data.figures` 처럼 바로 꺼냈다. 서버가 오류를 돌려주면 `data` 가\n * 없으므로 `Cannot read properties of undefined (reading 'figures')` 가 났고, 화면에는\n * 그 문장이 그대로 떴다. 사용자는 자기가 무엇을 잘못했는지 알 수 없다 — 실제로 AI\n * 요청에서 그렇게 났다.\n *\n * 값이 `null` 인 것은 오류가 아니다. 지운 것을 물었을 때처럼 없는 것이 답인 경우가 있다.\n * 여기서 막는 것은 **`data` 자체가 없는 것**이다.\n */\nfunction unwrap<T>(response: { data?: Record<string, unknown>; errors?: readonly { message: string }[] }, field: string): T {\n const said = response.errors?.[0]?.message\n if (said) throw new Error(said)\n\n if (!response.data) {\n /* The field name stays off the screen because the user cannot act on it; it goes to the console. */\n console.error(`[figure-ui] the server returned no data for ${field}`)\n throw new Error(i18next.t('figure.text.server-returned-no-data'))\n }\n\n return response.data[field] as T\n}\n\n\n/**\n * 목록에 필요한 필드만 가져온다 — `source` 는 무겁고 목록에서 쓰지 않는다.\n *\n * **`thumbnail` 도 여기 없다.** base64 문자열이라 목록 응답에 실으면 카탈로그를 열 때마다 그림\n * 전부가 다시 오고(표본 14개 227KB), data URL 은 브라우저가 캐시하지 못한다. 그림은 주소로\n * 부른다(`figure-thumbnail` 라우트). 대신 `thumbnailUpdatedAt` 을 받는다 — 그림이 있나 없나를\n * 그것으로 알고, 판을 가리는 값으로도 쓴다.\n */\nconst FIGURE_LIST_FIELDS = `\n id\n type\n name\n description\n category\n tags\n state\n version\n score\n triangles\n groups\n thumbnailUpdatedAt\n updatedAt\n updater { id name }\n`\n\n/**\n * 목록을 가져온다.\n *\n * 걸러 보기·정렬·쪽 나누기를 **그대로 넘긴다.** 화면이 `search`·`state` 같은 이름을 따로 만들어\n * filter 로 옮기던 것을 그만두었다 — 그 이름들은 격자(ox-grist)가 이미 컬럼 설정에서 만들어 주고,\n * 중간에 한 벌 더 두면 격자가 아는 조건과 서버에 가는 조건이 어긋난다.\n */\nexport async function fetchFigureList(params: {\n page?: number\n limit?: number\n filters?: unknown[]\n sortings?: { name: string; desc?: boolean }[]\n}): Promise<FigureListResult> {\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n figures(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items { ${FIGURE_LIST_FIELDS} }\n total\n }\n }\n `,\n variables: {\n filters: params.filters ?? [],\n pagination: { page: params.page ?? 1, limit: params.limit ?? 30 },\n sortings: params.sortings?.length ? params.sortings : [{ name: 'updatedAt', desc: true }]\n },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureListResult>(response, 'figures')\n}\n\n/** 저작면에 필요한 전부 — `source` 를 포함한다. */\nexport async function fetchFigure(id: string): Promise<Figure> {\n const response = await client.query({\n query: gql`\n query ($id: String!) {\n figure(id: $id) {\n ${FIGURE_LIST_FIELDS}\n source\n properties\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<Figure>(response, 'figure')\n}\n\n/**\n * 이미 쓰이고 있는 타입 이름.\n *\n * `type` 은 만든 뒤에 고칠 수 없으므로 **만들기 전에** 알려 줘야 한다. 저장을 눌러\n * 거절당하고 나서 아는 것은 늦다.\n */\nexport async function fetchFigureTypeNames(): Promise<string[]> {\n const response = await client.query({\n query: gql`\n query {\n figureTypeNames\n }\n `,\n fetchPolicy: 'network-only'\n })\n\n return unwrap<string[] | null>(response, 'figureTypeNames') ?? []\n}\n\nexport interface SaveResult {\n figure: Figure\n /** 막지 않는 발견. 저장은 됐지만 화면이 보여 줘야 한다. */\n violations: FigureFinding[]\n}\n\nexport async function createFigure(figure: NewFigure): Promise<SaveResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($figure: NewFigure!) {\n createFigure(figure: $figure) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n }\n }\n `,\n variables: { figure }\n })\n\n return unwrap<SaveResult>(response, 'createFigure')\n}\n\nexport async function updateFigure(id: string, patch: FigurePatch): Promise<SaveResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: FigurePatch!) {\n updateFigure(id: $id, patch: $patch) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n }\n }\n `,\n variables: { id, patch }\n })\n\n return unwrap<SaveResult>(response, 'updateFigure')\n}\n\nexport async function deleteFigure(id: string): Promise<boolean> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteFigure(id: $id)\n }\n `,\n variables: { id }\n })\n\n return unwrap<boolean>(response, 'deleteFigure')\n}\n\n/**\n * 발행한다 — 판 번호가 오르고, 그 순간이 판본으로 남는다.\n *\n * 이미 발행된 것을 다시 발행하면 서버가 거절한다. 고쳐서 다시 내려면 저장이 먼저이고, 저장은\n * 발행을 푼다(초안으로 돌아온다).\n */\nexport async function releaseFigure(id: string, comment?: string): Promise<Figure> {\n const response = await client.mutate({\n mutation: gql`\n mutation ReleaseFigure($id: String!, $comment: String) {\n releaseFigure(id: $id, comment: $comment) {\n id\n version\n state\n updatedAt\n }\n }\n `,\n variables: { id, comment }\n })\n\n return unwrap<Figure>(response, 'releaseFigure')\n}\n\n/** 옛 판을 초안으로 되살린다. 정본·속성·그림이 돌아오고 이름·설명은 그대로다. */\nexport async function revertFigureVersion(id: string, version: number): Promise<SaveResult> {\n const response = await client.mutate({\n mutation: gql`\n mutation RevertFigureVersion($id: String!, $version: Float!) {\n revertFigureVersion(id: $id, version: $version) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n }\n }\n `,\n variables: { id, version }\n })\n\n return unwrap<SaveResult>(response, 'revertFigureVersion')\n}\n\n/**\n * 발행해도 되나 — **누르기 전에** 묻는다.\n *\n * 막는 것은 서버의 `releaseFigure` 이고 이것은 이유를 미리 보여 주기 위한 것이다. 허가증이\n * 아니므로 이 답이 통과라 해도 발행은 다시 판정을 받는다.\n */\nexport async function inspectFigure(id: string): Promise<FigureInspection> {\n const response = await client.query({\n query: gql`\n query InspectFigure($id: String!) {\n inspectFigure(id: $id) {\n blocked\n findings {\n code\n message\n why\n how\n at\n blocking\n }\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureInspection>(response, 'inspectFigure')\n}\n\n/**\n * 편집 중인 원본의 발행 판정 — 저장하지 않은 것을 잰다.\n *\n * 발행 패널의 `inspectFigure` 는 저장된 원본을 재므로 고친 결과를 저장한 뒤에야 보여 준다. 편집하면서 막히는\n * 것을 보려면 편집 중인 원본을 보내야 한다. 판정은 서버의 같은 문(`inspectAnySource`)이 하고 문장도 같다 —\n * 화면이 제 판정을 따로 가지면 두 자리가 다른 말을 한다. `id` 가 있으면 서버가 그 행의 타입과 직전 발행판을 읽는다.\n */\nexport async function inspectFigureDraft(source: string, id?: string): Promise<FigureInspection> {\n const response = await client.query({\n query: gql`\n query InspectFigureDraft($source: String!, $id: String) {\n inspectFigureDraft(source: $source, id: $id) {\n blocked\n findings {\n code\n message\n why\n how\n at\n blocking\n }\n }\n }\n `,\n variables: { source, id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureInspection>(response, 'inspectFigureDraft')\n}\n\n/** 발행된 판들 — 최근 것부터 열 개. */\n/**\n * 직전 발행판의 정본. 한 번도 발행 안 했으면 `undefined`.\n *\n * 저작 중에 「이 고침이 이미 놓인 것을 깨나」를 그 자리에서 보여 주려면 견줄 것이 필요하다. 저장\n * 전 초안은 서버가 모르므로 판정도 화면에서 한다 — 형식의 `contractChanges` 를 같이 쓴다.\n */\nexport async function fetchReleasedSource(id: string): Promise<string | undefined> {\n const response = await client.query({\n query: gql`\n query FigureReleased($id: String!) {\n figureReleased(id: $id) {\n version\n source\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<{ source?: string }>(response, 'figureReleased')?.source ?? undefined\n}\n\nexport async function fetchFigureVersions(id: string): Promise<FigureVersion[]> {\n const response = await client.query({\n query: gql`\n query FigureVersions($id: String!) {\n figureVersions(id: $id) {\n version\n comment\n state\n updatedAt\n updater { id name }\n score\n triangles\n groups\n }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n\n return unwrap<FigureVersion[]>(response, 'figureVersions') ?? []\n}\n"]}
|
|
@@ -28,28 +28,15 @@ export declare class FigureHistoryPanel extends FigureHistoryPanel_base {
|
|
|
28
28
|
figureVersion: number;
|
|
29
29
|
/** 저장하지 않은 것이 있나 — 있으면 발행을 막는다. */
|
|
30
30
|
dirty: boolean;
|
|
31
|
-
/**
|
|
32
|
-
* 열린 도형의 형식. 발행 · 판본 · 복원은 figure id 로만 서버를 묻고 서버의 한 문이 두 형식을 다 읽으므로 두 화면이
|
|
33
|
-
* 이 패널을 함께 쓴다. 갈리는 것은 조치 단추 하나다 — 부품을 옮기는 두 조치는 형식마다 다른 편집이라, 받는
|
|
34
|
-
* 화면이 없는 단추를 그리면 눌러도 아무 일이 없다.
|
|
35
|
-
*/
|
|
36
|
-
format: 'v2' | 'v3';
|
|
37
31
|
/**
|
|
38
32
|
* 행의 타입. 판정 하나(`type-is-placeholder`)가 이것을 본다 — V3 자산은 타입을 원본에 적지 않아서, 타입을 바꾸면
|
|
39
33
|
* 원본도 dirty 도 그대로다. 그래서 이 값이 바뀌면 판정을 다시 묻는다. 안 그러면 타입을 정한 뒤에도 「임시 타입」
|
|
40
34
|
* 판정이 남아 발행 단추가 잠긴 채다.
|
|
41
35
|
*/
|
|
42
36
|
figureType?: string;
|
|
43
|
-
/** 직전 발행판의 기준 상자. 외형 변화 안내에서 X·Z 복원 선택지에만 쓴다. */
|
|
44
|
-
releasedBase?: {
|
|
45
|
-
x: number;
|
|
46
|
-
y: number;
|
|
47
|
-
z: number;
|
|
48
|
-
};
|
|
49
37
|
private versions;
|
|
50
38
|
private busy;
|
|
51
39
|
private failure;
|
|
52
|
-
private keepChangedAppearance;
|
|
53
40
|
/**
|
|
54
41
|
* 발행 판정. **저장된 정본에 대한 것**이라 저장할 때마다 다시 묻는다(`figureVersion` 이 아니라
|
|
55
42
|
* `dirty` 가 풀리는 순간이 그때다).
|
|
@@ -68,15 +55,6 @@ export declare class FigureHistoryPanel extends FigureHistoryPanel_base {
|
|
|
68
55
|
* 준다 — 화면이 문장을 지으면 서버가 거절할 때와 다른 말을 하게 된다.
|
|
69
56
|
*/
|
|
70
57
|
private renderFinding;
|
|
71
|
-
/**
|
|
72
|
-
* 게이트는 수정을 저장하지 않는다. 부모 모델러가 현재 정본과 이 한 수정안을 나란히 렌더하고,
|
|
73
|
-
* 저작자가 '변경 받기'를 눌렀을 때에만 자기 작업 사본에 적용한다.
|
|
74
|
-
*/
|
|
75
|
-
private previewFix;
|
|
76
|
-
/** 서버가 전수 재검사해 모든 차단 split이 사라진 후보만 작업 사본에 원클릭 적용한다. 저장은 하지 않는다. */
|
|
77
|
-
private applyFix;
|
|
78
|
-
/** Apply the server-remeasured coordinated repair to the draft only; save remains explicit. */
|
|
79
|
-
private applyFixPlan;
|
|
80
58
|
/**
|
|
81
59
|
* 넘친 것을 담는 길 하나를 여기서도 내민다 — **같은 동작이 속성 패널에도 있다**(「부품에 맞추기」).
|
|
82
60
|
*
|
|
@@ -86,24 +64,13 @@ export declare class FigureHistoryPanel extends FigureHistoryPanel_base {
|
|
|
86
64
|
* 여전히 저작자가 따로 한다.
|
|
87
65
|
*/
|
|
88
66
|
/**
|
|
89
|
-
* 「부품에 맞추기」를 내미는
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* V3 의 장착면이 떠 있는 것은 여기 없다. 부품 하나를 평면에 붙이면 그 부품에 붙지 않은 것은 따라오지 않아
|
|
94
|
-
* V2 의 「바닥에 앉히기」와 뜻이 다르다. 판정의 「어떻게」가 할 일을 말한다.
|
|
67
|
+
* 「부품에 맞추기」를 내미는 판정: 공간을 부품에서 다시 선언한다(`declare-occupancy`) — 부품은 그대로 두고 선언만
|
|
68
|
+
* 바꾼다. 공간을 아직 선언하지 않은 도형도 같은 단추로 선언한다: 새로 만든 도형은 전부 그 판정으로 막히고, 화면에
|
|
69
|
+
* 다른 길이 없다. 장착면이 떠 있는 것은 여기 없다 — 부품 하나를 평면에 붙여도 거기 붙지 않은 것은 따라오지 않는다.
|
|
70
|
+
* 판정의 「어떻게」가 할 일을 말한다.
|
|
95
71
|
*/
|
|
96
72
|
private offersFit;
|
|
97
73
|
private fitBase;
|
|
98
|
-
/**
|
|
99
|
-
* 떠 있는 것을 앉힌다. 부품 하나를 고르고 「바닥에」를 누르는 길은 전부터 있었으나, 그 길은
|
|
100
|
-
* 고른 것 하나만 옮기고 **여기서는 닿지 않는다.** 막혔다는 말을 읽은 자리에서 조치가 되게 한다.
|
|
101
|
-
*/
|
|
102
|
-
private settleOnFloor;
|
|
103
|
-
/** 기준 상자의 X·Z만 직전 발행값으로 되돌린다. Y와 부품 편집은 보존한다. */
|
|
104
|
-
private restoreReleasedFootprint;
|
|
105
|
-
/** 현재 외형으로 발행하겠다는 명시적 선택이다. 저장·발행은 이어서 따로 한다. */
|
|
106
|
-
private keepChangedFootprint;
|
|
107
74
|
private renderVersion;
|
|
108
75
|
private release;
|
|
109
76
|
private revert;
|