@things-factory/figure-ui 10.1.6 → 10.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/graphql/index.ts +1 -0
- package/client/modeller/figure-animations.ts +712 -0
- package/client/modeller/figure-inspector.ts +47 -0
- package/client/modeller/figure-side.ts +65 -17
- package/client/pages/figure-list-page.ts +56 -4
- package/client/pages/figure-modeller-page.ts +25 -126
- package/client/types.ts +6 -0
- package/client/viewparts/figure-thumb.ts +78 -0
- package/dist-client/graphql/index.js +1 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/modeller/figure-animations.d.ts +93 -0
- package/dist-client/modeller/figure-animations.js +655 -0
- package/dist-client/modeller/figure-animations.js.map +1 -0
- package/dist-client/modeller/figure-inspector.d.ts +14 -0
- package/dist-client/modeller/figure-inspector.js +46 -0
- package/dist-client/modeller/figure-inspector.js.map +1 -1
- package/dist-client/modeller/figure-side.d.ts +23 -0
- package/dist-client/modeller/figure-side.js +68 -16
- package/dist-client/modeller/figure-side.js.map +1 -1
- package/dist-client/pages/figure-list-page.d.ts +10 -0
- package/dist-client/pages/figure-list-page.js +55 -4
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +7 -19
- package/dist-client/pages/figure-modeller-page.js +24 -126
- package/dist-client/pages/figure-modeller-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/types.d.ts +6 -0
- package/dist-client/types.js.map +1 -1
- package/dist-client/viewparts/figure-thumb.js +74 -0
- package/dist-client/viewparts/figure-thumb.js.map +1 -1
- package/package.json +3 -4
- package/test/i18n-prefix-guard.test.ts +42 -0
- package/translations/en.json +50 -6
- package/translations/ja.json +2 -1
- package/translations/ko.json +50 -6
- package/translations/ms.json +2 -1
- package/translations/zh.json +2 -1
package/dist-client/types.d.ts
CHANGED
|
@@ -13,7 +13,10 @@ export interface Figure {
|
|
|
13
13
|
type: string;
|
|
14
14
|
name: string;
|
|
15
15
|
description?: string;
|
|
16
|
+
/** 분류 하나 — 팔레트에서 묶어 보이는 기준. */
|
|
16
17
|
category?: string;
|
|
18
|
+
/** 태그 여럿 — 분류로는 못 하는 찾기. */
|
|
19
|
+
tags?: string[];
|
|
17
20
|
state?: 'draft' | 'released';
|
|
18
21
|
/** 재활용 점수 0~100. 서버가 정본에서 계산한다 — 화면이 보내는 값이 아니다. */
|
|
19
22
|
score?: number;
|
|
@@ -65,6 +68,7 @@ export interface NewFigure {
|
|
|
65
68
|
name: string;
|
|
66
69
|
description?: string;
|
|
67
70
|
category?: string;
|
|
71
|
+
tags?: string[];
|
|
68
72
|
source: string;
|
|
69
73
|
properties?: string;
|
|
70
74
|
state?: 'draft' | 'released';
|
|
@@ -80,6 +84,8 @@ export interface FigurePatch {
|
|
|
80
84
|
name?: string;
|
|
81
85
|
description?: string;
|
|
82
86
|
category?: string;
|
|
87
|
+
/** 빈 배열은 「전부 지운다」, 안 주는 것은 「건드리지 않는다」. */
|
|
88
|
+
tags?: string[];
|
|
83
89
|
source?: string;
|
|
84
90
|
properties?: string;
|
|
85
91
|
state?: 'draft' | 'released';
|
package/dist-client/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../client/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * 화면이 다루는 모양.\n *\n * 정본 형식(`FigureSource`)의 타입은 **여기서 다시 정의하지 않는다** —\n * `@hatiolab/figure-model` 에서 가져온다. 두 벌이면 갈리고, 갈리면 화면이 서버가\n * 거절할 것을 만들어 낸다.\n */\nexport type { FigureSource, FigureBlueprint, FigurePart, ErrorCode, ViolationCode } from '@hatiolab/figure-model'\n\n/** 서버가 돌려주는 Figure 한 벌. `source` 는 저작면에서만 받는다. */\nexport interface Figure {\n id: string\n /** 컴포넌트 타입 이름. **만든 뒤에 고칠 수 없다.** */\n type: string\n name: string\n description?: string\n category?: string\n state?: 'draft' | 'released'\n /** 재활용 점수 0~100. 서버가 정본에서 계산한다 — 화면이 보내는 값이 아니다. */\n score?: number\n triangles?: number\n groups?: number\n thumbnail?: string\n thumbnailUpdatedAt?: string\n updatedAt?: string\n updater?: { id: string; name: string }\n /** `FigureSource` JSON 문자열. 목록 질의에는 실려 오지 않는다. */\n source?: string\n properties?: string\n version?: number\n}\n\n/**\n * 발행된 판 하나.\n *\n * 저작 중 저장은 여기 오지 않는다 — 판본은 발행에서만 생긴다.\n */\nexport interface FigureVersion {\n version: number\n /** 발행하며 남긴 말. */\n comment?: string\n state?: 'draft' | 'released'\n updatedAt?: string\n updater?: { id: string; name: string }\n score?: number\n triangles?: number\n groups?: number\n}\n\nexport interface FigureListResult {\n items: Figure[]\n total: number\n}\n\n/**\n * 새 Figure.\n *\n * `score`·`triangles`·`groups` 가 없다 — **서버가 정본에서 계산한다.** 화면이 보내면\n * 거짓이 들어오고 목록이 실물과 다른 것을 보여 준다.\n */\nexport interface NewFigure {\n type: string\n name: string\n description?: string\n category?: string\n source: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/**\n * 수정.\n *\n * `type` 이 없다 — 저장되는 식별자라 고칠 수 없다. 배치된 보드가 그 이름으로 타입을\n * 찾으므로, 바꾸면 그 보드가 빈 화면이 된다.\n */\nexport interface FigurePatch {\n name?: string\n description?: string\n category?: string\n source?: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/** 검증 결과 한 줄. 코드는 figure-model 의 것을 그대로 쓴다. */\nexport interface FigureFinding {\n code: string\n message: string\n at?: string\n}\n\n/**\n * 저작 보조가 낸 후보.\n *\n * **정본이 아니다.** 저작자가 보고 받아야 정본이 된다 — 서버는 저장하지 않고\n * 돌려주기만 한다.\n */\nexport interface FigureProposal {\n /** 후보 정본. JSON 문자열이다 — 화면이 파싱한다. */\n source: string\n score: number\n grade: string\n triangles: number\n /** 재질 묶음 = 인스턴스 하나가 무는 draw call. */\n groups: number\n /** 형식은 맞으나 정책을 넘은 것. 막지 않는다 — 저작자가 정한다. */\n violations: FigureFinding[]\n /** 몇 번 만에 됐나. 진단용이다. */\n attempts: number\n}\n\n/**\n * 발행 판정 한 줄.\n *\n * `FigureFinding` 과 달리 **`blocking` 이 있다.** 저장 쪽 발견은 정의상 막지 않는 것들이고,\n * 발행 판정은 막는 것과 알리는 것을 함께 내므로 어느 쪽인지를 값이 스스로 말한다 — 코드를 보고\n * 화면이 다시 분류하면 규칙이 두 벌이 된다.\n */\nexport interface FigureGateFinding {\n code: string\n message: string\n /** 왜 그것이 문제인가 — 사람이 무엇을 보게 되나. */\n why?: string\n /** 무엇을 바꾸면 되나 — 부품 이름과 값으로. */\n how?: string\n /** 갈라진 부품 이름들, 또는 정본 안의 경로. */\n at?: string\n blocking: boolean\n}\n\n/** 발행해도 되나. `blocked` 면 서버가 거절한다. */\nexport interface FigureInspection {\n blocked: boolean\n findings: FigureGateFinding[]\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../client/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * 화면이 다루는 모양.\n *\n * 정본 형식(`FigureSource`)의 타입은 **여기서 다시 정의하지 않는다** —\n * `@hatiolab/figure-model` 에서 가져온다. 두 벌이면 갈리고, 갈리면 화면이 서버가\n * 거절할 것을 만들어 낸다.\n */\nexport type { FigureSource, FigureBlueprint, FigurePart, ErrorCode, ViolationCode } from '@hatiolab/figure-model'\n\n/** 서버가 돌려주는 Figure 한 벌. `source` 는 저작면에서만 받는다. */\nexport interface Figure {\n id: string\n /** 컴포넌트 타입 이름. **만든 뒤에 고칠 수 없다.** */\n type: string\n name: string\n description?: string\n /** 분류 하나 — 팔레트에서 묶어 보이는 기준. */\n category?: string\n /** 태그 여럿 — 분류로는 못 하는 찾기. */\n tags?: string[]\n state?: 'draft' | 'released'\n /** 재활용 점수 0~100. 서버가 정본에서 계산한다 — 화면이 보내는 값이 아니다. */\n score?: number\n triangles?: number\n groups?: number\n thumbnail?: string\n thumbnailUpdatedAt?: string\n updatedAt?: string\n updater?: { id: string; name: string }\n /** `FigureSource` JSON 문자열. 목록 질의에는 실려 오지 않는다. */\n source?: string\n properties?: string\n version?: number\n}\n\n/**\n * 발행된 판 하나.\n *\n * 저작 중 저장은 여기 오지 않는다 — 판본은 발행에서만 생긴다.\n */\nexport interface FigureVersion {\n version: number\n /** 발행하며 남긴 말. */\n comment?: string\n state?: 'draft' | 'released'\n updatedAt?: string\n updater?: { id: string; name: string }\n score?: number\n triangles?: number\n groups?: number\n}\n\nexport interface FigureListResult {\n items: Figure[]\n total: number\n}\n\n/**\n * 새 Figure.\n *\n * `score`·`triangles`·`groups` 가 없다 — **서버가 정본에서 계산한다.** 화면이 보내면\n * 거짓이 들어오고 목록이 실물과 다른 것을 보여 준다.\n */\nexport interface NewFigure {\n type: string\n name: string\n description?: string\n category?: string\n tags?: string[]\n source: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/**\n * 수정.\n *\n * `type` 이 없다 — 저장되는 식별자라 고칠 수 없다. 배치된 보드가 그 이름으로 타입을\n * 찾으므로, 바꾸면 그 보드가 빈 화면이 된다.\n */\nexport interface FigurePatch {\n name?: string\n description?: string\n category?: string\n /** 빈 배열은 「전부 지운다」, 안 주는 것은 「건드리지 않는다」. */\n tags?: string[]\n source?: string\n properties?: string\n state?: 'draft' | 'released'\n thumbnail?: string\n}\n\n/** 검증 결과 한 줄. 코드는 figure-model 의 것을 그대로 쓴다. */\nexport interface FigureFinding {\n code: string\n message: string\n at?: string\n}\n\n/**\n * 저작 보조가 낸 후보.\n *\n * **정본이 아니다.** 저작자가 보고 받아야 정본이 된다 — 서버는 저장하지 않고\n * 돌려주기만 한다.\n */\nexport interface FigureProposal {\n /** 후보 정본. JSON 문자열이다 — 화면이 파싱한다. */\n source: string\n score: number\n grade: string\n triangles: number\n /** 재질 묶음 = 인스턴스 하나가 무는 draw call. */\n groups: number\n /** 형식은 맞으나 정책을 넘은 것. 막지 않는다 — 저작자가 정한다. */\n violations: FigureFinding[]\n /** 몇 번 만에 됐나. 진단용이다. */\n attempts: number\n}\n\n/**\n * 발행 판정 한 줄.\n *\n * `FigureFinding` 과 달리 **`blocking` 이 있다.** 저장 쪽 발견은 정의상 막지 않는 것들이고,\n * 발행 판정은 막는 것과 알리는 것을 함께 내므로 어느 쪽인지를 값이 스스로 말한다 — 코드를 보고\n * 화면이 다시 분류하면 규칙이 두 벌이 된다.\n */\nexport interface FigureGateFinding {\n code: string\n message: string\n /** 왜 그것이 문제인가 — 사람이 무엇을 보게 되나. */\n why?: string\n /** 무엇을 바꾸면 되나 — 부품 이름과 값으로. */\n how?: string\n /** 갈라진 부품 이름들, 또는 정본 안의 경로. */\n at?: string\n blocking: boolean\n}\n\n/** 발행해도 되나. `blocked` 면 서버가 거절한다. */\nexport interface FigureInspection {\n blocked: boolean\n findings: FigureGateFinding[]\n}\n"]}
|
|
@@ -97,6 +97,65 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
97
97
|
border-radius: 4px;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/*
|
|
101
|
+
태그 — **그림 위에 얹는다.**
|
|
102
|
+
|
|
103
|
+
카드 본문에 한 줄로 두었더니 태그가 있는 카드만 그만큼 높아져 격자가 들쭉날쭉해졌다.
|
|
104
|
+
태그는 도형마다 개수가 다르므로 본문에 두는 한 이 문제가 남는다. 그림 위는 이미 자리가
|
|
105
|
+
잡혀 있고(초안 표시·삭제), 얹어도 카드 높이가 변하지 않는다.
|
|
106
|
+
|
|
107
|
+
아래쪽에 둔다 — 위는 초안 표시와 삭제 단추가 쓴다.
|
|
108
|
+
|
|
109
|
+
**투명한 알약이다.** 면을 꽉 채우면 그림 위에 흰 띠가 생겨 형상을 가린다 — 카탈로그에서
|
|
110
|
+
먼저 보아야 하는 것은 형상이다. 그래서 바탕은 거의 비우고, 읽히는 것은 뒤를 흐리는 것과
|
|
111
|
+
실낱 테두리가 맡는다. 흐림이 안 되는 환경에서도 테두리가 남아 글자가 형상에 묻히지 않는다.
|
|
112
|
+
*/
|
|
113
|
+
div[tags] {
|
|
114
|
+
position: absolute;
|
|
115
|
+
right: 6px;
|
|
116
|
+
bottom: 6px;
|
|
117
|
+
left: 6px;
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-wrap: wrap-reverse;
|
|
120
|
+
justify-content: flex-end;
|
|
121
|
+
gap: 3px;
|
|
122
|
+
/* 태그가 많아도 그림을 덮지 않는다 — 넘치는 것은 위로 잘린다 */
|
|
123
|
+
max-height: 44px;
|
|
124
|
+
overflow: hidden;
|
|
125
|
+
/* 알약 사이의 빈 자리는 그림이 받는다 — 거기서 누르면 저작면이 열린다 */
|
|
126
|
+
pointer-events: none;
|
|
127
|
+
}
|
|
128
|
+
/*
|
|
129
|
+
**알약은 누를 수 있다.** 누르면 그 태그로 목록을 거른다.
|
|
130
|
+
|
|
131
|
+
태그를 눈에 보이게 한 다음의 물음은 「그래서 이걸로 뭘 하나」다. 같은 태그가 붙은 것을
|
|
132
|
+
보려고 태그를 눈으로 읽어 검색창에 옮겨 적게 두면, 보여 준 뜻이 절반만 산다.
|
|
133
|
+
|
|
134
|
+
카드 자체가 저작면을 여는 자리이므로 알약에서 그 클릭을 멈춘다 — 안 멈추면 태그를 누른
|
|
135
|
+
사람이 편집기로 끌려간다.
|
|
136
|
+
*/
|
|
137
|
+
div[tags] button {
|
|
138
|
+
pointer-events: auto;
|
|
139
|
+
cursor: pointer;
|
|
140
|
+
padding: 0 7px;
|
|
141
|
+
font: var(--label-font, inherit);
|
|
142
|
+
font-size: 0.66rem;
|
|
143
|
+
line-height: 1.7;
|
|
144
|
+
color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));
|
|
145
|
+
background-color: color-mix(in srgb, var(--md-sys-color-surface, #fff) 34%, transparent);
|
|
146
|
+
border: 1px solid color-mix(in srgb, var(--md-sys-color-outline, #888) 42%, transparent);
|
|
147
|
+
backdrop-filter: blur(4px);
|
|
148
|
+
border-radius: 999em;
|
|
149
|
+
white-space: nowrap;
|
|
150
|
+
}
|
|
151
|
+
div[tags] button:hover,
|
|
152
|
+
div[tags] button:focus-visible {
|
|
153
|
+
color: var(--md-sys-color-on-surface);
|
|
154
|
+
background-color: color-mix(in srgb, var(--md-sys-color-surface, #fff) 72%, transparent);
|
|
155
|
+
border-color: var(--md-sys-color-primary);
|
|
156
|
+
outline: none;
|
|
157
|
+
}
|
|
158
|
+
|
|
100
159
|
button[remove] {
|
|
101
160
|
position: absolute;
|
|
102
161
|
top: 5px;
|
|
@@ -142,6 +201,21 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
142
201
|
</div>
|
|
143
202
|
`}
|
|
144
203
|
${figure.state === 'released' ? '' : html `<span draft>${i18next.t('figure.label.draft')}</span>`}
|
|
204
|
+
${figure.tags?.length
|
|
205
|
+
? html `<div tags>
|
|
206
|
+
${figure.tags.map(tag => html `
|
|
207
|
+
<button
|
|
208
|
+
title=${i18next.t('figure.text.find-by-this-tag')}
|
|
209
|
+
@click=${(e) => {
|
|
210
|
+
e.stopPropagation();
|
|
211
|
+
this.dispatchEvent(new CustomEvent('search-tag', { detail: { tag }, bubbles: true, composed: true }));
|
|
212
|
+
}}
|
|
213
|
+
>
|
|
214
|
+
${tag}
|
|
215
|
+
</button>
|
|
216
|
+
`)}
|
|
217
|
+
</div>`
|
|
218
|
+
: ''}
|
|
145
219
|
|
|
146
220
|
<button
|
|
147
221
|
remove
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figure-thumb.js","sourceRoot":"","sources":["../../client/viewparts/figure-thumb.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AAEnC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAI5C;;;;;;;;;GASG;AACH,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACtD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,OAAO,EAAE,CAAA;IAE3D,OAAO,GAAG,WAAW,qBAAqB,MAAM,CAAC,EAAE,MAAM,KAAK,EAAE,CAAA;AAClE,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AAEI,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;aACrD,WAAM,GAAG,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"figure-thumb.js","sourceRoot":"","sources":["../../client/viewparts/figure-thumb.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { getPathInfo } from '@operato/utils'\n\nimport type { Figure } from '../types.js'\n\n/**\n * 그림을 부르는 주소.\n *\n * **base64 를 목록 응답에 실어 오지 않는다.** data URL 은 브라우저가 캐시할 수 없어, 카탈로그를\n * 열 때마다 그림 전부가 다시 온다([확인함] 표본 14개 227KB). 주소로 부르면 캐시가 되고, 판을\n * 가리는 `v` 가 바뀐 것만 다시 받는다. 내주는 자리는 `figure-thumbnail-router` 다.\n *\n * 도메인 조각(`/domain/{subdomain}`)은 셸이 쓰는 것과 같은 함수로 읽는다 — 화면이 주소를\n * 손으로 짜맞추면 도메인이 바뀌는 배치에서 조용히 틀린다.\n */\nfunction thumbnailSrc(figure: Figure): string | undefined {\n if (!figure.thumbnailUpdatedAt) {\n return undefined\n }\n\n const { contextPath } = getPathInfo(location.pathname)\n const stamp = new Date(figure.thumbnailUpdatedAt).getTime()\n\n return `${contextPath}/figure-thumbnail/${figure.id}?v=${stamp}`\n}\n\n/**\n * 카드의 그림 칸.\n *\n * ## 왜 컴포넌트인가\n *\n * 카탈로그는 `ox-grist` 의 CARD 모드로 그린다. 격자에게 그림 칸은 컬럼 하나이고, 그 칸을 그리는\n * 것은 컬럼의 `record.renderer` 다. 그림 위에 얹히는 것들(초안 표시, 삭제)은 **그림 칸 안에**\n * 있어야 하므로 renderer 가 그려야 하는데, renderer 의 결과는 `ox-card-field` 의 shadow root\n * 안으로 들어간다 — 화면의 `static styles` 가 닿지 않고, 인라인 style 로는 `:hover` 를 쓸 수 없다.\n *\n * 그래서 그림 칸만 컴포넌트로 둔다. 자기 스타일을 갖고 있으니 hover 도 테마도 정상으로 돈다.\n * 격자로 옮기기 전 손으로 만들었던 카드의 위쪽 절반이 그대로 이 자리다.\n *\n * ## 삭제가 왜 여기 있나\n *\n * 격자의 gutter 줄에 두면 카드 아래에 단추 줄이 하나 생긴다. 감춰도 자리가 남고, 자리를 없애면\n * 눌릴 곳이 사라진다. 되돌릴 수 없는 조작은 **고르는 눈길에 걸리지 않아야** 하므로 그림 오른쪽\n * 위, 손이 갔을 때만 나오는 자리에 둔다.\n */\n@customElement('figure-thumb')\nexport class FigureThumb extends localize(i18next)(LitElement) {\n static styles = css`\n :host {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n img {\n display: block;\n width: 100%;\n height: 100%;\n /* 형상을 자르지 않는다 — 긴 것(컨베이어)과 높은 것(시그널타워)이 한 줄에 선다 */\n object-fit: contain;\n }\n\n /* 아직 그림이 없는 것을 빈 사각형으로 두지 않는다 — 왜 없는지 말한다 */\n div[empty] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--spacing-tiny, 4px);\n width: 100%;\n height: 100%;\n font: var(--label-font, inherit);\n font-size: 0.7rem;\n color: var(--md-sys-color-outline);\n }\n div[empty] md-icon {\n --md-icon-size: 26px;\n color: var(--md-sys-color-outline-variant);\n }\n\n /*\n 초안 표시.\n\n 발행된 것에는 아무 말도 하지 않는다 — 카탈로그에 있는 것은 대개 발행된 것이라, 카드마다\n 「발행됨」이 붙으면 아무것도 가르지 않고 자리만 먹는다. 초안만 말하면 이 표시가 있는 카드가\n 곧 「아직 쓰면 안 되는 것」이 된다.\n */\n span[draft] {\n position: absolute;\n top: 6px;\n left: 6px;\n padding: 1px 7px;\n font: var(--label-font, inherit);\n font-size: 0.68rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-secondary-container);\n background-color: var(--md-sys-color-secondary-container);\n border-radius: 4px;\n }\n\n /*\n 태그 — **그림 위에 얹는다.**\n\n 카드 본문에 한 줄로 두었더니 태그가 있는 카드만 그만큼 높아져 격자가 들쭉날쭉해졌다.\n 태그는 도형마다 개수가 다르므로 본문에 두는 한 이 문제가 남는다. 그림 위는 이미 자리가\n 잡혀 있고(초안 표시·삭제), 얹어도 카드 높이가 변하지 않는다.\n\n 아래쪽에 둔다 — 위는 초안 표시와 삭제 단추가 쓴다.\n\n **투명한 알약이다.** 면을 꽉 채우면 그림 위에 흰 띠가 생겨 형상을 가린다 — 카탈로그에서\n 먼저 보아야 하는 것은 형상이다. 그래서 바탕은 거의 비우고, 읽히는 것은 뒤를 흐리는 것과\n 실낱 테두리가 맡는다. 흐림이 안 되는 환경에서도 테두리가 남아 글자가 형상에 묻히지 않는다.\n */\n div[tags] {\n position: absolute;\n right: 6px;\n bottom: 6px;\n left: 6px;\n display: flex;\n flex-wrap: wrap-reverse;\n justify-content: flex-end;\n gap: 3px;\n /* 태그가 많아도 그림을 덮지 않는다 — 넘치는 것은 위로 잘린다 */\n max-height: 44px;\n overflow: hidden;\n /* 알약 사이의 빈 자리는 그림이 받는다 — 거기서 누르면 저작면이 열린다 */\n pointer-events: none;\n }\n /*\n **알약은 누를 수 있다.** 누르면 그 태그로 목록을 거른다.\n\n 태그를 눈에 보이게 한 다음의 물음은 「그래서 이걸로 뭘 하나」다. 같은 태그가 붙은 것을\n 보려고 태그를 눈으로 읽어 검색창에 옮겨 적게 두면, 보여 준 뜻이 절반만 산다.\n\n 카드 자체가 저작면을 여는 자리이므로 알약에서 그 클릭을 멈춘다 — 안 멈추면 태그를 누른\n 사람이 편집기로 끌려간다.\n */\n div[tags] button {\n pointer-events: auto;\n cursor: pointer;\n padding: 0 7px;\n font: var(--label-font, inherit);\n font-size: 0.66rem;\n line-height: 1.7;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background-color: color-mix(in srgb, var(--md-sys-color-surface, #fff) 34%, transparent);\n border: 1px solid color-mix(in srgb, var(--md-sys-color-outline, #888) 42%, transparent);\n backdrop-filter: blur(4px);\n border-radius: 999em;\n white-space: nowrap;\n }\n div[tags] button:hover,\n div[tags] button:focus-visible {\n color: var(--md-sys-color-on-surface);\n background-color: color-mix(in srgb, var(--md-sys-color-surface, #fff) 72%, transparent);\n border-color: var(--md-sys-color-primary);\n outline: none;\n }\n\n button[remove] {\n position: absolute;\n top: 5px;\n right: 5px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 3px;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n /* 그림이 밝을 수도 어두울 수도 있다 — 옅은 면을 깔아 아이콘이 어디서나 읽히게 한다 */\n background-color: color-mix(in srgb, var(--md-sys-color-surface, #fff) 82%, transparent);\n border: none;\n border-radius: 4px;\n cursor: pointer;\n opacity: 0;\n transition: opacity 0.12s ease-out;\n }\n button[remove] md-icon {\n --md-icon-size: 18px;\n }\n :host(:hover) button[remove],\n button[remove]:focus-visible {\n opacity: 1;\n }\n button[remove]:hover {\n color: var(--md-sys-color-on-error, #fff);\n background-color: var(--md-sys-color-error);\n }\n `\n\n @property({ type: Object }) figure!: Figure\n\n render() {\n const figure = this.figure\n if (!figure) {\n return html``\n }\n\n const src = thumbnailSrc(figure)\n\n return html`\n ${src\n ? html`<img src=${src} alt=${figure.name || ''} loading=\"lazy\" />`\n : html`\n <div empty>\n <md-icon>deployed_code</md-icon>\n ${i18next.t('figure.text.thumbnail-not-made-yet')}\n </div>\n `}\n ${figure.state === 'released' ? '' : html`<span draft>${i18next.t('figure.label.draft')}</span>`}\n ${figure.tags?.length\n ? html`<div tags>\n ${figure.tags.map(\n tag => html`\n <button\n title=${i18next.t('figure.text.find-by-this-tag')}\n @click=${(e: Event) => {\n e.stopPropagation()\n this.dispatchEvent(\n new CustomEvent('search-tag', { detail: { tag }, bubbles: true, composed: true })\n )\n }}\n >\n ${tag}\n </button>\n `\n )}\n </div>`\n : ''}\n\n <button\n remove\n title=${i18next.t('figure.button.delete-figure')}\n @click=${(e: Event) => {\n /* 카드 클릭(=저작면 열기)까지 올라가지 않게 여기서 멈춘다 */\n e.stopPropagation()\n this.dispatchEvent(new CustomEvent('remove'))\n }}\n >\n <md-icon>delete</md-icon>\n </button>\n `\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/figure-ui",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.7",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -37,9 +37,8 @@
|
|
|
37
37
|
"@operato/shell": "^10.0.0",
|
|
38
38
|
"@operato/styles": "^10.0.0",
|
|
39
39
|
"@operato/utils": "^10.0.0",
|
|
40
|
-
"@things-factory/
|
|
41
|
-
"@things-factory/figure-service": "^10.1.6",
|
|
40
|
+
"@things-factory/figure-service": "^10.1.7",
|
|
42
41
|
"three": "^0.185.1"
|
|
43
42
|
},
|
|
44
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "9a036f83f5751f9e4af9180e9a0018f716a4ba27"
|
|
45
44
|
}
|
|
@@ -28,6 +28,8 @@ import { readFileSync, readdirSync } from 'node:fs'
|
|
|
28
28
|
import { fileURLToPath } from 'node:url'
|
|
29
29
|
import { join } from 'node:path'
|
|
30
30
|
|
|
31
|
+
import { CHANNEL_PATHS, CLIP_DRIVES, INTERPOLATIONS } from '@hatiolab/figure-model'
|
|
32
|
+
|
|
31
33
|
const HERE = fileURLToPath(new URL('.', import.meta.url))
|
|
32
34
|
const TRANSLATIONS = join(HERE, '../translations')
|
|
33
35
|
const CLIENT = join(HERE, '../client')
|
|
@@ -120,6 +122,46 @@ test('★ 통이 갈린 짝 — 사다리 이름과 설명', () => {
|
|
|
120
122
|
assert.ok(source.includes('figure.text.rung-${which}-note'), '사다리 설명을 text 통에서 찾지 않는다')
|
|
121
123
|
})
|
|
122
124
|
|
|
125
|
+
/*
|
|
126
|
+
애니메이션 탭도 열거 값을 키에 이어 붙인다. 사다리와 **같은 모양의 함정**이라 같은 못을 박는다.
|
|
127
|
+
|
|
128
|
+
갈린 통이 둘이다 — 구동은 이름(`label`)과 설명(`text.…-note`), 경로는 이름(`label`)과
|
|
129
|
+
단위 설명(`text.…-unit`). 한쪽만 넣으면 화면에 키가 그대로 뜨고, 넓은 앞머리가 위의
|
|
130
|
+
미사용 검사를 덮어 통과시킨다.
|
|
131
|
+
|
|
132
|
+
열거 값을 여기 다시 적지 않고 형식에서 읽는다. 적어 두면 형식에 값을 하나 더할 때
|
|
133
|
+
검사가 조용히 통과한다.
|
|
134
|
+
*/
|
|
135
|
+
test('★ 열거 값마다 짝이 다 있다 — 구동 · 경로 · 보간', () => {
|
|
136
|
+
const missing: string[] = []
|
|
137
|
+
const want = (key: string) => {
|
|
138
|
+
if (!(key in KO)) missing.push(key)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (const drive of CLIP_DRIVES) {
|
|
142
|
+
want(`figure.label.drive-${drive}`)
|
|
143
|
+
want(`figure.text.drive-${drive}-note`)
|
|
144
|
+
}
|
|
145
|
+
for (const path of CHANNEL_PATHS) {
|
|
146
|
+
want(`figure.label.path-${path}`)
|
|
147
|
+
want(`figure.text.path-${path}-unit`)
|
|
148
|
+
}
|
|
149
|
+
for (const one of INTERPOLATIONS) {
|
|
150
|
+
want(`figure.label.interpolation-${one}`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
assert.deepEqual(missing, [], `이어 붙여 부르는 키가 번역에 없다:\n ${missing.join('\n ')}`)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test('★ 애니메이션 탭이 그 통에서 찾는다 — 이름은 label, 설명은 text', () => {
|
|
157
|
+
const source = readFileSync(join(CLIENT, 'modeller/figure-animations.ts'), 'utf-8')
|
|
158
|
+
|
|
159
|
+
assert.ok(source.includes("'figure.label.drive-' + drive"), '구동 이름을 label 통에서 찾지 않는다')
|
|
160
|
+
assert.ok(source.includes("'figure.text.drive-'"), '구동 설명을 text 통에서 찾지 않는다')
|
|
161
|
+
assert.ok(source.includes("'figure.label.path-' + path"), '경로 이름을 label 통에서 찾지 않는다')
|
|
162
|
+
assert.ok(source.includes("'figure.text.path-' + channel.path + '-unit'"), '단위 설명을 text 통에서 찾지 않는다')
|
|
163
|
+
})
|
|
164
|
+
|
|
123
165
|
test('★ 이 가드가 실제로 읽고 있다 — 0이면 검사가 죽은 것이다', () => {
|
|
124
166
|
assert.ok(Object.keys(KO).length > 250, `읽은 키가 ${Object.keys(KO).length}개다`)
|
|
125
167
|
assert.ok(FILES.length > 10, `읽은 화면이 ${FILES.length}개다`)
|
package/translations/en.json
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
+
"figure.button.add-channel": "add channel",
|
|
3
|
+
"figure.button.add-clip": "add clip",
|
|
4
|
+
"figure.button.add-key": "add key",
|
|
2
5
|
"figure.button.ask": "ask",
|
|
3
6
|
"figure.button.attach-a-picture": "image",
|
|
4
7
|
"figure.button.center-x": "centre X",
|
|
@@ -6,14 +9,16 @@
|
|
|
6
9
|
"figure.button.close": "close",
|
|
7
10
|
"figure.button.delete-figure": "delete",
|
|
8
11
|
"figure.button.discard": "discard",
|
|
9
|
-
"figure.button.dock": "chat",
|
|
10
12
|
"figure.button.duplicate-part": "duplicate",
|
|
11
13
|
"figure.button.mirror-x": "mirror X",
|
|
12
14
|
"figure.button.mirror-z": "mirror Z",
|
|
13
15
|
"figure.button.move-part-down": "move down",
|
|
14
16
|
"figure.button.move-part-up": "move up",
|
|
15
17
|
"figure.button.new-figure": "new figure",
|
|
18
|
+
"figure.button.pivot-clear": "part centre",
|
|
19
|
+
"figure.button.pivot-set": "set",
|
|
16
20
|
"figure.button.release-figure": "release",
|
|
21
|
+
"figure.button.remove": "remove",
|
|
17
22
|
"figure.button.remove-part": "delete",
|
|
18
23
|
"figure.button.reset-view": "reset",
|
|
19
24
|
"figure.button.revert": "revert",
|
|
@@ -39,6 +44,7 @@
|
|
|
39
44
|
"figure.label.anchor-min-z": "stays on the back face",
|
|
40
45
|
"figure.label.anchor-scale": "grows with the box",
|
|
41
46
|
"figure.label.anchor-span": "touches both faces, fills between",
|
|
47
|
+
"figure.label.animation": "animation",
|
|
42
48
|
"figure.label.axis-consistency": "visual consistency",
|
|
43
49
|
"figure.label.axis-consistency-short": "consistency",
|
|
44
50
|
"figure.label.axis-depth": "depth",
|
|
@@ -55,7 +61,7 @@
|
|
|
55
61
|
"figure.label.axis-scale": "scale",
|
|
56
62
|
"figure.label.axis-scale-short": "scale",
|
|
57
63
|
"figure.label.axis-width": "width",
|
|
58
|
-
"figure.label.budget-animations": "
|
|
64
|
+
"figure.label.budget-animations": "moving parts",
|
|
59
65
|
"figure.label.budget-groups": "material groups",
|
|
60
66
|
"figure.label.budget-parts": "part count",
|
|
61
67
|
"figure.label.budget-surface": "surfaces outside the palette",
|
|
@@ -63,6 +69,12 @@
|
|
|
63
69
|
"figure.label.budgets": "budgets",
|
|
64
70
|
"figure.label.by-axis": "maturity by axis",
|
|
65
71
|
"figure.label.cast-shadow": "cast shadow",
|
|
72
|
+
"figure.label.category": "category",
|
|
73
|
+
"figure.label.channel-path": "moves",
|
|
74
|
+
"figure.label.channel-pivot": "pivot",
|
|
75
|
+
"figure.label.channel-target": "part",
|
|
76
|
+
"figure.label.channels": "channels",
|
|
77
|
+
"figure.label.clip-name": "clip name",
|
|
66
78
|
"figure.label.coordinate-ground": "coordinate ground",
|
|
67
79
|
"figure.label.corner-round": "corner round",
|
|
68
80
|
"figure.label.detail-L": "detailed",
|
|
@@ -74,6 +86,8 @@
|
|
|
74
86
|
"figure.label.draft": "draft",
|
|
75
87
|
"figure.label.draw-calls-at-100": "draw calls for 100",
|
|
76
88
|
"figure.label.draw-calls-at-10k": "draw calls at 10k instances",
|
|
89
|
+
"figure.label.drive-hold": "position",
|
|
90
|
+
"figure.label.drive-loop": "speed",
|
|
77
91
|
"figure.label.emissive": "emissive",
|
|
78
92
|
"figure.label.emissive-color": "lit color",
|
|
79
93
|
"figure.label.environment": "environment",
|
|
@@ -98,7 +112,10 @@
|
|
|
98
112
|
"figure.label.hollow-floor": "floor thickness",
|
|
99
113
|
"figure.label.hollow-wall": "wall thickness",
|
|
100
114
|
"figure.label.intensity": "intensity",
|
|
115
|
+
"figure.label.interpolation-linear": "smooth",
|
|
116
|
+
"figure.label.interpolation-step": "stepped",
|
|
101
117
|
"figure.label.keep-round": "section",
|
|
118
|
+
"figure.label.key-at": "at (s)",
|
|
102
119
|
"figure.label.key-light": "key light",
|
|
103
120
|
"figure.label.maturity": "maturity",
|
|
104
121
|
"figure.label.mode-edit": "edit",
|
|
@@ -114,6 +131,9 @@
|
|
|
114
131
|
"figure.label.part-position": "position (centre)",
|
|
115
132
|
"figure.label.part-size": "size",
|
|
116
133
|
"figure.label.parts": "parts",
|
|
134
|
+
"figure.label.path-rotation": "rotation",
|
|
135
|
+
"figure.label.path-scale": "scale",
|
|
136
|
+
"figure.label.path-translation": "offset",
|
|
117
137
|
"figure.label.placement": "position and size",
|
|
118
138
|
"figure.label.placement-limit": "placement limit",
|
|
119
139
|
"figure.label.preset": "preset",
|
|
@@ -164,6 +184,7 @@
|
|
|
164
184
|
"figure.label.status-colors": "lit colors (lamps · indicators)",
|
|
165
185
|
"figure.label.tab-capability": "capability",
|
|
166
186
|
"figure.label.tab-environment": "environment",
|
|
187
|
+
"figure.label.tags": "tags",
|
|
167
188
|
"figure.label.tools": "align",
|
|
168
189
|
"figure.label.transparent": "transparent",
|
|
169
190
|
"figure.label.triangles": "triangles",
|
|
@@ -178,6 +199,12 @@
|
|
|
178
199
|
"figure.text.already-released-edit-to-release-again": "already released. edit and save to go back to draft, then release again.",
|
|
179
200
|
"figure.text.anchor-explained": "where this part stays as the box grows. Two touching parts on different faces drift apart as it grows.",
|
|
180
201
|
"figure.text.anchors-are-not-drawn": "a slot is where things are placed, a port is where things enter and leave. neither is drawn on a board; the editor shows their positions",
|
|
202
|
+
"figure.text.animated-parts-cost": "A moving part is drawn on its own — one more draw call each, and a matrix update every frame.",
|
|
203
|
+
"figure.text.animated-parts-over": "Over the limit. Nothing is blocked, but many of these on one board will cost frames.",
|
|
204
|
+
"figure.text.animation-empty-examples": "A turning roller · a sliding door leaf · a rising lift · a blinking beacon.",
|
|
205
|
+
"figure.text.animation-empty-what-it-is": "This figure does not move yet. A clip is one set of motion across several parts — a door opening is the two leaves sliding apart, not two separate things.",
|
|
206
|
+
"figure.text.animation-is-driven-by-a-fact": "One clip moves several parts together. On a board a fact sets its value — “this door is 40% open”.",
|
|
207
|
+
"figure.text.animation-needs-a-named-part": "Add a part first. A channel names the part it moves.",
|
|
181
208
|
"figure.text.ask-create-example": "e.g. a three-tier conveyor sorter",
|
|
182
209
|
"figure.text.ask-revise-example": "e.g. bring it down to two material groups",
|
|
183
210
|
"figure.text.ask-to-create": "create",
|
|
@@ -192,6 +219,8 @@
|
|
|
192
219
|
"figure.text.canvas-appears-once-a-part-exists": "the editing area where you place and move parts. pick one from shapes on the left to start.",
|
|
193
220
|
"figure.text.capability-hand-written": "these capabilities were set by hand and match no rung. the names below are what gets stored",
|
|
194
221
|
"figure.text.capacity-from-repeat": "capacity comes from the repeat setting and the instance size",
|
|
222
|
+
"figure.text.category-explained": "how the palette groups figures. one per figure — a figure in two groups meets the author twice.",
|
|
223
|
+
"figure.text.category-placeholder": "e.g. conveyor",
|
|
195
224
|
"figure.text.center-on-x": "move to the centre of the base box on X.",
|
|
196
225
|
"figure.text.center-on-z": "move to the centre of the base box on Z.",
|
|
197
226
|
"figure.text.change-added": "a new part",
|
|
@@ -201,6 +230,11 @@
|
|
|
201
230
|
"figure.text.change-detail": "the detail level",
|
|
202
231
|
"figure.text.change-removed": "a part being removed",
|
|
203
232
|
"figure.text.change-styleKit": "the style kit",
|
|
233
|
+
"figure.text.channel-target-is-gone": "No such part. If you renamed the part, pick it again here.",
|
|
234
|
+
"figure.text.clip-name-is-how-it-is-driven": "An instance drives the clip by this name.",
|
|
235
|
+
"figure.text.clip-name-taken": "Another clip already has this name. Instances drive by name, so two cannot share one.",
|
|
236
|
+
"figure.text.clip-needs-a-channel": "With no channel there is nothing to move.",
|
|
237
|
+
"figure.text.clip-needs-a-name": "Without a name nothing can drive it.",
|
|
204
238
|
"figure.text.collapse-parts-pane": "collapse the part list",
|
|
205
239
|
"figure.text.collapse-properties-pane": "collapse the property pane",
|
|
206
240
|
"figure.text.coordinate-ground-explained": "draws the origin and a grid on the floor",
|
|
@@ -208,14 +242,16 @@
|
|
|
208
242
|
"figure.text.designate-in-properties": "nothing designated yet. select a part and set its role in the properties tab",
|
|
209
243
|
"figure.text.detail-level-explained": "declares how detailed this figure is meant to be. S simple suits signs and posts, M normal suits most equipment, L detailed suits the large equipment a view is built around. the level sets the part limit, and maturity checks whether the figure lives up to it.",
|
|
210
244
|
"figure.text.discard-changes-and-start-new": "there are unsaved changes. discard them and start a new figure?",
|
|
211
|
-
"figure.text.dock-explained": "talk to the authoring assistant. it reads the figure you have open and offers candidates.",
|
|
212
|
-
"figure.text.dock-placeholder": "say what to make or change (Shift+Enter for a new line, Enter to send)",
|
|
213
245
|
"figure.text.does-not-block": "does not block",
|
|
246
|
+
"figure.text.drive-explained": "How the value an instance gives is read.",
|
|
247
|
+
"figure.text.drive-hold-note": "Time is held. The value an instance gives is a position from 0 to 1 — how far open a door is.",
|
|
248
|
+
"figure.text.drive-loop-note": "Time runs. The value an instance gives is a speed — a turning roller works this way.",
|
|
214
249
|
"figure.text.emissive-explained": "the surface lights itself. 0 means it is not a lamp. it does not spill onto its surroundings.",
|
|
215
250
|
"figure.text.environment-explained": "sets what metal surfaces reflect. it does not change the shape.",
|
|
216
251
|
"figure.text.expand-parts-pane": "expand the part list",
|
|
217
252
|
"figure.text.expand-properties-pane": "expand the property pane",
|
|
218
253
|
"figure.text.figure-display-name": "display name",
|
|
254
|
+
"figure.text.find-by-this-tag": "find by this tag",
|
|
219
255
|
"figure.text.fits-n-instances": "up to {n}",
|
|
220
256
|
"figure.text.floor-explained": "overlaps the coordinate ground, so usually off",
|
|
221
257
|
"figure.text.follow-camera-explained": "turn off to fix the shadow direction",
|
|
@@ -228,10 +264,12 @@
|
|
|
228
264
|
"figure.text.hide-this-part": "hide this part while you work. not saved",
|
|
229
265
|
"figure.text.how-many-fit": "how many fit on one board before it slows down",
|
|
230
266
|
"figure.text.instances-unit": "instances",
|
|
267
|
+
"figure.text.interpolation-explained": "How the gap between keys is filled.",
|
|
231
268
|
"figure.text.keep-round-cost": "1 draw call",
|
|
232
269
|
"figure.text.keep-round-merged": "merged into a group",
|
|
233
270
|
"figure.text.keep-round-off": "off",
|
|
234
271
|
"figure.text.keep-round-on": "keep round",
|
|
272
|
+
"figure.text.keys-must-rise": "Each key must come after the one before it.",
|
|
235
273
|
"figure.text.limit-comes-from-level": "the detail level sets this limit.",
|
|
236
274
|
"figure.text.maturity-blocked-no-binding": "the format has no way to connect a state value to a part yet, and the renderer does not read it.",
|
|
237
275
|
"figure.text.maturity-blocked-no-parts": "nothing to measure — there are no parts.",
|
|
@@ -259,12 +297,15 @@
|
|
|
259
297
|
"figure.text.other-segment-count": "enter a value",
|
|
260
298
|
"figure.text.part-limit-reached": "the detail level allows no more parts — raise the level or remove a part.",
|
|
261
299
|
"figure.text.part-limit-reached-short": "level {level} allows {limit} parts. Raise the level or remove a part.",
|
|
300
|
+
"figure.text.path-rotation-unit": "Euler degrees — the same convention as the part's own rotation.",
|
|
301
|
+
"figure.text.path-scale-unit": "A ratio. 1 is full size; 0 makes the part vanish.",
|
|
302
|
+
"figure.text.path-translation-unit": "Offset from where the part sits. 0 leaves it in place.",
|
|
262
303
|
"figure.text.pick-a-shape-above-to-start": "pick a shape above to add the first part.",
|
|
263
304
|
"figure.text.picked-changes-do-not-validate": "the selected changes do not make a valid figure",
|
|
264
305
|
"figure.text.picture-is-for-proportions": "used for proportions and form, not for surface detail.",
|
|
306
|
+
"figure.text.pivot-defaults-to-the-part-centre": "Left unset, the part turns about its own centre. Coordinates are local to the part.",
|
|
265
307
|
"figure.text.preview-blocked-by-errors": "cannot build — the figure breaks the format. the maturity tab has the reason.",
|
|
266
308
|
"figure.text.preview-needs-a-part": "shows how it looks placed on a board. appears once you make a part.",
|
|
267
|
-
"figure.text.proposal-had-no-source": "the candidate carried no source, so it cannot be taken.",
|
|
268
309
|
"figure.text.put-on-floor": "drop it to the floor (Z=0).",
|
|
269
310
|
"figure.text.release-needs-a-save-first": "there are unsaved changes. a release publishes what was saved, so save first.",
|
|
270
311
|
"figure.text.releasing": "releasing…",
|
|
@@ -284,7 +325,7 @@
|
|
|
284
325
|
"figure.text.rung-none-note": "takes nothing. most figures belong here",
|
|
285
326
|
"figure.text.save-the-figure-before-releasing": "this figure has not been saved yet. save it and you can release it.",
|
|
286
327
|
"figure.text.saving-figure": "saving",
|
|
287
|
-
"figure.text.search-figure
|
|
328
|
+
"figure.text.search-figure": "search by name, type, description, category or tag",
|
|
288
329
|
"figure.text.select-a-part-to-edit": "select a part to edit it here.",
|
|
289
330
|
"figure.text.show-all-parts": "show all",
|
|
290
331
|
"figure.text.show-this-part": "show this part again",
|
|
@@ -295,8 +336,11 @@
|
|
|
295
336
|
"figure.text.sizing-stretch-explained": "only one axis stretches — like a frame, a belt or a rail.",
|
|
296
337
|
"figure.text.slots-need-capability": "select \"holds things\" or above before designating a slot",
|
|
297
338
|
"figure.text.snap-explained": "rounds position and size to the grid — the same spacing you see on the canvas.",
|
|
339
|
+
"figure.text.tags-explained": "for the finding a single category cannot do. a figure may carry several — a conveyor that is also outbound. separate them with commas.",
|
|
340
|
+
"figure.text.tags-placeholder": "e.g. outbound, automatic, level 2",
|
|
298
341
|
"figure.text.thumbnail-not-made-yet": "no thumbnail yet",
|
|
299
342
|
"figure.text.triangle-count": "{count} triangles",
|
|
343
|
+
"figure.text.two-keys-at-least": "A channel needs two keys. With one it is not motion, it is a single pose.",
|
|
300
344
|
"figure.text.type-name-already-taken": "the type name {type} is already in use. pick another one — it cannot be changed later.",
|
|
301
345
|
"figure.text.type-name-cannot-change": "the type name is a stored identifier and cannot be changed after creation.",
|
|
302
346
|
"figure.text.unsaved-changes": "unsaved",
|
package/translations/ja.json
CHANGED
|
@@ -194,6 +194,7 @@
|
|
|
194
194
|
"figure.text.expand-parts-pane": "expand the part list",
|
|
195
195
|
"figure.text.expand-properties-pane": "expand the property pane",
|
|
196
196
|
"figure.text.figure-display-name": "display name",
|
|
197
|
+
"figure.text.find-by-this-tag": "find by this tag",
|
|
197
198
|
"figure.text.fits-n-instances": "up to {n}",
|
|
198
199
|
"figure.text.floor-explained": "overlaps the coordinate ground, so usually off",
|
|
199
200
|
"figure.text.follow-camera-explained": "turn off to fix the shadow direction",
|
|
@@ -253,7 +254,7 @@
|
|
|
253
254
|
"figure.text.revert-to-version-confirm": "第{version}版に戻します。作業中の内容がその版のものに変わります。続けますか?",
|
|
254
255
|
"figure.text.save-the-figure-before-releasing": "まだ保存されていません。保存すると公開できます。",
|
|
255
256
|
"figure.text.saving-figure": "saving",
|
|
256
|
-
"figure.text.search-figure
|
|
257
|
+
"figure.text.search-figure": "search by name, type, description, category or tag",
|
|
257
258
|
"figure.text.select-a-part-to-edit": "select a part to edit it here.",
|
|
258
259
|
"figure.text.show-all-parts": "show all",
|
|
259
260
|
"figure.text.show-this-part": "show this part again",
|