@things-factory/figure-ui 10.1.39 → 10.1.42
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 +1 -0
- package/client/modeller/figure-v3-material.ts +248 -0
- package/client/modeller/figure-v3-parts.ts +16 -1
- package/client/modeller/figure-v3-properties.ts +254 -0
- package/client/modeller/modeller-route.ts +25 -0
- package/client/modeller/v3-align.ts +84 -0
- package/client/modeller/v3-canvas.ts +167 -5
- package/client/modeller/v3-editor.ts +207 -47
- package/client/modeller/v3-figure-facts.ts +53 -0
- package/client/modeller/v3-gizmo.ts +80 -0
- package/client/modeller/v3-occupancy.ts +6 -2
- package/client/pages/figure-list-page.ts +5 -3
- package/client/pages/figure-modeller-page.ts +9 -0
- package/client/pages/figure-v3-modeller-page.ts +68 -13
- package/client/types.ts +2 -0
- package/dist-client/graphql/index.js +1 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-v3-material.d.ts +36 -0
- package/dist-client/modeller/figure-v3-material.js +249 -0
- package/dist-client/modeller/figure-v3-material.js.map +1 -0
- package/dist-client/modeller/figure-v3-parts.js +16 -1
- package/dist-client/modeller/figure-v3-parts.js.map +1 -1
- package/dist-client/modeller/figure-v3-properties.d.ts +46 -0
- package/dist-client/modeller/figure-v3-properties.js +259 -0
- package/dist-client/modeller/figure-v3-properties.js.map +1 -0
- package/dist-client/modeller/modeller-route.d.ts +16 -0
- package/dist-client/modeller/modeller-route.js +27 -0
- package/dist-client/modeller/modeller-route.js.map +1 -0
- package/dist-client/modeller/v3-align.d.ts +26 -0
- package/dist-client/modeller/v3-align.js +65 -0
- package/dist-client/modeller/v3-align.js.map +1 -0
- package/dist-client/modeller/v3-canvas.d.ts +24 -0
- package/dist-client/modeller/v3-canvas.js +181 -5
- package/dist-client/modeller/v3-canvas.js.map +1 -1
- package/dist-client/modeller/v3-editor.d.ts +24 -0
- package/dist-client/modeller/v3-editor.js +218 -44
- package/dist-client/modeller/v3-editor.js.map +1 -1
- package/dist-client/modeller/v3-figure-facts.d.ts +26 -0
- package/dist-client/modeller/v3-figure-facts.js +34 -0
- package/dist-client/modeller/v3-figure-facts.js.map +1 -0
- package/dist-client/modeller/v3-gizmo.d.ts +48 -0
- package/dist-client/modeller/v3-gizmo.js +40 -0
- package/dist-client/modeller/v3-gizmo.js.map +1 -0
- package/dist-client/modeller/v3-occupancy.d.ts +5 -1
- package/dist-client/modeller/v3-occupancy.js +4 -2
- package/dist-client/modeller/v3-occupancy.js.map +1 -1
- package/dist-client/pages/figure-list-page.d.ts +1 -0
- package/dist-client/pages/figure-list-page.js +5 -3
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.js +9 -0
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/pages/figure-v3-modeller-page.d.ts +16 -2
- package/dist-client/pages/figure-v3-modeller-page.js +78 -22
- package/dist-client/pages/figure-v3-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +2 -0
- package/dist-client/types.js.map +1 -1
- package/package.json +4 -4
- package/test/modeller-route.test.ts +42 -0
- package/test/v3-align.test.ts +64 -0
- package/test/v3-gizmo.test.ts +97 -0
- package/test/v3-material.test.ts +68 -0
- package/test/v3-parts-wiring.test.ts +56 -0
- package/test/v3-properties.test.ts +93 -0
- package/test/v3-theme.test.ts +67 -0
- package/translations/en.json +11 -0
- package/translations/ja.json +11 -0
- package/translations/ko.json +11 -0
- package/translations/ms.json +11 -0
- package/translations/zh.json +11 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { V3InputChange } from '@hatiolab/figure-model'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What the gizmo may write for a part — the V2 gizmo's move · turn · size, on a V3 asset (parity row ③).
|
|
5
|
+
*
|
|
6
|
+
* A drag can only be written back where the part is placed by its own numbers. A part the create commands made is:
|
|
7
|
+
* its pose is `rigid@1` into the asset frame from `<part>.position.*` and `<part>.rotation.*`, and a box's shape
|
|
8
|
+
* (`box-shape@1` or `rounded-box@1`) takes its width, height and depth from inputs of its own — `<part>.size.*` from
|
|
9
|
+
* `addV3Box`, `<part>.dimension.*` from the editor's shape palette. A part fastened to another, following an instance size or made by the
|
|
10
|
+
* converter from expressions has no such numbers; the gizmo does not offer what it could not write back, rather
|
|
11
|
+
* than move something the next evaluation would put back.
|
|
12
|
+
*/
|
|
13
|
+
export interface GizmoReach {
|
|
14
|
+
translate: boolean
|
|
15
|
+
rotate: boolean
|
|
16
|
+
scale: boolean
|
|
17
|
+
/** The inputs sizing the part along X, Y and Z, when it can be sized. */
|
|
18
|
+
sizes?: [string, string, string]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Shapes whose first three arguments are the extent along X, Y and Z. */
|
|
22
|
+
const BOXES = new Set(['box-shape@1', 'rounded-box@1'])
|
|
23
|
+
|
|
24
|
+
interface Node {
|
|
25
|
+
id: string
|
|
26
|
+
op: string
|
|
27
|
+
args: string[]
|
|
28
|
+
params?: Record<string, unknown>
|
|
29
|
+
}
|
|
30
|
+
interface AssetLike {
|
|
31
|
+
document: { model: { nodes: Node[]; inputs: Array<{ id: string; role?: string }> } }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const AXES = ['x', 'y', 'z'] as const
|
|
35
|
+
|
|
36
|
+
export function gizmoReachOf(asset: AssetLike, part: string): GizmoReach {
|
|
37
|
+
const { nodes, inputs } = asset.document.model
|
|
38
|
+
const design = new Set(inputs.filter(input => input.role === 'design').map(input => input.id))
|
|
39
|
+
const own = (name: string) => AXES.map(axis => `${part}.${name}.${axis}`)
|
|
40
|
+
const allDesign = (ids: string[]) => ids.every(id => design.has(id))
|
|
41
|
+
const pose = nodes.find(node => node.id === `${part}.pose`)
|
|
42
|
+
const placed = nodes.find(node => node.id === part && node.op === 'place@1')
|
|
43
|
+
const posed = !!pose && pose.op === 'rigid@1' && pose.params?.to === 'asset' && placed?.args[1] === `${part}.poseValue`
|
|
44
|
+
const shape = nodes.find(node => node.id === `${part}.shape`)
|
|
45
|
+
const extent = shape && BOXES.has(shape.op) ? shape.args.slice(0, 3) : []
|
|
46
|
+
const sizes =
|
|
47
|
+
extent.length === 3 && extent.every(id => id.startsWith(`${part}.`) && design.has(id)) ? (extent as [string, string, string]) : undefined
|
|
48
|
+
return {
|
|
49
|
+
translate: posed && allDesign(own('position')) && own('position').every((id, i) => pose!.args[i] === id),
|
|
50
|
+
rotate: posed && allDesign(own('rotation')) && own('rotation').every((id, i) => pose!.args[3 + i] === id),
|
|
51
|
+
scale: !!sizes,
|
|
52
|
+
...(sizes ? { sizes } : {})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Where a drag left the part, in the asset frame: millimetres, degrees and per-axis scale. */
|
|
57
|
+
export interface GizmoResult {
|
|
58
|
+
position?: [number, number, number]
|
|
59
|
+
rotation?: [number, number, number]
|
|
60
|
+
scale?: [number, number, number]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The input changes a drag makes — one list, applied as one step. Millimetres to the millimetre and degrees to a
|
|
65
|
+
* tenth, as the V2 gizmo rounds (a drag otherwise stores 722.4180046035418 mm). A size never goes below 1 mm.
|
|
66
|
+
*/
|
|
67
|
+
export function gizmoChanges(part: string, reach: GizmoReach, result: GizmoResult, sizes: Record<string, number>): V3InputChange[] {
|
|
68
|
+
const changes: V3InputChange[] = []
|
|
69
|
+
const put = (input: string, unit: V3InputChange['unit'], value: number) =>
|
|
70
|
+
changes.push({ kind: 'set-design', input, unit, value } as V3InputChange)
|
|
71
|
+
if (result.position && reach.translate)
|
|
72
|
+
AXES.forEach((axis, i) => put(`${part}.position.${axis}`, 'mm', Math.round(result.position![i]!)))
|
|
73
|
+
if (result.rotation && reach.rotate)
|
|
74
|
+
AXES.forEach((axis, i) => put(`${part}.rotation.${axis}`, 'deg', Math.round(result.rotation![i]! * 10) / 10))
|
|
75
|
+
if (result.scale && reach.scale && reach.sizes)
|
|
76
|
+
reach.sizes.forEach((id, i) => {
|
|
77
|
+
if (sizes[id] !== undefined) put(id, 'mm', Math.max(1, Math.round(sizes[id]! * result.scale![i]!)))
|
|
78
|
+
})
|
|
79
|
+
return changes
|
|
80
|
+
}
|
|
@@ -12,11 +12,15 @@ import { parseV3Asset, proposeV3Occupancy, type V3AuthoringAction } from '@hatio
|
|
|
12
12
|
* 막는다. 배치 기준은 이미 선언된 것을 지키고, 처음 선언하는 도형은 바닥에 선다고 둔다 — 새로 만든 도형의
|
|
13
13
|
* 모델러 기본이 바닥이다.
|
|
14
14
|
*/
|
|
15
|
-
export function occupancyFromParts(
|
|
15
|
+
export function occupancyFromParts(
|
|
16
|
+
source: string,
|
|
17
|
+
/** A different face to place it by — the properties tab's choice. Absent keeps the declared one. */
|
|
18
|
+
placement?: 'floor' | 'ceiling' | 'center'
|
|
19
|
+
): Extract<V3AuthoringAction, { kind: 'declare-occupancy' }> {
|
|
16
20
|
const asset = parseV3Asset(source)
|
|
17
21
|
return {
|
|
18
22
|
kind: 'declare-occupancy',
|
|
19
23
|
proposal: proposeV3Occupancy(asset, { over: 'range' }),
|
|
20
|
-
placement: asset.occupancy?.placement ?? 'floor'
|
|
24
|
+
placement: placement ?? asset.occupancy?.placement ?? 'floor'
|
|
21
25
|
}
|
|
22
26
|
}
|
|
@@ -15,6 +15,7 @@ import { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePage
|
|
|
15
15
|
import { capacityFrom } from '../modeller/scene-budget.js'
|
|
16
16
|
import { deleteFigure, fetchFigureList } from '../graphql/index.js'
|
|
17
17
|
import type { Figure } from '../types.js'
|
|
18
|
+
import { modellerPathOf } from '../modeller/modeller-route.js'
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* 이 값 아래면 점수를 색으로 눈에 띄게 한다.
|
|
@@ -530,7 +531,7 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
530
531
|
handlers: {
|
|
531
532
|
click: (_columns: any, _data: any, _column: any, record: Figure) => {
|
|
532
533
|
if (record?.id) {
|
|
533
|
-
this.openModeller(record
|
|
534
|
+
this.openModeller(record)
|
|
534
535
|
}
|
|
535
536
|
}
|
|
536
537
|
}
|
|
@@ -585,8 +586,9 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
585
586
|
return { total: total || 0, records: items || [] }
|
|
586
587
|
}
|
|
587
588
|
|
|
588
|
-
|
|
589
|
-
|
|
589
|
+
/** The modeller that can edit this figure, by its format; a new figure starts in V3 (`modeller-route.ts`). */
|
|
590
|
+
private openModeller(figure?: Figure) {
|
|
591
|
+
navigate(modellerPathOf(figure))
|
|
590
592
|
}
|
|
591
593
|
|
|
592
594
|
/** `remove` 는 HTMLElement 의 메서드다 — 가리면 조용한 결함이 된다. */
|
|
@@ -30,6 +30,7 @@ import { DEFAULT_VIEW } from '../modeller/figure-view.js'
|
|
|
30
30
|
import type { ViewSettings } from '../modeller/figure-view.js'
|
|
31
31
|
import { createFigure, fetchFigure, fetchFigureTypeNames, fetchReleasedSource, updateFigure } from '../graphql/index.js'
|
|
32
32
|
import type { Figure, FigureFinding, FigureGateFix, FigureGateFixPlan, FigureProposal, ProposalFeedback } from '../types.js'
|
|
33
|
+
import { isV3Source, modellerPathOf } from '../modeller/modeller-route.js'
|
|
33
34
|
|
|
34
35
|
const FigureModellerPageBase = localize(i18next)(PageView) as typeof PageView
|
|
35
36
|
|
|
@@ -1461,6 +1462,14 @@ export class FigureModellerPage extends FigureModellerPageBase {
|
|
|
1461
1462
|
const generation = ++this.loadGeneration
|
|
1462
1463
|
const figure = await fetchFigure(id)
|
|
1463
1464
|
if (generation !== this.loadGeneration || !this.active) return
|
|
1465
|
+
/*
|
|
1466
|
+
A converted figure reached by an old address — a bookmark, the AI dock, a link someone sent — opens in the
|
|
1467
|
+
modeller that can edit it instead of failing here on a source this page cannot read.
|
|
1468
|
+
*/
|
|
1469
|
+
if (isV3Source(figure.source)) {
|
|
1470
|
+
navigate(modellerPathOf({ id, format: 'v3' }), true)
|
|
1471
|
+
return
|
|
1472
|
+
}
|
|
1464
1473
|
|
|
1465
1474
|
// Decode before assigning the target ID or generating a thumbnail. On failure the previous
|
|
1466
1475
|
// editor snapshot remains internal, but is hidden and cannot be saved under the requested ID.
|
|
@@ -6,10 +6,12 @@ import { v3ProductStore } from '../graphql/v3-store.js'
|
|
|
6
6
|
import { i18next } from '@operato/i18n'
|
|
7
7
|
import { fetchFigure, updateFigure } from '../graphql/index.js'
|
|
8
8
|
import { occupancyFromParts } from '../modeller/v3-occupancy.js'
|
|
9
|
+
import type { V3AuthoringAction } from '@hatiolab/figure-model'
|
|
9
10
|
import '../modeller/v3-editor.js'
|
|
10
11
|
import '@material/web/icon/icon.js'
|
|
11
12
|
import '../modeller/figure-history-panel.js'
|
|
12
13
|
import '../modeller/figure-v3-verdict.js'
|
|
14
|
+
import '../modeller/figure-v3-properties.js'
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* The V3 product page has exactly one authoritative workspace for UI and AI input transactions.
|
|
@@ -144,15 +146,17 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
144
146
|
* 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 서버의 문 하나가 두 형식을 다 읽으므로
|
|
145
147
|
* V2 화면의 패널을 그대로 건다.
|
|
146
148
|
*/
|
|
147
|
-
@state() private row: { state?: 'draft' | 'released'; version: number; type?: string } = {
|
|
149
|
+
@state() private row: { state?: 'draft' | 'released'; version: number; type?: string; category?: string; tags?: string[] } = {
|
|
150
|
+
version: 0
|
|
151
|
+
}
|
|
148
152
|
@state() private typeError = ''
|
|
149
|
-
/** Why
|
|
150
|
-
@state() private
|
|
153
|
+
/** Why a command from the side did nothing. Shown beside the tabs — the editor stays, since nothing in it was lost. */
|
|
154
|
+
@state() private commandError = ''
|
|
151
155
|
/**
|
|
152
156
|
* Which tab the side shows. The verdict comes first: it is what the author needs while editing, and the count on
|
|
153
157
|
* its tab stays in sight while the history is open — as on the V2 page.
|
|
154
158
|
*/
|
|
155
|
-
@state() private tab: 'verdict' | 'history' = 'verdict'
|
|
159
|
+
@state() private tab: 'properties' | 'verdict' | 'history' = 'verdict'
|
|
156
160
|
@state() private instances?: number
|
|
157
161
|
/**
|
|
158
162
|
* The source the verdict judges. The session refuses to export while it loads or saves, so the last one read is
|
|
@@ -168,6 +172,7 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
168
172
|
return this.draft
|
|
169
173
|
}
|
|
170
174
|
private wasDirty = false
|
|
175
|
+
private titled?: string | null
|
|
171
176
|
private announce = () => {
|
|
172
177
|
const view = this.session.read()
|
|
173
178
|
if (!this.active || view.loading || view.saving || view.error || !view.context) return
|
|
@@ -185,9 +190,14 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
185
190
|
}
|
|
186
191
|
private changed = () => {
|
|
187
192
|
this.revision++
|
|
188
|
-
/* Any edit after a failed
|
|
189
|
-
this.
|
|
193
|
+
/* Any edit after a failed command makes the message stale — the author has done something about it. */
|
|
194
|
+
this.commandError = ''
|
|
190
195
|
const view = this.session.read()
|
|
196
|
+
/* The title bar reads the name through `context`, which the shell asks for only when told — a rename left it stale. */
|
|
197
|
+
if (view.name !== this.titled) {
|
|
198
|
+
this.titled = view.name
|
|
199
|
+
this.updateContext()
|
|
200
|
+
}
|
|
191
201
|
if (!view.saving) this.announce()
|
|
192
202
|
/* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 도형을 고쳐 저장하면 서버가 초안으로 내린다. */
|
|
193
203
|
if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)
|
|
@@ -197,7 +207,13 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
197
207
|
try {
|
|
198
208
|
const figure = await fetchFigure(id)
|
|
199
209
|
if (this.session.read().id === id)
|
|
200
|
-
this.row = {
|
|
210
|
+
this.row = {
|
|
211
|
+
state: figure.state as 'draft' | 'released',
|
|
212
|
+
version: figure.version ?? 0,
|
|
213
|
+
type: figure.type,
|
|
214
|
+
category: figure.category ?? undefined,
|
|
215
|
+
tags: figure.tags ?? undefined
|
|
216
|
+
}
|
|
201
217
|
} catch {
|
|
202
218
|
/* 행을 못 읽으면 패널은 판 번호 0 으로 보인다 — 발행 문이 서버에서 다시 판단하므로 여기서 지어내지 않는다. */
|
|
203
219
|
}
|
|
@@ -226,16 +242,28 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
226
242
|
}
|
|
227
243
|
/** 「부품에 맞추기」 — 공간을 지금 부품에서 다시 선언한다. 저장은 저작자가 따로 한다. */
|
|
228
244
|
private fitToParts = () => {
|
|
245
|
+
this.runCommand(() => occupancyFromParts(this.session.source()))
|
|
246
|
+
}
|
|
247
|
+
/** A command from the properties tab — rename, declare the room, the mounting face. */
|
|
248
|
+
private onCommand = (event: Event) => {
|
|
249
|
+
const action = (event as CustomEvent).detail?.action as V3AuthoringAction | undefined
|
|
250
|
+
if (action) this.runCommand(() => action)
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* One step of the session's history, whichever part of the side asked for it — so undo takes it back like any
|
|
254
|
+
* other edit, and it is saved with the figure.
|
|
255
|
+
*/
|
|
256
|
+
private runCommand(actionOf: () => V3AuthoringAction) {
|
|
229
257
|
try {
|
|
230
|
-
this.session.command(
|
|
231
|
-
this.
|
|
258
|
+
this.session.command(actionOf())
|
|
259
|
+
this.commandError = ''
|
|
232
260
|
this.changed()
|
|
233
261
|
} catch (error) {
|
|
234
262
|
/*
|
|
235
263
|
This used to set the page error, which replaces the whole editor with one raw line — for a figure with no
|
|
236
264
|
parts that line was 「OCCUPANCY_NO_PARTS: occupancy: no part could be measured」 (2026-09-24).
|
|
237
265
|
*/
|
|
238
|
-
this.
|
|
266
|
+
this.commandError =
|
|
239
267
|
(error as { code?: string })?.code === 'OCCUPANCY_NO_PARTS'
|
|
240
268
|
? i18next.t('figure.text.fit-needs-parts')
|
|
241
269
|
: error instanceof Error
|
|
@@ -243,9 +271,26 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
243
271
|
: String(error)
|
|
244
272
|
}
|
|
245
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Category and tags — the catalogue's, kept on the row, not in the asset. Written at once, as the V2 page does;
|
|
276
|
+
* the server's refusal is shown as it gave it.
|
|
277
|
+
*/
|
|
278
|
+
private onCatalog = async (event: Event) => {
|
|
279
|
+
const patch = (event as CustomEvent).detail as { category?: string; tags?: string[] }
|
|
280
|
+
const id = this.session.read().id
|
|
281
|
+
if (!id || !patch) return
|
|
282
|
+
try {
|
|
283
|
+
const result = await updateFigure(id, patch)
|
|
284
|
+
this.commandError = ''
|
|
285
|
+
this.row = { ...this.row, category: result.figure.category ?? undefined, tags: result.figure.tags ?? undefined }
|
|
286
|
+
} catch (error) {
|
|
287
|
+
this.commandError = error instanceof Error ? error.message : String(error)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
246
290
|
private released = (event: Event) => {
|
|
247
291
|
const figure = (event as CustomEvent).detail?.figure
|
|
248
|
-
|
|
292
|
+
/* Keep the rest of the row: rebuilding it from these two dropped the type, and the locked type field went blank. */
|
|
293
|
+
if (figure?.id && figure.id === this.session.read().id) this.row = { ...this.row, state: figure.state, version: figure.version ?? 0 }
|
|
249
294
|
}
|
|
250
295
|
/** 과거 판으로 되돌렸으면 서버의 원본이 바뀐 것이다 — 다시 연다. 패널은 저장 안 된 것이 있으면 되돌리기를 막는다. */
|
|
251
296
|
private reverted = async () => {
|
|
@@ -340,11 +385,21 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
340
385
|
/>`}
|
|
341
386
|
${this.typeError ? html`<small role="alert">${this.typeError}</small>` : ''}
|
|
342
387
|
</label>
|
|
343
|
-
${this.
|
|
388
|
+
${this.commandError ? html`<p fit-error role="alert">${this.commandError}</p>` : ''}
|
|
344
389
|
<div tabs>
|
|
390
|
+
${this.renderTab('properties', 'tune', i18next.t('figure.label.properties'))}
|
|
345
391
|
${this.renderTab('verdict', 'insights', i18next.t('figure.label.maturity'), this.instances)}
|
|
346
392
|
${this.renderTab('history', 'history', i18next.t('figure.label.history'))}
|
|
347
393
|
</div>
|
|
394
|
+
<figure-v3-properties
|
|
395
|
+
?off=${this.tab !== 'properties'}
|
|
396
|
+
.source=${this.draftSource()}
|
|
397
|
+
.figureId=${view.id}
|
|
398
|
+
.figureCategory=${this.row.category}
|
|
399
|
+
.figureTags=${this.row.tags}
|
|
400
|
+
@v3-command=${this.onCommand}
|
|
401
|
+
@v3-catalog=${this.onCatalog}
|
|
402
|
+
></figure-v3-properties>
|
|
348
403
|
<figure-v3-verdict
|
|
349
404
|
?off=${this.tab !== 'verdict'}
|
|
350
405
|
.source=${this.draftSource()}
|
|
@@ -367,7 +422,7 @@ export class FigureV3ModellerPage extends PageView {
|
|
|
367
422
|
></figure-history-panel>
|
|
368
423
|
</aside>`
|
|
369
424
|
}
|
|
370
|
-
private renderTab(tab: 'verdict' | 'history', icon: string, label: string, badge?: number) {
|
|
425
|
+
private renderTab(tab: 'properties' | 'verdict' | 'history', icon: string, label: string, badge?: number) {
|
|
371
426
|
return html`
|
|
372
427
|
<button tab ?on=${this.tab === tab} title=${label} aria-label=${label} @click=${() => (this.tab = tab)}>
|
|
373
428
|
<md-icon>${icon}</md-icon>${badge == null ? '' : html`<span badge>${badge.toLocaleString()}</span>`}
|
package/client/types.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface Figure {
|
|
|
27
27
|
thumbnailUpdatedAt?: string
|
|
28
28
|
updatedAt?: string
|
|
29
29
|
updater?: { id: string; name: string }
|
|
30
|
+
/** Which format the stored source is in — `'v2'`, `'v3'`, or null when the server cannot tell. */
|
|
31
|
+
format?: 'v2' | 'v3' | null
|
|
30
32
|
/** `FigureSource` JSON 문자열. 목록 질의에는 실려 오지 않는다. */
|
|
31
33
|
source?: string
|
|
32
34
|
properties?: string
|
|
@@ -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 FigureProposal,\n ProposalFeedback,\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 * 팔레트를 **클라이언트가 보낸다.** 서버는 팔레트를 모른다 — things-scene 이 갖고\n * 있는데 그 패키지의 node 조건은 팔레트가 없는 번들로 간다. 그리는 쪽이 어떤 토큰을\n * 풀 수 있는지 아는 유일한 자리이므로 그쪽이 보낸다.\n *\n * 돌아오는 것은 **후보**다. 저장되지 않았다 — 저작자가 받아야 정본이 된다.\n */\nexport async function proposeFigure(request: {\n prompt: string\n base?: string\n type?: string\n palette: string[]\n refine?: boolean\n feedback?: ProposalFeedback[]\n image?: File\n}): Promise<FigureProposal> {\n const response = await client.mutate({\n mutation: gql`\n mutation ProposeFigure($request: ProposeRequest!) {\n proposeFigure(request: $request) {\n source\n score\n grade\n triangles\n groups\n quality {\n status\n summary\n findings {\n dimension\n code\n message\n }\n visualRegions\n visualCoverage\n visualReadability\n }\n attempts\n violations {\n code\n message\n }\n }\n }\n `,\n variables: { request },\n // @operato/graphql 는 이 신호가 있을 때만 Upload가 포함된 변수를 multipart로 인코딩한다.\n // 없으면 브라우저 File은 JSON에서 {}가 되어 서버 Upload scalar가 요청 자체를 거절한다.\n context: { hasUpload: !!request.image }\n })\n\n return unwrap<FigureProposal>(response, 'proposeFigure')\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 FigureProposal,\n ProposalFeedback,\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 * 팔레트를 **클라이언트가 보낸다.** 서버는 팔레트를 모른다 — things-scene 이 갖고\n * 있는데 그 패키지의 node 조건은 팔레트가 없는 번들로 간다. 그리는 쪽이 어떤 토큰을\n * 풀 수 있는지 아는 유일한 자리이므로 그쪽이 보낸다.\n *\n * 돌아오는 것은 **후보**다. 저장되지 않았다 — 저작자가 받아야 정본이 된다.\n */\nexport async function proposeFigure(request: {\n prompt: string\n base?: string\n type?: string\n palette: string[]\n refine?: boolean\n feedback?: ProposalFeedback[]\n image?: File\n}): Promise<FigureProposal> {\n const response = await client.mutate({\n mutation: gql`\n mutation ProposeFigure($request: ProposeRequest!) {\n proposeFigure(request: $request) {\n source\n score\n grade\n triangles\n groups\n quality {\n status\n summary\n findings {\n dimension\n code\n message\n }\n visualRegions\n visualCoverage\n visualReadability\n }\n attempts\n violations {\n code\n message\n }\n }\n }\n `,\n variables: { request },\n // @operato/graphql 는 이 신호가 있을 때만 Upload가 포함된 변수를 multipart로 인코딩한다.\n // 없으면 브라우저 File은 JSON에서 {}가 되어 서버 Upload scalar가 요청 자체를 거절한다.\n context: { hasUpload: !!request.image }\n })\n\n return unwrap<FigureProposal>(response, 'proposeFigure')\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"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LitElement, nothing } from 'lit';
|
|
2
|
+
import type { V3AssetGeometry } from '@hatiolab/figure-model';
|
|
3
|
+
export declare const purposeOf: (token?: string) => string;
|
|
4
|
+
declare const FigureV3Material_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
5
|
+
/**
|
|
6
|
+
* 재질 — a V3 part's material, drawn as the V2 inspector draws it (V3 parity row ③).
|
|
7
|
+
*
|
|
8
|
+
* The V3 editor showed the colour role as a raw token (`palette.dark`) in a select and nothing else. The V3 material
|
|
9
|
+
* holds the colour role, a preset, transparency and a glow; a round body also has how finely its rim is drawn. This
|
|
10
|
+
* shows those, with the V2 words: painted colours and lit colours as two groups of swatches, each named for what it
|
|
11
|
+
* is for.
|
|
12
|
+
*
|
|
13
|
+
* Not here, because a V3 material has no place for them: flat shading and casting shadows (V2 fields the converter
|
|
14
|
+
* does not carry). The glow's colour comes from the lit colours, as in V2.
|
|
15
|
+
*
|
|
16
|
+
* Every change leaves as one `v3-part-edit`; the editor runs it through the session, one step of the undo history.
|
|
17
|
+
*/
|
|
18
|
+
export declare class FigureV3Material extends FigureV3Material_base {
|
|
19
|
+
static styles: import("lit").CSSResult;
|
|
20
|
+
/** The selected body as evaluated — its material, its segments and its shape. */
|
|
21
|
+
body?: V3AssetGeometry & {
|
|
22
|
+
id: string;
|
|
23
|
+
};
|
|
24
|
+
/** The asset's palette, token → colour. */
|
|
25
|
+
palette: Record<string, string>;
|
|
26
|
+
locked: boolean;
|
|
27
|
+
render(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
28
|
+
private renderSegments;
|
|
29
|
+
private swatches;
|
|
30
|
+
private offered;
|
|
31
|
+
/** Intensity 0 is no glow at all, as in V2; a new glow takes the part's own colour until another is picked. */
|
|
32
|
+
private glow;
|
|
33
|
+
private put;
|
|
34
|
+
private edit;
|
|
35
|
+
}
|
|
36
|
+
export {};
|