@things-factory/figure-ui 10.1.210 → 10.1.212
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 -1
- package/client/modeller/angle-dial.ts +1 -1
- package/client/modeller/canvas.ts +1 -1
- package/client/modeller/editor.ts +24 -32
- package/client/modeller/figure-history-panel.ts +1 -1
- package/client/modeller/figure-links.ts +61 -37
- package/client/modeller/figure-parts.ts +3 -3
- package/client/modeller/figure-settings.ts +1 -1
- package/client/modeller/figure-size-editor.ts +41 -4
- package/client/modeller/links.ts +63 -24
- package/client/modeller/live.ts +33 -0
- package/client/modeller/scene-budget.ts +3 -3
- package/client/modeller/thumbnail.ts +1 -1
- package/client/pages/figure-list-page.ts +2 -2
- package/client/pages/figure-modeller-page.ts +4 -4
- package/client/types.ts +2 -2
- package/client/viewparts/figure-thumb.ts +1 -1
- package/dist-client/graphql/index.d.ts +1 -1
- package/dist-client/graphql/index.js +1 -1
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/angle-dial.d.ts +1 -1
- package/dist-client/modeller/angle-dial.js.map +1 -1
- package/dist-client/modeller/canvas.js +1 -1
- package/dist-client/modeller/canvas.js.map +1 -1
- package/dist-client/modeller/editor.d.ts +5 -0
- package/dist-client/modeller/editor.js +24 -32
- package/dist-client/modeller/editor.js.map +1 -1
- package/dist-client/modeller/figure-history-panel.d.ts +1 -1
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +2 -0
- package/dist-client/modeller/figure-links.js +66 -39
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/figure-parts.d.ts +3 -3
- package/dist-client/modeller/figure-parts.js.map +1 -1
- package/dist-client/modeller/figure-settings.js +1 -1
- package/dist-client/modeller/figure-settings.js.map +1 -1
- package/dist-client/modeller/figure-size-editor.d.ts +3 -1
- package/dist-client/modeller/figure-size-editor.js +45 -4
- package/dist-client/modeller/figure-size-editor.js.map +1 -1
- package/dist-client/modeller/links.d.ts +15 -6
- package/dist-client/modeller/links.js +63 -25
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/modeller/live.d.ts +14 -0
- package/dist-client/modeller/live.js +31 -0
- package/dist-client/modeller/live.js.map +1 -1
- package/dist-client/modeller/scene-budget.d.ts +3 -3
- package/dist-client/modeller/scene-budget.js +3 -3
- package/dist-client/modeller/scene-budget.js.map +1 -1
- package/dist-client/modeller/thumbnail.js +1 -1
- package/dist-client/modeller/thumbnail.js.map +1 -1
- package/dist-client/pages/figure-list-page.d.ts +1 -1
- package/dist-client/pages/figure-list-page.js +2 -2
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +3 -3
- package/dist-client/pages/figure-modeller-page.js +4 -4
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +2 -2
- package/dist-client/types.js +1 -1
- package/dist-client/types.js.map +1 -1
- package/dist-client/viewparts/figure-thumb.js +1 -1
- package/dist-client/viewparts/figure-thumb.js.map +1 -1
- package/docs/multipart-response-review-2026-10-24.md +1 -1
- package/docs/response-authoring-2026-10-24.md +4 -4
- package/package.json +6 -6
- package/test/ai-proposal.test.ts +1 -1
- package/test/align.test.ts +1 -1
- package/test/browser/editing.check.mjs +38 -0
- package/test/browser/modeller.check.mjs +55 -0
- package/test/command-preview.test.ts +3 -3
- package/test/fields.test.ts +2 -2
- package/test/first-minute.test.ts +5 -5
- package/test/first-save.test.ts +3 -3
- package/test/gizmo.test.ts +3 -3
- package/test/keys.test.ts +2 -2
- package/test/layout.test.ts +1 -1
- package/test/links.test.ts +53 -14
- package/test/live.test.ts +4 -4
- package/test/material.test.ts +1 -1
- package/test/properties.test.ts +1 -1
- package/test/proposal-record.test.ts +1 -1
- package/test/refusals.test.ts +1 -1
- package/test/resize-with-placement.test.ts +1 -1
- package/test/scene-budget.test.ts +4 -4
- package/test/verdict.test.ts +1 -1
- package/translations/en.json +3 -0
- package/translations/es.json +3 -0
- package/translations/ja.json +3 -0
- package/translations/ko.json +109 -106
- package/translations/ms.json +3 -0
- package/translations/zh.json +3 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* ## 놓은 개수가 아니라 **보이는 개수**다
|
|
6
6
|
*
|
|
7
|
-
* 3D 는 요청이 있어야 다시 그린다. 가만히 서 있는
|
|
7
|
+
* 3D 는 요청이 있어야 다시 그린다. 가만히 서 있는 피규어는 프레임마다 아무 값도 치르지 않는다 —
|
|
8
8
|
* 900 대를 놓아 두어도 60fps 다. 값을 치르는 것은 **카메라 안에 들어 있고 그 프레임에 다시
|
|
9
9
|
* 그려지는** 것뿐이다. 그래서 이 파일의 분모는 「한 도면에 놓는 수」가 아니라 「한 화면에 보이는
|
|
10
10
|
* 부품 묶음 수」다.
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* 묶음 9 짜리 6 축 로봇 600 대 보이면 59.5 fps · 700 대에서 56.7 fps
|
|
19
19
|
* 묶음 2 짜리 컨베이어 1,600 대 보이면 59.3 fps · 2,500 대에서 36.8 fps
|
|
20
20
|
*
|
|
21
|
-
* 두
|
|
22
|
-
* 쓴다.
|
|
21
|
+
* 두 피규어의 꺾이는 지점이 **인스턴스 × 묶음 = 3,600 ~ 5,400** 에 들어온다. 보수적으로 4,000 을
|
|
22
|
+
* 쓴다. 피규어가 달라도 이 곱이 비슷한 데서 꺾이므로, 분모는 묶음 수로 둔다.
|
|
23
23
|
*
|
|
24
24
|
* ⚠ 이 수는 **이 기계에서 이 씬으로** 잰 값이다. 더 낮은 GPU 나 브라우저에서는 더 일찍 꺾인다.
|
|
25
25
|
* 화면에는 잰 기계와 날짜를 함께 적는다 — 적지 않으면 어디서나 참인 수처럼 읽힌다.
|
|
@@ -15,7 +15,7 @@ import type { FigureRenderPart } from './renderer.js'
|
|
|
15
15
|
*
|
|
16
16
|
* ## 왜 서버가 아니라 브라우저인가
|
|
17
17
|
*
|
|
18
|
-
* 백엔드에 GPU 가 없는 배포가 많아 헤드리스로 3D 를 찍는 것이 자주 실패한다.
|
|
18
|
+
* 백엔드에 GPU 가 없는 배포가 많아 헤드리스로 3D 를 찍는 것이 자주 실패한다. 피규어를 그릴 GPU 는
|
|
19
19
|
* 이미 보는 사람 손에 있다.
|
|
20
20
|
*
|
|
21
21
|
* ## 못 찍어도 저장은 간다
|
|
@@ -177,7 +177,7 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
177
177
|
* 검색창에 그 말을 채워 넣는다. 격자에 조건만 걸고 창을 비워 두면 저작자는 **왜 목록이
|
|
178
178
|
* 줄었는지 모른 채** 보게 되고, 되돌리려면 무엇을 지워야 하는지도 알 수 없다.
|
|
179
179
|
*
|
|
180
|
-
* 서버가 `tags` 뿐 아니라 이름·타입·설명·분류를 함께 훑으므로, 그 말이 다른 칸에 든
|
|
180
|
+
* 서버가 `tags` 뿐 아니라 이름·타입·설명·분류를 함께 훑으므로, 그 말이 다른 칸에 든 피규어도
|
|
181
181
|
* 같이 온다. 태그만 정확히 거르는 것이 아니라 **찾기**다.
|
|
182
182
|
*/
|
|
183
183
|
private findByTag(tag: string) {
|
|
@@ -280,7 +280,7 @@ export class FigureListPage extends FigureListPageBase {
|
|
|
280
280
|
정렬은 걸지 않는다. 배열이라 무엇을 기준으로 줄을 세우는지 답할 수 없다.
|
|
281
281
|
|
|
282
282
|
**카드에서는 이 컬럼이 그리지 않는다.** 본문에 한 줄로 두었더니 태그가 있는 카드만
|
|
283
|
-
높아져 격자가 어긋났다 — 개수가
|
|
283
|
+
높아져 격자가 어긋났다 — 개수가 피규어마다 다르므로 본문에 두는 한 남는 문제다.
|
|
284
284
|
카드에서는 `figure-thumb` 이 그림 위에 얹는다. 여기 남은 것은 표 모드의 칸이다.
|
|
285
285
|
*/
|
|
286
286
|
type: 'string',
|
|
@@ -133,7 +133,7 @@ export class FigureModellerPage extends PageView {
|
|
|
133
133
|
* 발행 패널이 읽는 행의 사실 — 상태와 발행 판 번호. 원본은 세션이 들고, 이 둘은 행에 있다.
|
|
134
134
|
*
|
|
135
135
|
* 이 화면에는 발행 패널이 없었다. 저장은 되고 발행 · 판본 · 복원 · 판정을 볼 자리가 없어서, ADR-0087 5단계에서
|
|
136
|
-
* `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새
|
|
136
|
+
* `figure-modeller` 가 이 화면으로 바뀌면 저작자는 발행을 할 수 없었다. 새 피규어는 공간을 선언하기 전에는 전부
|
|
137
137
|
* 발행 판정에 막히는데 그 선언을 할 단추도 없었다(2026-09-24 확인). 그래서 발행 패널을 건다.
|
|
138
138
|
*/
|
|
139
139
|
/** The categories the server allows, or null when a category is free text (G5). */
|
|
@@ -228,7 +228,7 @@ export class FigureModellerPage extends PageView {
|
|
|
228
228
|
this.routeId = view.id
|
|
229
229
|
navigate(modellerPathOf({ id: view.id }), true)
|
|
230
230
|
}
|
|
231
|
-
/* 저장이 끝난 순간 행을 다시 읽는다 — 발행된
|
|
231
|
+
/* 저장이 끝난 순간 행을 다시 읽는다 — 발행된 피규어를 고쳐 저장하면 서버가 초안으로 내린다. */
|
|
232
232
|
if (this.wasDirty && !view.dirty && !view.saving && view.id) this.readRow(view.id)
|
|
233
233
|
this.wasDirty = !!view.dirty
|
|
234
234
|
}
|
|
@@ -259,9 +259,9 @@ export class FigureModellerPage extends PageView {
|
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
/**
|
|
262
|
-
* 타입을 정한다 — 보드가
|
|
262
|
+
* 타입을 정한다 — 보드가 피규어를 부르는 이름이고, 발행하면 굳는다(ADR-0063 결정 4).
|
|
263
263
|
*
|
|
264
|
-
* 새
|
|
264
|
+
* 새 피규어는 저장할 때 임시 타입을 받는데, 이 화면에는 그것을 바꿀 칸이 없어서 임시 타입 그대로 발행됐다.
|
|
265
265
|
* 행만 바뀐다 — 자산은 타입을 원본에 적지 않으므로(적으면 형식이 거절한다) 편집 중인 원본과 부딪히지 않는다.
|
|
266
266
|
* 서버의 거절(이미 쓰는 타입 · 발행 뒤)은 그 문장 그대로 칸 아래에 둔다.
|
|
267
267
|
*/
|
package/client/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 화면이 다루는 모양.
|
|
2
|
+
* 화면이 다루는 모양. 피규어 자산의 타입은 `@hatiolab/figure-model` 이 정한다 — 여기서 다시 적지 않는다.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/** 서버가 돌려주는 Figure 한 벌. `source` 는 상세 또는 저작면을 열 때만 받는다. */
|
|
@@ -24,7 +24,7 @@ export interface Figure {
|
|
|
24
24
|
thumbnailUpdatedAt?: string
|
|
25
25
|
updatedAt?: string
|
|
26
26
|
updater?: { id: string; name: string }
|
|
27
|
-
/**
|
|
27
|
+
/** 피규어 자산 JSON 문자열. 목록 질의에는 실려 오지 않는다. */
|
|
28
28
|
source?: string
|
|
29
29
|
properties?: string
|
|
30
30
|
version?: number
|
|
@@ -65,7 +65,7 @@ export class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
65
65
|
태그 — **그림 위에 얹는다.**
|
|
66
66
|
|
|
67
67
|
카드 본문에 한 줄로 두었더니 태그가 있는 카드만 그만큼 높아져 격자가 들쭉날쭉해졌다.
|
|
68
|
-
태그는
|
|
68
|
+
태그는 피규어마다 개수가 다르므로 본문에 두는 한 이 문제가 남는다. 그림 위는 이미 자리가
|
|
69
69
|
잡혀 있고(초안 표시·삭제), 얹어도 카드 높이가 변하지 않는다.
|
|
70
70
|
|
|
71
71
|
아래쪽에 둔다 — 위는 초안 표시와 삭제 단추가 쓴다.
|
|
@@ -97,7 +97,7 @@ export interface ProposalOutcome {
|
|
|
97
97
|
recordedAt?: string;
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
|
-
* 제안이 무엇이 됐나 -- 그 제안을 담은 저장이 들어간
|
|
100
|
+
* 제안이 무엇이 됐나 -- 그 제안을 담은 저장이 들어간 피규어들. 비었으면 저장되지 않았다(적용만 하고 저장 전이거나, 되돌렸거나,
|
|
101
101
|
* 적용하지 않았다). 도크의 「결과 확인」이 이것을 묻는다: 도크의 기억이 아니라 서버의 기록이다.
|
|
102
102
|
*/
|
|
103
103
|
export declare function fetchProposalOutcome(proposalKey: string): Promise<ProposalOutcome[]>;
|
|
@@ -303,7 +303,7 @@ export async function undoFigureRevert(keptId, requestId) {
|
|
|
303
303
|
return unwrap(response, 'undoFigureRevert');
|
|
304
304
|
}
|
|
305
305
|
/**
|
|
306
|
-
* 제안이 무엇이 됐나 -- 그 제안을 담은 저장이 들어간
|
|
306
|
+
* 제안이 무엇이 됐나 -- 그 제안을 담은 저장이 들어간 피규어들. 비었으면 저장되지 않았다(적용만 하고 저장 전이거나, 되돌렸거나,
|
|
307
307
|
* 적용하지 않았다). 도크의 「결과 확인」이 이것을 묻는다: 도크의 기억이 아니라 서버의 기록이다.
|
|
308
308
|
*/
|
|
309
309
|
export async function fetchProposalOutcome(proposalKey) {
|
|
@@ -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, uploadContext, type UploadProgress } from '@operato/graphql'\n\nimport { reached, refusedIn } from './reached.js'\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 namespace\n type\n name\n description\n category\n tags\n state\n version\n deprecated\n supersededBy\n public\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 /** 'public': other domains' public figures, with their domain (LV figure-server design G2). The domain's own by default. */\n scope?: 'own' | 'public'\n}): Promise<FigureListResult> {\n const field = params.scope === 'public' ? 'publicFigures' : 'figures'\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n ${field}(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items { ${FIGURE_LIST_FIELDS} ${params.scope === 'public' ? 'domain { id name subdomain }' : ''} }\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, field)\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\n/** Makes a released figure public to other domains, or private again (LV figure-server design G2). */\nexport async function setFigurePublic(id: string, isPublic: boolean): Promise<Figure> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $public: Boolean!) {\n setFigurePublic(id: $id, public: $public) {\n ${FIGURE_LIST_FIELDS}\n }\n }\n `,\n variables: { id, public: isPublic }\n })\n\n return unwrap<Figure>(response, 'setFigurePublic')\n}\n\n/** Retires a released figure, naming the type to use instead, or brings it back. Release rows are not touched. */\nexport async function setFigureDeprecation(id: string, deprecated: boolean, supersededBy?: string | null): Promise<Figure> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $deprecated: Boolean!, $supersededBy: String) {\n setFigureDeprecation(id: $id, deprecated: $deprecated, supersededBy: $supersededBy) {\n ${FIGURE_LIST_FIELDS}\n }\n }\n `,\n variables: { id, deprecated, supersededBy: supersededBy || null }\n })\n\n return unwrap<Figure>(response, 'setFigureDeprecation')\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/** A namespace the domain owns (LV figure-server design G1): the `{namespace}` of the full name `{namespace}/{type}`. */\nexport interface FigureNamespace {\n id: string\n name: string\n description?: string | null\n state: 'active' | 'suspended'\n isDefault: boolean\n}\n\nconst FIGURE_NAMESPACE_FIELDS = 'id name description state isDefault'\n\n/** The categories the server allows, or null when a category is free text (LV figure-server design G5). */\nexport async function fetchFigureCategories(): Promise<string[] | null> {\n const response = await client.query({\n query: gql`\n query {\n figureCategories\n }\n `,\n fetchPolicy: 'network-only'\n })\n return unwrap<string[] | null>(response, 'figureCategories') ?? null\n}\n\n/** The domain's namespaces, its default first. */\nexport async function fetchFigureNamespaces(): Promise<FigureNamespace[]> {\n const response = await client.query({\n query: gql`\n query {\n figureNamespaces { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n fetchPolicy: 'network-only'\n })\n return unwrap<FigureNamespace[]>(response, 'figureNamespaces') ?? []\n}\n\n/** Adds a namespace to the domain; the server says why when it refuses (not a namespace, another domain's). */\nexport async function createFigureNamespace(name: string, isDefault = false): Promise<FigureNamespace> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($name: String!, $isDefault: Boolean) {\n createFigureNamespace(name: $name, isDefault: $isDefault) { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n variables: { name, isDefault }\n })\n return unwrap<FigureNamespace>(response, 'createFigureNamespace')\n}\n\n/** Makes one of the domain's active namespaces its default. */\nexport async function setDefaultFigureNamespace(name: string): Promise<FigureNamespace> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($name: String!) {\n setDefaultFigureNamespace(name: $name) { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n variables: { name }\n })\n return unwrap<FigureNamespace>(response, 'setDefaultFigureNamespace')\n}\n\n/** Suspends a namespace or makes it active again. */\nexport async function setFigureNamespaceState(name: string, state: 'active' | 'suspended'): Promise<FigureNamespace> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($name: String!, $state: String!) {\n setFigureNamespaceState(name: $name, state: $state) { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n variables: { name, state }\n })\n return unwrap<FigureNamespace>(response, 'setFigureNamespaceState')\n}\n\n/** A draft a revert kept when it wrote over it (ADR-0097 §5.6). Not a released version. */\nexport interface KeptDraft {\n id: string\n kind: 'revert' | 'undo'\n state?: string\n version?: number\n revertedToVersion?: number\n restoredAt?: string\n createdAt?: string\n creator?: { name?: string }\n}\n\nconst KEPT_FIELDS = `id kind state version revertedToVersion restoredAt createdAt creator { name }`\n\n/**\n * 옛 판을 초안으로 되살린다. 정본·속성·그림이 돌아오고 이름·설명은 그대로다. 덮이는 초안은 같은 트랜잭션에서 서버가\n * 보관한다. `expectedSource` 는 서버가 들고 있다고 확인된 정본 -- 그 사이 바뀌었으면 거절된다. 답을 못 받았으면\n * (REVERT_UNREACHABLE) 같은 `requestId` 로 다시 묻는다: 서버는 같은 되돌리기로 답하고 두 번 하지 않는다.\n */\nexport async function revertFigureVersion(\n id: string,\n version: number,\n requestId: string,\n expectedSource: string\n): Promise<SaveResult & { kept: KeptDraft }> {\n const response = await reached(\n client.mutate({\n mutation: gql`\n mutation RevertFigureVersion($id: String!, $version: Float!, $requestId: String!, $expectedSource: String!) {\n revertFigureVersion(id: $id, version: $version, requestId: $requestId, expectedSource: $expectedSource) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n kept { ${KEPT_FIELDS} }\n }\n }\n `,\n variables: { id, version, requestId, expectedSource }\n }),\n 'REVERT_UNREACHABLE'\n )\n refusedIn(response)\n return unwrap<SaveResult & { kept: KeptDraft }>(response, 'revertFigureVersion')\n}\n\n/** 되돌리기 취소 -- 보관한 초안을 되살린다. 되돌린 뒤 모델이 바뀌었으면 서버가 거절하고 보관본은 남는다. */\nexport async function undoFigureRevert(keptId: string, requestId: string): Promise<SaveResult & { kept: KeptDraft }> {\n const response = await reached(\n client.mutate({\n mutation: gql`\n mutation UndoFigureRevert($keptId: String!, $requestId: String!) {\n undoFigureRevert(keptId: $keptId, requestId: $requestId) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n kept { ${KEPT_FIELDS} }\n }\n }\n `,\n variables: { keptId, requestId }\n }),\n 'REVERT_UNREACHABLE'\n )\n refusedIn(response)\n return unwrap<SaveResult & { kept: KeptDraft }>(response, 'undoFigureRevert')\n}\n\n/** A figure an AI proposal was saved into (ADR-0097 §5.6 item 5). */\nexport interface ProposalOutcome {\n figureId: string\n type?: string\n name?: string\n recordedAt?: string\n}\n\n/**\n * 제안이 무엇이 됐나 -- 그 제안을 담은 저장이 들어간 도형들. 비었으면 저장되지 않았다(적용만 하고 저장 전이거나, 되돌렸거나,\n * 적용하지 않았다). 도크의 「결과 확인」이 이것을 묻는다: 도크의 기억이 아니라 서버의 기록이다.\n */\nexport async function fetchProposalOutcome(proposalKey: string): Promise<ProposalOutcome[]> {\n const response = await client.query({\n query: gql`\n query FigureProposalOutcome($proposalKey: String!) {\n figureProposalOutcome(proposalKey: $proposalKey) { figureId type name recordedAt }\n }\n `,\n variables: { proposalKey },\n fetchPolicy: 'network-only'\n })\n return unwrap<ProposalOutcome[]>(response, 'figureProposalOutcome') ?? []\n}\n\n/** 되돌리기가 보관한 초안들, 새것부터 -- 다시 접속해도 여기서 찾는다. */\nexport async function fetchReplacedDrafts(id: string): Promise<KeptDraft[]> {\n const response = await client.query({\n query: gql`\n query FigureReplacedDrafts($id: String!) {\n figureReplacedDrafts(id: $id) { ${KEPT_FIELDS} }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n return unwrap<KeptDraft[]>(response, 'figureReplacedDrafts')\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 return (await fetchReleasedFigure(id))?.source\n}\n\n/** Read-only catalogue preview: source and version come from the same released snapshot. */\nexport async function fetchReleasedFigure(id: string): Promise<{ version: number; source: 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<{ version: number; source: string } | undefined>(response, 'figureReleased') ?? 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\n/** A recipe template as the server gives it (ADR-0095, amended): words in the language asked, the recipe as JSON. */\nexport interface RecipeTemplateRow {\n name: string\n category: string\n tags: string[]\n target: 'part' | 'figure'\n title: string\n description: string\n source: string\n hasThumbnail: boolean\n}\n\nexport async function fetchRecipeTemplates(lang: string): Promise<RecipeTemplateRow[]> {\n const response = await client.query({\n query: gql`\n query ($lang: String) {\n figureRecipeTemplates(lang: $lang) {\n name\n category\n tags\n target\n title\n description\n source\n hasThumbnail\n }\n }\n `,\n variables: { lang }\n })\n return unwrap<RecipeTemplateRow[] | null>(response, 'figureRecipeTemplates') ?? []\n}\n\n/** One template's guide in the language asked, or '' while none is written. */\nexport async function fetchRecipeGuide(name: string, lang: string): Promise<string> {\n const response = await client.query({\n query: gql`\n query ($name: String!, $lang: String) {\n figureRecipeTemplate(name: $name, lang: $lang) {\n guide\n }\n }\n `,\n variables: { name, lang }\n })\n return unwrap<{ guide?: string } | null>(response, 'figureRecipeTemplate')?.guide ?? ''\n}\n\n/**\n * A GLB for a mesh part (ADR-0129): the server reads it, keeps it by its hash and answers what it holds. A file a board\n * cannot draw is refused with the reason in the screen's words.\n */\nexport async function uploadFigureMesh(file: File, onProgress: UploadProgress): Promise<import('../modeller/mesh-import.js').MeshUpload> {\n const response = await client.mutate({\n mutation: gql`\n mutation UploadFigureMesh($file: Upload!) {\n uploadFigureMesh(file: $file) {\n hash\n summary\n }\n }\n `,\n variables: { file },\n context: uploadContext(onProgress)\n })\n return unwrap(response, 'uploadFigureMesh')\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, uploadContext, type UploadProgress } from '@operato/graphql'\n\nimport { reached, refusedIn } from './reached.js'\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 namespace\n type\n name\n description\n category\n tags\n state\n version\n deprecated\n supersededBy\n public\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 /** 'public': other domains' public figures, with their domain (LV figure-server design G2). The domain's own by default. */\n scope?: 'own' | 'public'\n}): Promise<FigureListResult> {\n const field = params.scope === 'public' ? 'publicFigures' : 'figures'\n const response = await client.query({\n query: gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n ${field}(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items { ${FIGURE_LIST_FIELDS} ${params.scope === 'public' ? 'domain { id name subdomain }' : ''} }\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, field)\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\n/** Makes a released figure public to other domains, or private again (LV figure-server design G2). */\nexport async function setFigurePublic(id: string, isPublic: boolean): Promise<Figure> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $public: Boolean!) {\n setFigurePublic(id: $id, public: $public) {\n ${FIGURE_LIST_FIELDS}\n }\n }\n `,\n variables: { id, public: isPublic }\n })\n\n return unwrap<Figure>(response, 'setFigurePublic')\n}\n\n/** Retires a released figure, naming the type to use instead, or brings it back. Release rows are not touched. */\nexport async function setFigureDeprecation(id: string, deprecated: boolean, supersededBy?: string | null): Promise<Figure> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $deprecated: Boolean!, $supersededBy: String) {\n setFigureDeprecation(id: $id, deprecated: $deprecated, supersededBy: $supersededBy) {\n ${FIGURE_LIST_FIELDS}\n }\n }\n `,\n variables: { id, deprecated, supersededBy: supersededBy || null }\n })\n\n return unwrap<Figure>(response, 'setFigureDeprecation')\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/** A namespace the domain owns (LV figure-server design G1): the `{namespace}` of the full name `{namespace}/{type}`. */\nexport interface FigureNamespace {\n id: string\n name: string\n description?: string | null\n state: 'active' | 'suspended'\n isDefault: boolean\n}\n\nconst FIGURE_NAMESPACE_FIELDS = 'id name description state isDefault'\n\n/** The categories the server allows, or null when a category is free text (LV figure-server design G5). */\nexport async function fetchFigureCategories(): Promise<string[] | null> {\n const response = await client.query({\n query: gql`\n query {\n figureCategories\n }\n `,\n fetchPolicy: 'network-only'\n })\n return unwrap<string[] | null>(response, 'figureCategories') ?? null\n}\n\n/** The domain's namespaces, its default first. */\nexport async function fetchFigureNamespaces(): Promise<FigureNamespace[]> {\n const response = await client.query({\n query: gql`\n query {\n figureNamespaces { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n fetchPolicy: 'network-only'\n })\n return unwrap<FigureNamespace[]>(response, 'figureNamespaces') ?? []\n}\n\n/** Adds a namespace to the domain; the server says why when it refuses (not a namespace, another domain's). */\nexport async function createFigureNamespace(name: string, isDefault = false): Promise<FigureNamespace> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($name: String!, $isDefault: Boolean) {\n createFigureNamespace(name: $name, isDefault: $isDefault) { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n variables: { name, isDefault }\n })\n return unwrap<FigureNamespace>(response, 'createFigureNamespace')\n}\n\n/** Makes one of the domain's active namespaces its default. */\nexport async function setDefaultFigureNamespace(name: string): Promise<FigureNamespace> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($name: String!) {\n setDefaultFigureNamespace(name: $name) { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n variables: { name }\n })\n return unwrap<FigureNamespace>(response, 'setDefaultFigureNamespace')\n}\n\n/** Suspends a namespace or makes it active again. */\nexport async function setFigureNamespaceState(name: string, state: 'active' | 'suspended'): Promise<FigureNamespace> {\n const response = await client.mutate({\n mutation: gql`\n mutation ($name: String!, $state: String!) {\n setFigureNamespaceState(name: $name, state: $state) { ${FIGURE_NAMESPACE_FIELDS} }\n }\n `,\n variables: { name, state }\n })\n return unwrap<FigureNamespace>(response, 'setFigureNamespaceState')\n}\n\n/** A draft a revert kept when it wrote over it (ADR-0097 §5.6). Not a released version. */\nexport interface KeptDraft {\n id: string\n kind: 'revert' | 'undo'\n state?: string\n version?: number\n revertedToVersion?: number\n restoredAt?: string\n createdAt?: string\n creator?: { name?: string }\n}\n\nconst KEPT_FIELDS = `id kind state version revertedToVersion restoredAt createdAt creator { name }`\n\n/**\n * 옛 판을 초안으로 되살린다. 정본·속성·그림이 돌아오고 이름·설명은 그대로다. 덮이는 초안은 같은 트랜잭션에서 서버가\n * 보관한다. `expectedSource` 는 서버가 들고 있다고 확인된 정본 -- 그 사이 바뀌었으면 거절된다. 답을 못 받았으면\n * (REVERT_UNREACHABLE) 같은 `requestId` 로 다시 묻는다: 서버는 같은 되돌리기로 답하고 두 번 하지 않는다.\n */\nexport async function revertFigureVersion(\n id: string,\n version: number,\n requestId: string,\n expectedSource: string\n): Promise<SaveResult & { kept: KeptDraft }> {\n const response = await reached(\n client.mutate({\n mutation: gql`\n mutation RevertFigureVersion($id: String!, $version: Float!, $requestId: String!, $expectedSource: String!) {\n revertFigureVersion(id: $id, version: $version, requestId: $requestId, expectedSource: $expectedSource) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n kept { ${KEPT_FIELDS} }\n }\n }\n `,\n variables: { id, version, requestId, expectedSource }\n }),\n 'REVERT_UNREACHABLE'\n )\n refusedIn(response)\n return unwrap<SaveResult & { kept: KeptDraft }>(response, 'revertFigureVersion')\n}\n\n/** 되돌리기 취소 -- 보관한 초안을 되살린다. 되돌린 뒤 모델이 바뀌었으면 서버가 거절하고 보관본은 남는다. */\nexport async function undoFigureRevert(keptId: string, requestId: string): Promise<SaveResult & { kept: KeptDraft }> {\n const response = await reached(\n client.mutate({\n mutation: gql`\n mutation UndoFigureRevert($keptId: String!, $requestId: String!) {\n undoFigureRevert(keptId: $keptId, requestId: $requestId) {\n figure { ${FIGURE_LIST_FIELDS} source }\n violations { code message at }\n kept { ${KEPT_FIELDS} }\n }\n }\n `,\n variables: { keptId, requestId }\n }),\n 'REVERT_UNREACHABLE'\n )\n refusedIn(response)\n return unwrap<SaveResult & { kept: KeptDraft }>(response, 'undoFigureRevert')\n}\n\n/** A figure an AI proposal was saved into (ADR-0097 §5.6 item 5). */\nexport interface ProposalOutcome {\n figureId: string\n type?: string\n name?: string\n recordedAt?: string\n}\n\n/**\n * 제안이 무엇이 됐나 -- 그 제안을 담은 저장이 들어간 피규어들. 비었으면 저장되지 않았다(적용만 하고 저장 전이거나, 되돌렸거나,\n * 적용하지 않았다). 도크의 「결과 확인」이 이것을 묻는다: 도크의 기억이 아니라 서버의 기록이다.\n */\nexport async function fetchProposalOutcome(proposalKey: string): Promise<ProposalOutcome[]> {\n const response = await client.query({\n query: gql`\n query FigureProposalOutcome($proposalKey: String!) {\n figureProposalOutcome(proposalKey: $proposalKey) { figureId type name recordedAt }\n }\n `,\n variables: { proposalKey },\n fetchPolicy: 'network-only'\n })\n return unwrap<ProposalOutcome[]>(response, 'figureProposalOutcome') ?? []\n}\n\n/** 되돌리기가 보관한 초안들, 새것부터 -- 다시 접속해도 여기서 찾는다. */\nexport async function fetchReplacedDrafts(id: string): Promise<KeptDraft[]> {\n const response = await client.query({\n query: gql`\n query FigureReplacedDrafts($id: String!) {\n figureReplacedDrafts(id: $id) { ${KEPT_FIELDS} }\n }\n `,\n variables: { id },\n fetchPolicy: 'network-only'\n })\n return unwrap<KeptDraft[]>(response, 'figureReplacedDrafts')\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 return (await fetchReleasedFigure(id))?.source\n}\n\n/** Read-only catalogue preview: source and version come from the same released snapshot. */\nexport async function fetchReleasedFigure(id: string): Promise<{ version: number; source: 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<{ version: number; source: string } | undefined>(response, 'figureReleased') ?? 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\n/** A recipe template as the server gives it (ADR-0095, amended): words in the language asked, the recipe as JSON. */\nexport interface RecipeTemplateRow {\n name: string\n category: string\n tags: string[]\n target: 'part' | 'figure'\n title: string\n description: string\n source: string\n hasThumbnail: boolean\n}\n\nexport async function fetchRecipeTemplates(lang: string): Promise<RecipeTemplateRow[]> {\n const response = await client.query({\n query: gql`\n query ($lang: String) {\n figureRecipeTemplates(lang: $lang) {\n name\n category\n tags\n target\n title\n description\n source\n hasThumbnail\n }\n }\n `,\n variables: { lang }\n })\n return unwrap<RecipeTemplateRow[] | null>(response, 'figureRecipeTemplates') ?? []\n}\n\n/** One template's guide in the language asked, or '' while none is written. */\nexport async function fetchRecipeGuide(name: string, lang: string): Promise<string> {\n const response = await client.query({\n query: gql`\n query ($name: String!, $lang: String) {\n figureRecipeTemplate(name: $name, lang: $lang) {\n guide\n }\n }\n `,\n variables: { name, lang }\n })\n return unwrap<{ guide?: string } | null>(response, 'figureRecipeTemplate')?.guide ?? ''\n}\n\n/**\n * A GLB for a mesh part (ADR-0129): the server reads it, keeps it by its hash and answers what it holds. A file a board\n * cannot draw is refused with the reason in the screen's words.\n */\nexport async function uploadFigureMesh(file: File, onProgress: UploadProgress): Promise<import('../modeller/mesh-import.js').MeshUpload> {\n const response = await client.mutate({\n mutation: gql`\n mutation UploadFigureMesh($file: Upload!) {\n uploadFigureMesh(file: $file) {\n hash\n summary\n }\n }\n `,\n variables: { file },\n context: uploadContext(onProgress)\n })\n return unwrap(response, 'uploadFigureMesh')\n}\n"]}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 한 화면에 세운 상태). 미리보기는 크기 반응을 보려고 여덟 대를 나란히 놓는 자리라 한 대가
|
|
10
10
|
* 20 픽셀쯤이고, 그 위의 어떤 표시도 몇 픽셀을 넘지 못한다.
|
|
11
11
|
*
|
|
12
|
-
* 각을 읽는 일은 3D 의 배율에서 떼어 놓는다. 눈은 막대 옆 24 픽셀 자리에 서고,
|
|
12
|
+
* 각을 읽는 일은 3D 의 배율에서 떼어 놓는다. 눈은 막대 옆 24 픽셀 자리에 서고, 피규어가 아무리
|
|
13
13
|
* 작게 서 있어도 같은 크기로 읽힌다.
|
|
14
14
|
*/
|
|
15
15
|
export interface DialShape {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"angle-dial.js","sourceRoot":"","sources":["../../client/modeller/angle-dial.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0BH,wDAAwD;AACxD,SAAS,EAAE,CAAC,MAAgC,EAAE,MAAc,EAAE,OAAe;IAC3E,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;IACrC,OAAO;QACL,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAClD,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;KACnD,CAAA;AACH,CAAC;AAED,iCAAiC;AACjC,MAAM,IAAI,GAAG,KAAK,CAAA;AAElB;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa,EAAE,MAAM,GAAG,CAAC;IAC3E,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IAE9E,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;QACtC,OAAO;YACL,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,UAAU,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI;YACjI,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC;YAChC,MAAM;YACN,MAAM;SACP,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEhC,OAAO;QACL,MAAM,EAAE,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI;QAC9G,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC;QAChC,MAAM;QACN,MAAM;KACP,CAAA;AACH,CAAC","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\n/**\n * 막대 옆에 서는 **각도 눈**. 지금 각이 범위의 어디인지를 한 눈에 보인다.\n *\n * ## 왜 3D 로는 모자랐나\n *\n * 관절이 돌아도 화면이 그대로인 경우가 있다 — 제 축에서 대칭인 부품이 그렇다. 그래서 관절이\n * 모는 부품에 바늘을 달아 미리보기에서 함께 돌게 했는데(`joint-gizmo.ts` 의 `jointNeedles`),\n * **미리보기에서는 그 바늘이 4 × 2 픽셀이다**(잰 값: 부품 100mm · 바늘 60 × 12mm · 여덟 대를\n * 한 화면에 세운 상태). 미리보기는 크기 반응을 보려고 여덟 대를 나란히 놓는 자리라 한 대가\n * 20 픽셀쯤이고, 그 위의 어떤 표시도 몇 픽셀을 넘지 못한다.\n *\n * 각을 읽는 일은 3D 의 배율에서 떼어 놓는다. 눈은 막대 옆 24 픽셀 자리에 서고,
|
|
1
|
+
{"version":3,"file":"angle-dial.js","sourceRoot":"","sources":["../../client/modeller/angle-dial.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0BH,wDAAwD;AACxD,SAAS,EAAE,CAAC,MAAgC,EAAE,MAAc,EAAE,OAAe;IAC3E,MAAM,GAAG,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;IACrC,OAAO;QACL,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAClD,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;KACnD,CAAA;AACH,CAAC;AAED,iCAAiC;AACjC,MAAM,IAAI,GAAG,KAAK,CAAA;AAElB;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa,EAAE,MAAM,GAAG,CAAC;IAC3E,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IAE9E,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;QACjC,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;QACtC,OAAO;YACL,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,UAAU,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI;YACjI,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC;YAChC,MAAM;YACN,MAAM;SACP,CAAA;IACH,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IACpC,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEhC,OAAO;QACL,MAAM,EAAE,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,MAAM,IAAI,MAAM,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI;QAC9G,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC;QAChC,MAAM;QACN,MAAM;KACP,CAAA;AACH,CAAC","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n */\n\n/**\n * 막대 옆에 서는 **각도 눈**. 지금 각이 범위의 어디인지를 한 눈에 보인다.\n *\n * ## 왜 3D 로는 모자랐나\n *\n * 관절이 돌아도 화면이 그대로인 경우가 있다 — 제 축에서 대칭인 부품이 그렇다. 그래서 관절이\n * 모는 부품에 바늘을 달아 미리보기에서 함께 돌게 했는데(`joint-gizmo.ts` 의 `jointNeedles`),\n * **미리보기에서는 그 바늘이 4 × 2 픽셀이다**(잰 값: 부품 100mm · 바늘 60 × 12mm · 여덟 대를\n * 한 화면에 세운 상태). 미리보기는 크기 반응을 보려고 여덟 대를 나란히 놓는 자리라 한 대가\n * 20 픽셀쯤이고, 그 위의 어떤 표시도 몇 픽셀을 넘지 못한다.\n *\n * 각을 읽는 일은 3D 의 배율에서 떼어 놓는다. 눈은 막대 옆 24 픽셀 자리에 서고, 피규어가 아무리\n * 작게 서 있어도 같은 크기로 읽힌다.\n */\n\nexport interface DialShape {\n /** 가동 범위를 채운 부채꼴. SVG path 의 `d`. */\n sector: string\n /** 지금 값이 가리키는 끝점. */\n needle: { x: number; y: number }\n centre: { x: number; y: number }\n radius: number\n}\n\n/** 0 도가 위를 가리키고, 값이 커지면 시계 방향으로 돈다 — 화면의 각과 같은 방향이다. */\nfunction at(centre: { x: number; y: number }, radius: number, degrees: number) {\n const rad = (degrees * Math.PI) / 180\n return {\n x: +(centre.x + radius * Math.sin(rad)).toFixed(2),\n y: +(centre.y - radius * Math.cos(rad)).toFixed(2)\n }\n}\n\n/** 한 바퀴에 가까우면 부채꼴 대신 온전한 원이다. */\nconst FULL = 359.9\n\n/**\n * 눈의 모양. 값이 범위를 벗어나면 범위 끝에 세운다 — 밖을 가리키는 바늘은 없는 값을 말한다.\n */\nexport function dialShape(min: number, max: number, value: number, radius = 9): DialShape {\n const centre = { x: radius + 1, y: radius + 1 }\n const span = Math.abs(max - min)\n const held = Math.min(Math.max(value, Math.min(min, max)), Math.max(min, max))\n\n if (span >= FULL) {\n const top = at(centre, radius, 0)\n const bottom = at(centre, radius, 180)\n return {\n sector: `M ${top.x} ${top.y} A ${radius} ${radius} 0 1 1 ${bottom.x} ${bottom.y} A ${radius} ${radius} 0 1 1 ${top.x} ${top.y} Z`,\n needle: at(centre, radius, held),\n centre,\n radius\n }\n }\n\n const from = at(centre, radius, min)\n const to = at(centre, radius, max)\n const large = span > 180 ? 1 : 0\n\n return {\n sector: `M ${centre.x} ${centre.y} L ${from.x} ${from.y} A ${radius} ${radius} 0 ${large} 1 ${to.x} ${to.y} Z`,\n needle: at(centre, radius, held),\n centre,\n radius\n }\n}\n"]}
|
|
@@ -90,7 +90,7 @@ let FigureCanvas = class FigureCanvas extends LitElement {
|
|
|
90
90
|
render() {
|
|
91
91
|
/*
|
|
92
92
|
* No line of its own over an empty stage: a new figure's start card says how to begin, and an empty stage otherwise
|
|
93
|
-
* is one loading, or with every part hidden -- where
|
|
93
|
+
* is one loading, or with every part hidden -- where the empty stage's hint flashed and said the wrong thing (2026-10-02).
|
|
94
94
|
*/
|
|
95
95
|
return html `<div stage></div>`;
|
|
96
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../client/modeller/canvas.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css, unsafeCSS, type PropertyValues } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { create, Model, niceGridStep } from '@hatiolab/things-scene'\n\nimport type { GizmoReach, GizmoResult } from './gizmo.js'\nimport type { FigureRenderPart } from './renderer.js'\nimport { DEFAULT_VIEW, sceneLook, WORKBENCH, type ViewSettings } from '@things-factory/figure-viewer/dist-client/figure-view.js'\nimport { boardFor, changedKeys, componentOf, gizmoResultOf, needsRefit, type Board } from './stage-model.js'\nimport { JOINT_FAN } from './joint-fan.js'\nimport { SLOT_MARK } from './slot-layout-host.js'\nimport { PREVIEW, TARGET_FACE } from './command-preview.js'\nimport { ownerOf } from './fields.js'\n\n/*\n * The modeller's canvas -- a things-scene board in editing mode.\n *\n * Each body figure-model evaluates stands on the board as a `figure-part` component, so everything the canvas\n * has comes from the scene, not from code of our own: picking and the orange outline, the gizmo with its snapping and\n * live readout, box and shift selection, the camera (damping, zoom toward the cursor, double-click to fit, the six\n * view presets and their keys), the studio environment, the coordinate ground, lights and shadows, supersampling.\n * An earlier canvas drew the bodies with three.js directly and lost all of that (user, 2026-09-24: \"why nerf it?\").\n *\n * What the gizmo writes back to a component comes out as `part-transformed` in the inputs' terms (`stage-model`),\n * and the editor turns it into one step of the history; the next evaluation puts the component where the inputs say,\n * so a change a part cannot take (a fastened part moved) springs back.\n */\n\ninterface SceneComponent {\n state: Record<string, unknown>\n set: (props: Record<string, unknown>) => void\n dispose?: () => void\n}\ninterface SceneLayer extends SceneComponent {\n components?: SceneComponent[]\n root?: { on?: (event: string, handler: (...args: unknown[]) => void) => void; selected?: SceneComponent[] }\n on?: (event: string, handler: (...args: unknown[]) => void) => void\n addComponent?: (component: SceneComponent) => void\n removeComponent?: (component: SceneComponent) => void\n invalidate?: () => void\n fitCameraToModel?: () => void\n setGizmoMode?: (mode: 'translate' | 'rotate' | 'scale') => void\n app?: unknown\n}\ninterface SceneHandle {\n dispose?: () => void\n resize?: () => void\n fit?: (type?: string) => void\n root?: SceneLayer\n}\n\n/* How long the figure stands still before the camera judges whether to follow it. */\nconst REFIT_SETTLE_MS = 400\n\n/* The fan's colour when the figure's palette names no accent. */\nconst FAN_COLOUR = '#ff8a00'\n\n/* The 2D side of the board, which the author never sees on this page but the scene paints. */\nconst PAPER = '#ffffff'\n\n@customElement('figure-canvas')\nexport class FigureCanvas extends LitElement {\n static styles = css`\n :host {\n position: relative;\n display: flex;\n min-height: 0;\n overflow: hidden;\n background-color: ${unsafeCSS(WORKBENCH)};\n }\n /* The scene sets position: relative inline on its target; the size is given so it still fills the host. */\n div[stage] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n }\n `\n @property({ attribute: false }) parts: FigureRenderPart[] = []\n @property({ attribute: false }) palette: Record<string, string> = {}\n /** Which figure is open. Another one stands the board up again. */\n @property() context = ''\n @property() selected = ''\n /**\n * The fastening window is open (ADR-0140 stage 4): a part pressed is aimed at, not chosen -- told out as `part-aimed`\n * with where the press landed (the scene's `pickedAt`: the point and the drawn triangle's outward normal, in the\n * figure's frame), and the part being fastened stays chosen.\n */\n @property({ type: Boolean }) aiming = false\n /** What the gizmo may write for the selected part; a change beyond it is not applied and the part springs back. */\n @property({ attribute: false }) reach: GizmoReach = { translate: false, rotate: false, scale: false }\n @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW\n /**\n * View only: nothing that happens on the board is told out. The card's picture is taken from a second canvas stood up\n * off screen (`thumbnail.ts`); the scene settles its components as it builds them, and a change told from there\n * would reach the editor as an edit.\n */\n @property({ type: Boolean }) viewOnly = false\n /** Where the selected part turns and how far (`joint-fan`): shown, never picked, never saved. */\n @property({ attribute: false }) fan: FigureRenderPart | null = null\n /**\n * A command seen before it is done (ADR-0097 §5.5): the bodies it would move, where they would stand, translucent,\n * and the face they would be fastened to, lit. Shown, never picked, never saved; gone when the command is.\n */\n @property({ attribute: false }) ghost: FigureRenderPart[] = []\n @property({ attribute: false }) highlight: FigureRenderPart | null = null\n /** The slots a slot layout unfolds to (ADR-0118), as boxes of what each allows: shown, never picked, never saved. */\n @property({ attribute: false }) marks: FigureRenderPart[] = []\n\n @query('div[stage]') private stage!: HTMLDivElement\n\n private scene?: SceneHandle\n private board?: Board & { depth: number }\n /** The figure's reach when the view was last fitted to it. */\n private fittedFor?: Board & { depth: number }\n private refitTimer?: ReturnType<typeof setTimeout>\n private standingFor?: string\n private watcher?: ResizeObserver\n /** Set while this canvas writes to components, so the scene's echo of it is not taken for a drag. */\n private writing = false\n\n render() {\n /*\n * No line of its own over an empty stage: a new figure's start card says how to begin, and an empty stage otherwise\n * is one loading, or with every part hidden -- where 「도형을 추가해…」 flashed and said the wrong thing (2026-10-02).\n */\n return html`<div stage></div>`\n }\n\n firstUpdated() {\n this.standUp()\n /* Out of sight it has no size to draw at: it resizes when it is shown again, not to nothing. */\n this.watcher = new ResizeObserver(() => {\n if (this.checkVisibility?.() !== false) this.scene?.resize?.()\n })\n this.watcher.observe(this)\n }\n\n protected updated(changed: PropertyValues) {\n if (!this.hasUpdated) return\n if (['parts', 'palette', 'context', 'fan', 'ghost', 'highlight', 'marks'].some(key => changed.has(key))) this.standUp()\n if (changed.has('view')) this.applyView()\n if (changed.has('selected')) this.showSelected()\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n clearTimeout(this.refitTimer)\n this.watcher?.disconnect()\n this.watcher = undefined\n this.teardown()\n }\n\n /**\n * The board as a picture, for the card: the 3D view at this size, or nothing when it cannot be taken. The scene's\n * `toDataURL` renders the 2D board (a white sheet), and the 3D renderer keeps no drawing buffer, so the\n * scene's own `snapshot3d` draws and reads in one task. A picture that cannot be taken is not an error and is not\n * faked (user, 2026-09-24: no card had a picture after this canvas moved onto the scene).\n */\n async snapshot(width = 480, height = 360): Promise<string | undefined> {\n /* Kept standing while the preview is in front, it is not drawn: no picture, as when there was no canvas. */\n if (this.checkVisibility?.() === false) return undefined\n const layer = this.scene?.root as (SceneLayer & { snapshot3d?: (w?: number, h?: number, type?: string, quality?: number) => Promise<string | undefined> }) | undefined\n if (!layer?.snapshot3d) return undefined\n try {\n /* webp: a card needs no alpha, and png is three or four times heavier in every list response. */\n const url = await layer.snapshot3d(width, height, 'image/webp', 0.85)\n return typeof url === 'string' && url.startsWith('data:') ? url : undefined\n } catch (e) {\n console.warn(`[thumbnail] could not take the picture -- ${(e as Error).message}`)\n return undefined\n }\n }\n\n /** The whole figure in view: the board's fit, then the 3D camera's own. */\n fit() {\n this.fittedFor = boardFor(this.parts)\n this.scene?.fit?.('ratio')\n this.scene?.root?.fitCameraToModel?.()\n }\n\n /** The gizmo in a mode on the selected part, as W, E and R do with the canvas focused -- the scene's toggle. */\n gizmo(mode: 'translate' | 'rotate' | 'scale') {\n this.scene?.root?.setGizmoMode?.(mode)\n }\n\n /**\n * Stand the board up for this figure, or bring the one standing up to date. A new board only for another figure or\n * one that has outgrown it -- standing up again loses the selection and the camera.\n */\n private standUp() {\n if (!this.stage) return\n const needed = boardFor(this.parts, this.board)\n const outgrown = !this.board || needed.width > this.board.width || needed.height > this.board.height || needed.depth > this.board.depth\n if (this.scene && this.standingFor === this.context) {\n /*\n A part dragged past the board's edge grows the board where it stands. Standing it up again lost the selection,\n the gizmo and the camera after every such drag -- the gizmo seemed not to work at all (user, 2026-09-24).\n */\n if (outgrown) this.grow(needed)\n this.sync()\n /*\n Grown or shrunk far past the view (a size typed in): the camera follows, once the figure has settled. A value is\n drawn as it is typed -- 「3」, 「30」, 「300」 on the way to 3000 -- so the reach is judged after the last change.\n */\n clearTimeout(this.refitTimer)\n if (this.parts.length) this.refitTimer = setTimeout(() => needsRefit(this.fittedFor, boardFor(this.parts)) && this.fit(), REFIT_SETTLE_MS)\n return\n }\n this.teardown()\n if (!this.parts.length) return\n this.board = needed\n try {\n this.scene = create({\n target: this.stage,\n model: {\n type: 'model-layer',\n width: needed.width,\n height: needed.height,\n depth: needed.depth,\n threed: true,\n /* Two samples a pixel: MSAA alone leaves steps on the silhouettes of one asset shown large. */\n pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),\n ...sceneLook(this.view ?? DEFAULT_VIEW),\n gridStep: niceGridStep(Math.max(needed.width, needed.height)),\n sky: 'color',\n fillStyle: PAPER,\n components: this.components()\n },\n /* Editing mode: the camera view buttons, gizmo modes, help and keys. */\n mode: 1\n }) as SceneHandle\n this.standingFor = this.context\n this.watchScene()\n this.scene.resize?.()\n this.fit()\n this.showSelected()\n } catch (error) {\n this.dispatchEvent(new CustomEvent('render-error', { detail: error, bubbles: true, composed: true }))\n }\n }\n\n /**\n * A part's component state, kept for the part object it was made from while the palette and the board are the same: a\n * size drag redraws the board every frame, and only the parts the frame moved are new objects (ADR-0140 4.4).\n */\n private made = new WeakMap<FigureRenderPart, Record<string, unknown> | null>()\n private madeFor?: [Record<string, string>, Board]\n private componentFor(part: FigureRenderPart): Record<string, unknown> | null {\n if (this.madeFor?.[0] !== this.palette || this.madeFor?.[1] !== this.board) (this.made = new WeakMap()), (this.madeFor = [this.palette, this.board!])\n if (!this.made.has(part)) this.made.set(part, componentOf(part, this.palette, this.board!, true))\n return this.made.get(part)!\n }\n\n private components(): Record<string, unknown>[] {\n const parts = this.parts.map(part => this.componentFor(part)).filter((c): c is Record<string, unknown> => !!c)\n /* The fan in the accent colour, translucent; `capturable: false` keeps clicks, the marquee and the gizmo off it. */\n const fan = this.fan ? componentOf(this.fan, { ...this.palette, [JOINT_FAN]: this.palette['palette.accent'] ?? FAN_COLOUR }, this.board!) : null\n const accent = this.palette['palette.accent'] ?? FAN_COLOUR\n const slots = this.marks.map(part => componentOf(part, { ...this.palette, [SLOT_MARK]: this.palette['palette.secondary'] ?? accent }, this.board!))\n const shown = [...(fan ? [fan] : []), ...slots, ...this.ghost.map(part => componentOf(part, { ...this.palette, [PREVIEW]: accent, [TARGET_FACE]: accent }, this.board!)), ...(this.highlight ? [componentOf(this.highlight, { ...this.palette, [TARGET_FACE]: accent }, this.board!)] : [])]\n return [...parts, ...shown.filter((c): c is Record<string, unknown> => !!c).map(c => ({ ...c, capturable: false, locked: true }))]\n }\n\n /** The components standing now, by name. Not held between calls: the scene stands its board up again at times. */\n private standing(): Map<string, SceneComponent> {\n const found = new Map<string, SceneComponent>()\n for (const component of this.scene?.root?.components ?? []) {\n const name = component.state?.name\n if (typeof name === 'string') found.set(name, component)\n }\n return found\n }\n\n /** A larger board under the same scene: its size, and the grid that suits it. Components are re-placed by `sync`. */\n private grow(needed: Board & { depth: number }) {\n const layer = this.scene?.root\n this.board = needed\n if (!layer) return\n this.writing = true\n try {\n layer.set({ width: needed.width, height: needed.height, depth: needed.depth, gridStep: niceGridStep(Math.max(needed.width, needed.height)) })\n } finally {\n this.writing = false\n }\n }\n\n /** Bring the components to the evaluation: remove the gone, write only what differs, add the new. */\n private sync() {\n const layer = this.scene?.root\n if (!layer || !this.board) return\n const wanted = new Map(this.components().map(c => [c.name as string, c]))\n const standing = this.standing()\n this.writing = true\n try {\n for (const [name, component] of standing) {\n if (wanted.has(name)) continue\n layer.removeComponent?.(component)\n component.dispose?.()\n }\n for (const [name, state] of wanted) {\n const found = standing.get(name)\n if (found) {\n const diff = changedKeys(found.state, state)\n /*\n A part whose size is being dragged keeps the size the gizmo gives it: what is drawn does not go back into what the\n gizmo measures (ADR-0140 4.4). Written back, a size the frame could not change (one that follows the figure) put\n the part back to its old size, and the release wrote nothing.\n */\n if (diff && this.pointerDown && this.grabs.has(name)) for (const key of ['width', 'depth', 'height']) delete diff[key]\n if (diff && Object.keys(diff).length) found.set(diff)\n continue\n }\n const made = Model.compile({ ...state }, layer.app as never) as SceneComponent | null\n if (made) layer.addComponent?.(made)\n }\n } finally {\n this.writing = false\n }\n layer.invalidate?.()\n }\n\n /** Selection and drags, told out by the part's name -- never by its place in the list, which may change meanwhile. */\n private watchScene() {\n const layer = this.scene?.root\n const root = layer?.root\n if (!layer || !root?.on || this.viewOnly) return\n root.on('selected', (after: unknown) => {\n /*\n A canvas taken off the screen disposes its scene, and the scene empties its selection as it goes. Told out,\n that emptied the editor's choice whenever the middle turned to the preview (U03): the listener lit set on this\n element hears it even after it left the page. A canvas not on the page has nothing to say about the choice.\n */\n if (!this.isConnected) return\n /*\n While aiming, a press is told by the click (below); the part being fastened is chosen again once the scene has\n done with the pick -- put back while the scene was still telling it, the selection read right but the outline\n stayed on the part pressed (:3300, 2026-10-20).\n */\n if (this.aiming) {\n setTimeout(() => this.showSelected())\n return\n }\n const name = (after as SceneComponent[] | undefined)?.[0]?.state?.name\n const id = typeof name === 'string' ? name : ''\n if (ownerOf(id) !== this.selected) this.dispatchEvent(new CustomEvent('part-selected', { detail: id, bubbles: true, composed: true }))\n })\n /*\n A press while aiming: the part pressed -- the one being fastened too, which changes no selection and so is told by\n no `selected` -- with where it landed (`pickedAt`, filled before the click is told). It comes up to the board\n with the component in the last argument's `origin`, as a change does.\n */\n layer.on?.('click', (...args: unknown[]) => {\n if (!this.aiming || !this.isConnected) return\n const origin = (args[args.length - 1] as { origin?: SceneComponent & { realObject?: { pickedAt?: { point: number[]; normal: number[] } } } } | undefined)?.origin\n const id = origin?.state?.name\n if (typeof id !== 'string' || !id) return\n const at = origin?.realObject?.pickedAt\n this.dispatchEvent(new CustomEvent('part-aimed', { detail: { id, ...(at ? { point: at.point, normal: at.normal } : {}) }, bubbles: true, composed: true }))\n })\n /*\n A change comes up to the board with the component in the last argument's `origin` (the scene appends\n { origin, name, deliverer } as it bubbles). Reading the first argument as the component does nothing.\n */\n /*\n A drag is written once, when it ends. The scene's gizmo writes the component on every move -- a size anchored on\n the opposite face, from the scale it captured when the drag began -- and writing each move back through the\n editor (and the evaluation back onto the component) fought it: the scale gizmo did not work (user, 2026-09-24).\n While a pointer is down the changes are only kept; on release the last one is told, and the board is brought to\n the evaluation. A change with no pointer down (the arrow keys) is told after a short pause.\n */\n layer.on?.('change', (...args: unknown[]) => {\n if (this.writing) return\n const origin = (args[args.length - 1] as { origin?: SceneComponent } | undefined)?.origin\n const name = origin?.state?.name\n if (typeof name !== 'string' || !this.parts.some(p => p.id === name)) return\n this.pending.set(name, origin!)\n /* A size drag: the scene tells the handle held; figure-model draws each frame from it (ADR-0140 4.4). */\n const grab = origin!.state?.scaleGrab as GizmoResult['grab'] | undefined\n if (this.pointerDown && grab) {\n this.grabs.set(name, { ...grab })\n this.sizingSoon(name)\n }\n if (!this.pointerDown) this.commitSoon(80)\n })\n this.stage.addEventListener('pointerdown', this.onPointerDown, true)\n window.addEventListener('pointerup', this.onPointerUp, true)\n window.addEventListener('pointercancel', this.onPointerUp, true)\n }\n\n /** Components the gizmo changed since the last commit, by part name. */\n private pending = new Map<string, SceneComponent>()\n /** The handle each size drag held, by part name, from the scene's `scaleGrab` -- told with the release. */\n private grabs = new Map<string, NonNullable<GizmoResult['grab']>>()\n private sizingFrame?: number\n /** Once an animation frame: where a size drag stands, told so the editor draws figure-model's result for it. */\n private sizingSoon(name: string) {\n if (this.sizingFrame !== undefined) return\n this.sizingFrame = requestAnimationFrame(() => {\n this.sizingFrame = undefined\n const part = this.pressedPart(name)\n const component = this.pending.get(name)\n if (!part || !component || !this.board || !this.pointerDown) return\n const result = { ...gizmoResultOf(part, component.state, this.board), grab: this.grabs.get(name) }\n if (result.scale) this.dispatchEvent(new CustomEvent('part-sizing', { detail: { id: part.id, result }, bubbles: true, composed: true }))\n })\n }\n private pointerDown = false\n private commitTimer?: ReturnType<typeof setTimeout>\n /**\n * The parts as they stood when the pointer went down. A drag is measured against them: while a size drag is drawn, the\n * parts given are its frames (ADR-0140 4.4), and measured against those it grew on itself and its release wrote nothing.\n */\n private atPress?: Map<string, FigureRenderPart>\n private onPointerDown = () => {\n this.pointerDown = true\n this.atPress = new Map(this.parts.map(p => [p.id, p]))\n }\n private pressedPart(name: string): FigureRenderPart | undefined {\n return this.atPress?.get(name) ?? this.parts.find(p => p.id === name)\n }\n private onPointerUp = () => {\n if (!this.pointerDown) return\n this.pointerDown = false\n /* After the scene's own drag end (it settles the depth and rebuilds the geometry on release). */\n this.commitSoon(0)\n }\n private commitSoon(delay: number) {\n clearTimeout(this.commitTimer)\n this.commitTimer = setTimeout(() => this.commit(), delay)\n }\n /** Tell each changed part once, in the inputs' terms; then put the board where the inputs say (a refusal springs back). */\n private commit() {\n /*\n Not while a pointer is down: a pause set by a change just before the press (the arrow keys, the scene settling\n the pick) fired in the middle of the drag and wrote where the part was then -- a drag on a part placed by a rule\n became two steps, the first one a few millimetres in (browser check, 2026-09-25). What is pending waits for the release.\n */\n if (this.pointerDown || !this.board || !this.pending.size) return\n const changed = [...this.pending]\n this.pending.clear()\n if (this.sizingFrame !== undefined) cancelAnimationFrame(this.sizingFrame)\n this.sizingFrame = undefined\n for (const [name, component] of changed) {\n const part = this.pressedPart(name)\n if (!part) continue\n const grab = this.grabs.get(name)\n const result = { ...gizmoResultOf(part, component.state, this.board), ...(grab ? { grab } : {}) }\n if (!result.position && !result.rotation && !result.scale) continue\n this.dispatchEvent(new CustomEvent('part-transformed', { detail: { id: part.id, result }, bubbles: true, composed: true }))\n }\n this.grabs.clear()\n this.atPress = undefined\n setTimeout(() => this.sync())\n }\n\n private showSelected() {\n const layer = this.scene?.root\n const root = layer?.root\n if (!layer || !root) return\n /* A repeat is selected whole: every copy of it (the editor selects the part the author made). */\n const wanted = this.selected ? [...this.standing()].filter(([name]) => ownerOf(name) === this.selected).map(([, component]) => component) : []\n const now = root.selected ?? []\n if (now.length === wanted.length && wanted.every((one, i) => now[i] === one)) return\n root.selected = wanted\n layer.invalidate?.()\n }\n\n /** View settings go into the scene as they are: they are the board's own state names. Nothing is stood up again. */\n private applyView() {\n const layer = this.scene?.root\n if (!layer) return\n layer.set(sceneLook(this.view ?? DEFAULT_VIEW))\n layer.invalidate?.()\n }\n\n private teardown() {\n clearTimeout(this.commitTimer)\n this.pending.clear()\n this.grabs.clear()\n if (this.sizingFrame !== undefined) cancelAnimationFrame(this.sizingFrame)\n this.sizingFrame = undefined\n this.stage?.removeEventListener('pointerdown', this.onPointerDown, true)\n window.removeEventListener('pointerup', this.onPointerUp, true)\n window.removeEventListener('pointercancel', this.onPointerUp, true)\n this.scene?.dispose?.()\n this.scene = undefined\n this.standingFor = undefined\n this.board = undefined\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../client/modeller/canvas.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css, unsafeCSS, type PropertyValues } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { create, Model, niceGridStep } from '@hatiolab/things-scene'\n\nimport type { GizmoReach, GizmoResult } from './gizmo.js'\nimport type { FigureRenderPart } from './renderer.js'\nimport { DEFAULT_VIEW, sceneLook, WORKBENCH, type ViewSettings } from '@things-factory/figure-viewer/dist-client/figure-view.js'\nimport { boardFor, changedKeys, componentOf, gizmoResultOf, needsRefit, type Board } from './stage-model.js'\nimport { JOINT_FAN } from './joint-fan.js'\nimport { SLOT_MARK } from './slot-layout-host.js'\nimport { PREVIEW, TARGET_FACE } from './command-preview.js'\nimport { ownerOf } from './fields.js'\n\n/*\n * The modeller's canvas -- a things-scene board in editing mode.\n *\n * Each body figure-model evaluates stands on the board as a `figure-part` component, so everything the canvas\n * has comes from the scene, not from code of our own: picking and the orange outline, the gizmo with its snapping and\n * live readout, box and shift selection, the camera (damping, zoom toward the cursor, double-click to fit, the six\n * view presets and their keys), the studio environment, the coordinate ground, lights and shadows, supersampling.\n * An earlier canvas drew the bodies with three.js directly and lost all of that (user, 2026-09-24: \"why nerf it?\").\n *\n * What the gizmo writes back to a component comes out as `part-transformed` in the inputs' terms (`stage-model`),\n * and the editor turns it into one step of the history; the next evaluation puts the component where the inputs say,\n * so a change a part cannot take (a fastened part moved) springs back.\n */\n\ninterface SceneComponent {\n state: Record<string, unknown>\n set: (props: Record<string, unknown>) => void\n dispose?: () => void\n}\ninterface SceneLayer extends SceneComponent {\n components?: SceneComponent[]\n root?: { on?: (event: string, handler: (...args: unknown[]) => void) => void; selected?: SceneComponent[] }\n on?: (event: string, handler: (...args: unknown[]) => void) => void\n addComponent?: (component: SceneComponent) => void\n removeComponent?: (component: SceneComponent) => void\n invalidate?: () => void\n fitCameraToModel?: () => void\n setGizmoMode?: (mode: 'translate' | 'rotate' | 'scale') => void\n app?: unknown\n}\ninterface SceneHandle {\n dispose?: () => void\n resize?: () => void\n fit?: (type?: string) => void\n root?: SceneLayer\n}\n\n/* How long the figure stands still before the camera judges whether to follow it. */\nconst REFIT_SETTLE_MS = 400\n\n/* The fan's colour when the figure's palette names no accent. */\nconst FAN_COLOUR = '#ff8a00'\n\n/* The 2D side of the board, which the author never sees on this page but the scene paints. */\nconst PAPER = '#ffffff'\n\n@customElement('figure-canvas')\nexport class FigureCanvas extends LitElement {\n static styles = css`\n :host {\n position: relative;\n display: flex;\n min-height: 0;\n overflow: hidden;\n background-color: ${unsafeCSS(WORKBENCH)};\n }\n /* The scene sets position: relative inline on its target; the size is given so it still fills the host. */\n div[stage] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n }\n `\n @property({ attribute: false }) parts: FigureRenderPart[] = []\n @property({ attribute: false }) palette: Record<string, string> = {}\n /** Which figure is open. Another one stands the board up again. */\n @property() context = ''\n @property() selected = ''\n /**\n * The fastening window is open (ADR-0140 stage 4): a part pressed is aimed at, not chosen -- told out as `part-aimed`\n * with where the press landed (the scene's `pickedAt`: the point and the drawn triangle's outward normal, in the\n * figure's frame), and the part being fastened stays chosen.\n */\n @property({ type: Boolean }) aiming = false\n /** What the gizmo may write for the selected part; a change beyond it is not applied and the part springs back. */\n @property({ attribute: false }) reach: GizmoReach = { translate: false, rotate: false, scale: false }\n @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW\n /**\n * View only: nothing that happens on the board is told out. The card's picture is taken from a second canvas stood up\n * off screen (`thumbnail.ts`); the scene settles its components as it builds them, and a change told from there\n * would reach the editor as an edit.\n */\n @property({ type: Boolean }) viewOnly = false\n /** Where the selected part turns and how far (`joint-fan`): shown, never picked, never saved. */\n @property({ attribute: false }) fan: FigureRenderPart | null = null\n /**\n * A command seen before it is done (ADR-0097 §5.5): the bodies it would move, where they would stand, translucent,\n * and the face they would be fastened to, lit. Shown, never picked, never saved; gone when the command is.\n */\n @property({ attribute: false }) ghost: FigureRenderPart[] = []\n @property({ attribute: false }) highlight: FigureRenderPart | null = null\n /** The slots a slot layout unfolds to (ADR-0118), as boxes of what each allows: shown, never picked, never saved. */\n @property({ attribute: false }) marks: FigureRenderPart[] = []\n\n @query('div[stage]') private stage!: HTMLDivElement\n\n private scene?: SceneHandle\n private board?: Board & { depth: number }\n /** The figure's reach when the view was last fitted to it. */\n private fittedFor?: Board & { depth: number }\n private refitTimer?: ReturnType<typeof setTimeout>\n private standingFor?: string\n private watcher?: ResizeObserver\n /** Set while this canvas writes to components, so the scene's echo of it is not taken for a drag. */\n private writing = false\n\n render() {\n /*\n * No line of its own over an empty stage: a new figure's start card says how to begin, and an empty stage otherwise\n * is one loading, or with every part hidden -- where the empty stage's hint flashed and said the wrong thing (2026-10-02).\n */\n return html`<div stage></div>`\n }\n\n firstUpdated() {\n this.standUp()\n /* Out of sight it has no size to draw at: it resizes when it is shown again, not to nothing. */\n this.watcher = new ResizeObserver(() => {\n if (this.checkVisibility?.() !== false) this.scene?.resize?.()\n })\n this.watcher.observe(this)\n }\n\n protected updated(changed: PropertyValues) {\n if (!this.hasUpdated) return\n if (['parts', 'palette', 'context', 'fan', 'ghost', 'highlight', 'marks'].some(key => changed.has(key))) this.standUp()\n if (changed.has('view')) this.applyView()\n if (changed.has('selected')) this.showSelected()\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n clearTimeout(this.refitTimer)\n this.watcher?.disconnect()\n this.watcher = undefined\n this.teardown()\n }\n\n /**\n * The board as a picture, for the card: the 3D view at this size, or nothing when it cannot be taken. The scene's\n * `toDataURL` renders the 2D board (a white sheet), and the 3D renderer keeps no drawing buffer, so the\n * scene's own `snapshot3d` draws and reads in one task. A picture that cannot be taken is not an error and is not\n * faked (user, 2026-09-24: no card had a picture after this canvas moved onto the scene).\n */\n async snapshot(width = 480, height = 360): Promise<string | undefined> {\n /* Kept standing while the preview is in front, it is not drawn: no picture, as when there was no canvas. */\n if (this.checkVisibility?.() === false) return undefined\n const layer = this.scene?.root as (SceneLayer & { snapshot3d?: (w?: number, h?: number, type?: string, quality?: number) => Promise<string | undefined> }) | undefined\n if (!layer?.snapshot3d) return undefined\n try {\n /* webp: a card needs no alpha, and png is three or four times heavier in every list response. */\n const url = await layer.snapshot3d(width, height, 'image/webp', 0.85)\n return typeof url === 'string' && url.startsWith('data:') ? url : undefined\n } catch (e) {\n console.warn(`[thumbnail] could not take the picture -- ${(e as Error).message}`)\n return undefined\n }\n }\n\n /** The whole figure in view: the board's fit, then the 3D camera's own. */\n fit() {\n this.fittedFor = boardFor(this.parts)\n this.scene?.fit?.('ratio')\n this.scene?.root?.fitCameraToModel?.()\n }\n\n /** The gizmo in a mode on the selected part, as W, E and R do with the canvas focused -- the scene's toggle. */\n gizmo(mode: 'translate' | 'rotate' | 'scale') {\n this.scene?.root?.setGizmoMode?.(mode)\n }\n\n /**\n * Stand the board up for this figure, or bring the one standing up to date. A new board only for another figure or\n * one that has outgrown it -- standing up again loses the selection and the camera.\n */\n private standUp() {\n if (!this.stage) return\n const needed = boardFor(this.parts, this.board)\n const outgrown = !this.board || needed.width > this.board.width || needed.height > this.board.height || needed.depth > this.board.depth\n if (this.scene && this.standingFor === this.context) {\n /*\n A part dragged past the board's edge grows the board where it stands. Standing it up again lost the selection,\n the gizmo and the camera after every such drag -- the gizmo seemed not to work at all (user, 2026-09-24).\n */\n if (outgrown) this.grow(needed)\n this.sync()\n /*\n Grown or shrunk far past the view (a size typed in): the camera follows, once the figure has settled. A value is\n drawn as it is typed -- 「3」, 「30」, 「300」 on the way to 3000 -- so the reach is judged after the last change.\n */\n clearTimeout(this.refitTimer)\n if (this.parts.length) this.refitTimer = setTimeout(() => needsRefit(this.fittedFor, boardFor(this.parts)) && this.fit(), REFIT_SETTLE_MS)\n return\n }\n this.teardown()\n if (!this.parts.length) return\n this.board = needed\n try {\n this.scene = create({\n target: this.stage,\n model: {\n type: 'model-layer',\n width: needed.width,\n height: needed.height,\n depth: needed.depth,\n threed: true,\n /* Two samples a pixel: MSAA alone leaves steps on the silhouettes of one asset shown large. */\n pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),\n ...sceneLook(this.view ?? DEFAULT_VIEW),\n gridStep: niceGridStep(Math.max(needed.width, needed.height)),\n sky: 'color',\n fillStyle: PAPER,\n components: this.components()\n },\n /* Editing mode: the camera view buttons, gizmo modes, help and keys. */\n mode: 1\n }) as SceneHandle\n this.standingFor = this.context\n this.watchScene()\n this.scene.resize?.()\n this.fit()\n this.showSelected()\n } catch (error) {\n this.dispatchEvent(new CustomEvent('render-error', { detail: error, bubbles: true, composed: true }))\n }\n }\n\n /**\n * A part's component state, kept for the part object it was made from while the palette and the board are the same: a\n * size drag redraws the board every frame, and only the parts the frame moved are new objects (ADR-0140 4.4).\n */\n private made = new WeakMap<FigureRenderPart, Record<string, unknown> | null>()\n private madeFor?: [Record<string, string>, Board]\n private componentFor(part: FigureRenderPart): Record<string, unknown> | null {\n if (this.madeFor?.[0] !== this.palette || this.madeFor?.[1] !== this.board) (this.made = new WeakMap()), (this.madeFor = [this.palette, this.board!])\n if (!this.made.has(part)) this.made.set(part, componentOf(part, this.palette, this.board!, true))\n return this.made.get(part)!\n }\n\n private components(): Record<string, unknown>[] {\n const parts = this.parts.map(part => this.componentFor(part)).filter((c): c is Record<string, unknown> => !!c)\n /* The fan in the accent colour, translucent; `capturable: false` keeps clicks, the marquee and the gizmo off it. */\n const fan = this.fan ? componentOf(this.fan, { ...this.palette, [JOINT_FAN]: this.palette['palette.accent'] ?? FAN_COLOUR }, this.board!) : null\n const accent = this.palette['palette.accent'] ?? FAN_COLOUR\n const slots = this.marks.map(part => componentOf(part, { ...this.palette, [SLOT_MARK]: this.palette['palette.secondary'] ?? accent }, this.board!))\n const shown = [...(fan ? [fan] : []), ...slots, ...this.ghost.map(part => componentOf(part, { ...this.palette, [PREVIEW]: accent, [TARGET_FACE]: accent }, this.board!)), ...(this.highlight ? [componentOf(this.highlight, { ...this.palette, [TARGET_FACE]: accent }, this.board!)] : [])]\n return [...parts, ...shown.filter((c): c is Record<string, unknown> => !!c).map(c => ({ ...c, capturable: false, locked: true }))]\n }\n\n /** The components standing now, by name. Not held between calls: the scene stands its board up again at times. */\n private standing(): Map<string, SceneComponent> {\n const found = new Map<string, SceneComponent>()\n for (const component of this.scene?.root?.components ?? []) {\n const name = component.state?.name\n if (typeof name === 'string') found.set(name, component)\n }\n return found\n }\n\n /** A larger board under the same scene: its size, and the grid that suits it. Components are re-placed by `sync`. */\n private grow(needed: Board & { depth: number }) {\n const layer = this.scene?.root\n this.board = needed\n if (!layer) return\n this.writing = true\n try {\n layer.set({ width: needed.width, height: needed.height, depth: needed.depth, gridStep: niceGridStep(Math.max(needed.width, needed.height)) })\n } finally {\n this.writing = false\n }\n }\n\n /** Bring the components to the evaluation: remove the gone, write only what differs, add the new. */\n private sync() {\n const layer = this.scene?.root\n if (!layer || !this.board) return\n const wanted = new Map(this.components().map(c => [c.name as string, c]))\n const standing = this.standing()\n this.writing = true\n try {\n for (const [name, component] of standing) {\n if (wanted.has(name)) continue\n layer.removeComponent?.(component)\n component.dispose?.()\n }\n for (const [name, state] of wanted) {\n const found = standing.get(name)\n if (found) {\n const diff = changedKeys(found.state, state)\n /*\n A part whose size is being dragged keeps the size the gizmo gives it: what is drawn does not go back into what the\n gizmo measures (ADR-0140 4.4). Written back, a size the frame could not change (one that follows the figure) put\n the part back to its old size, and the release wrote nothing.\n */\n if (diff && this.pointerDown && this.grabs.has(name)) for (const key of ['width', 'depth', 'height']) delete diff[key]\n if (diff && Object.keys(diff).length) found.set(diff)\n continue\n }\n const made = Model.compile({ ...state }, layer.app as never) as SceneComponent | null\n if (made) layer.addComponent?.(made)\n }\n } finally {\n this.writing = false\n }\n layer.invalidate?.()\n }\n\n /** Selection and drags, told out by the part's name -- never by its place in the list, which may change meanwhile. */\n private watchScene() {\n const layer = this.scene?.root\n const root = layer?.root\n if (!layer || !root?.on || this.viewOnly) return\n root.on('selected', (after: unknown) => {\n /*\n A canvas taken off the screen disposes its scene, and the scene empties its selection as it goes. Told out,\n that emptied the editor's choice whenever the middle turned to the preview (U03): the listener lit set on this\n element hears it even after it left the page. A canvas not on the page has nothing to say about the choice.\n */\n if (!this.isConnected) return\n /*\n While aiming, a press is told by the click (below); the part being fastened is chosen again once the scene has\n done with the pick -- put back while the scene was still telling it, the selection read right but the outline\n stayed on the part pressed (:3300, 2026-10-20).\n */\n if (this.aiming) {\n setTimeout(() => this.showSelected())\n return\n }\n const name = (after as SceneComponent[] | undefined)?.[0]?.state?.name\n const id = typeof name === 'string' ? name : ''\n if (ownerOf(id) !== this.selected) this.dispatchEvent(new CustomEvent('part-selected', { detail: id, bubbles: true, composed: true }))\n })\n /*\n A press while aiming: the part pressed -- the one being fastened too, which changes no selection and so is told by\n no `selected` -- with where it landed (`pickedAt`, filled before the click is told). It comes up to the board\n with the component in the last argument's `origin`, as a change does.\n */\n layer.on?.('click', (...args: unknown[]) => {\n if (!this.aiming || !this.isConnected) return\n const origin = (args[args.length - 1] as { origin?: SceneComponent & { realObject?: { pickedAt?: { point: number[]; normal: number[] } } } } | undefined)?.origin\n const id = origin?.state?.name\n if (typeof id !== 'string' || !id) return\n const at = origin?.realObject?.pickedAt\n this.dispatchEvent(new CustomEvent('part-aimed', { detail: { id, ...(at ? { point: at.point, normal: at.normal } : {}) }, bubbles: true, composed: true }))\n })\n /*\n A change comes up to the board with the component in the last argument's `origin` (the scene appends\n { origin, name, deliverer } as it bubbles). Reading the first argument as the component does nothing.\n */\n /*\n A drag is written once, when it ends. The scene's gizmo writes the component on every move -- a size anchored on\n the opposite face, from the scale it captured when the drag began -- and writing each move back through the\n editor (and the evaluation back onto the component) fought it: the scale gizmo did not work (user, 2026-09-24).\n While a pointer is down the changes are only kept; on release the last one is told, and the board is brought to\n the evaluation. A change with no pointer down (the arrow keys) is told after a short pause.\n */\n layer.on?.('change', (...args: unknown[]) => {\n if (this.writing) return\n const origin = (args[args.length - 1] as { origin?: SceneComponent } | undefined)?.origin\n const name = origin?.state?.name\n if (typeof name !== 'string' || !this.parts.some(p => p.id === name)) return\n this.pending.set(name, origin!)\n /* A size drag: the scene tells the handle held; figure-model draws each frame from it (ADR-0140 4.4). */\n const grab = origin!.state?.scaleGrab as GizmoResult['grab'] | undefined\n if (this.pointerDown && grab) {\n this.grabs.set(name, { ...grab })\n this.sizingSoon(name)\n }\n if (!this.pointerDown) this.commitSoon(80)\n })\n this.stage.addEventListener('pointerdown', this.onPointerDown, true)\n window.addEventListener('pointerup', this.onPointerUp, true)\n window.addEventListener('pointercancel', this.onPointerUp, true)\n }\n\n /** Components the gizmo changed since the last commit, by part name. */\n private pending = new Map<string, SceneComponent>()\n /** The handle each size drag held, by part name, from the scene's `scaleGrab` -- told with the release. */\n private grabs = new Map<string, NonNullable<GizmoResult['grab']>>()\n private sizingFrame?: number\n /** Once an animation frame: where a size drag stands, told so the editor draws figure-model's result for it. */\n private sizingSoon(name: string) {\n if (this.sizingFrame !== undefined) return\n this.sizingFrame = requestAnimationFrame(() => {\n this.sizingFrame = undefined\n const part = this.pressedPart(name)\n const component = this.pending.get(name)\n if (!part || !component || !this.board || !this.pointerDown) return\n const result = { ...gizmoResultOf(part, component.state, this.board), grab: this.grabs.get(name) }\n if (result.scale) this.dispatchEvent(new CustomEvent('part-sizing', { detail: { id: part.id, result }, bubbles: true, composed: true }))\n })\n }\n private pointerDown = false\n private commitTimer?: ReturnType<typeof setTimeout>\n /**\n * The parts as they stood when the pointer went down. A drag is measured against them: while a size drag is drawn, the\n * parts given are its frames (ADR-0140 4.4), and measured against those it grew on itself and its release wrote nothing.\n */\n private atPress?: Map<string, FigureRenderPart>\n private onPointerDown = () => {\n this.pointerDown = true\n this.atPress = new Map(this.parts.map(p => [p.id, p]))\n }\n private pressedPart(name: string): FigureRenderPart | undefined {\n return this.atPress?.get(name) ?? this.parts.find(p => p.id === name)\n }\n private onPointerUp = () => {\n if (!this.pointerDown) return\n this.pointerDown = false\n /* After the scene's own drag end (it settles the depth and rebuilds the geometry on release). */\n this.commitSoon(0)\n }\n private commitSoon(delay: number) {\n clearTimeout(this.commitTimer)\n this.commitTimer = setTimeout(() => this.commit(), delay)\n }\n /** Tell each changed part once, in the inputs' terms; then put the board where the inputs say (a refusal springs back). */\n private commit() {\n /*\n Not while a pointer is down: a pause set by a change just before the press (the arrow keys, the scene settling\n the pick) fired in the middle of the drag and wrote where the part was then -- a drag on a part placed by a rule\n became two steps, the first one a few millimetres in (browser check, 2026-09-25). What is pending waits for the release.\n */\n if (this.pointerDown || !this.board || !this.pending.size) return\n const changed = [...this.pending]\n this.pending.clear()\n if (this.sizingFrame !== undefined) cancelAnimationFrame(this.sizingFrame)\n this.sizingFrame = undefined\n for (const [name, component] of changed) {\n const part = this.pressedPart(name)\n if (!part) continue\n const grab = this.grabs.get(name)\n const result = { ...gizmoResultOf(part, component.state, this.board), ...(grab ? { grab } : {}) }\n if (!result.position && !result.rotation && !result.scale) continue\n this.dispatchEvent(new CustomEvent('part-transformed', { detail: { id: part.id, result }, bubbles: true, composed: true }))\n }\n this.grabs.clear()\n this.atPress = undefined\n setTimeout(() => this.sync())\n }\n\n private showSelected() {\n const layer = this.scene?.root\n const root = layer?.root\n if (!layer || !root) return\n /* A repeat is selected whole: every copy of it (the editor selects the part the author made). */\n const wanted = this.selected ? [...this.standing()].filter(([name]) => ownerOf(name) === this.selected).map(([, component]) => component) : []\n const now = root.selected ?? []\n if (now.length === wanted.length && wanted.every((one, i) => now[i] === one)) return\n root.selected = wanted\n layer.invalidate?.()\n }\n\n /** View settings go into the scene as they are: they are the board's own state names. Nothing is stood up again. */\n private applyView() {\n const layer = this.scene?.root\n if (!layer) return\n layer.set(sceneLook(this.view ?? DEFAULT_VIEW))\n layer.invalidate?.()\n }\n\n private teardown() {\n clearTimeout(this.commitTimer)\n this.pending.clear()\n this.grabs.clear()\n if (this.sizingFrame !== undefined) cancelAnimationFrame(this.sizingFrame)\n this.sizingFrame = undefined\n this.stage?.removeEventListener('pointerdown', this.onPointerDown, true)\n window.removeEventListener('pointerup', this.onPointerUp, true)\n window.removeEventListener('pointercancel', this.onPointerUp, true)\n this.scene?.dispose?.()\n this.scene = undefined\n this.standingFor = undefined\n this.board = undefined\n }\n}\n"]}
|
|
@@ -270,6 +270,11 @@ export declare class FigureEditor extends LitElement {
|
|
|
270
270
|
/** Runs a command; true when it was taken. */
|
|
271
271
|
private linkCommand;
|
|
272
272
|
private changeField;
|
|
273
|
+
/**
|
|
274
|
+
* A number written to an input, from a field, a scrub or the size editor's rows: a size keeps the part's middle, or its
|
|
275
|
+
* fastening -- figure-model's one size function (ADR-0140 4.1). Gives the value the input holds after, refused or not.
|
|
276
|
+
*/
|
|
277
|
+
private writeField;
|
|
273
278
|
/** Typing or a held spinner: each number on the way is drawn; the field's change records it. */
|
|
274
279
|
private typing;
|
|
275
280
|
/**
|