@things-factory/figure-ui 10.1.159 → 10.1.161
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 +38 -0
- package/client/index.ts +1 -0
- package/client/modeller/figure-links.ts +21 -6
- package/client/modeller/links.ts +13 -0
- package/client/pages/figure-list-page.ts +5 -72
- package/client/setting-let/figure-namespace-setting-let.ts +85 -0
- package/client/viewparts/figure-thumb.ts +10 -37
- package/dist-client/graphql/index.d.ts +13 -0
- package/dist-client/graphql/index.js +24 -0
- package/dist-client/graphql/index.js.map +1 -1
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +1 -0
- package/dist-client/index.js.map +1 -1
- package/dist-client/modeller/figure-links.js +21 -7
- package/dist-client/modeller/figure-links.js.map +1 -1
- package/dist-client/modeller/links.d.ts +13 -0
- package/dist-client/modeller/links.js +11 -0
- package/dist-client/modeller/links.js.map +1 -1
- package/dist-client/pages/figure-list-page.js +5 -72
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/setting-let/figure-namespace-setting-let.d.ts +18 -0
- package/dist-client/setting-let/figure-namespace-setting-let.js +94 -0
- package/dist-client/setting-let/figure-namespace-setting-let.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/figure-thumb.d.ts +3 -1
- package/dist-client/viewparts/figure-thumb.js +10 -37
- package/dist-client/viewparts/figure-thumb.js.map +1 -1
- package/package.json +5 -4
- package/test/links.test.ts +29 -0
- package/translations/en.json +8 -1
- package/translations/es.json +8 -1
- package/translations/ja.json +8 -1
- package/translations/ko.json +8 -1
- package/translations/ms.json +8 -1
- package/translations/zh.json +8 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import '@material/web/icon/icon.js';
|
|
2
2
|
import { LitElement } from 'lit';
|
|
3
|
+
import '@operato/data-grist/ox-card-thumb.js';
|
|
3
4
|
import '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js';
|
|
4
5
|
import type { Figure } from '../types.js';
|
|
5
6
|
declare const FigureThumb_base: (new (...args: any[]) => LitElement) & typeof LitElement;
|
|
6
7
|
/**
|
|
7
|
-
* 카드의 그림 칸.
|
|
8
|
+
* 카드의 그림 칸. 그림 · 그림 없음 · 초안 표시는 모든 카드 카탈로그의 `ox-card-thumb`(@operato/data-grist)가
|
|
9
|
+
* 그리고, 여기서는 그 위에 이 카탈로그의 것(태그 · 삭제)만 얹는다(ADR-0117 개정 2026-10-13).
|
|
8
10
|
*
|
|
9
11
|
* ## 왜 컴포넌트인가
|
|
10
12
|
*
|
|
@@ -3,6 +3,7 @@ import '@material/web/icon/icon.js';
|
|
|
3
3
|
import { css, html, LitElement } from 'lit';
|
|
4
4
|
import { customElement, property } from 'lit/decorators.js';
|
|
5
5
|
import { i18next, localize } from '@operato/i18n';
|
|
6
|
+
import '@operato/data-grist/ox-card-thumb.js';
|
|
6
7
|
import '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js';
|
|
7
8
|
import { getPathInfo } from '@operato/utils';
|
|
8
9
|
/**
|
|
@@ -24,7 +25,8 @@ function thumbnailSrc(figure) {
|
|
|
24
25
|
return `${contextPath}/figure-thumbnail/${figure.id}?v=${stamp}`;
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
|
-
* 카드의 그림 칸.
|
|
28
|
+
* 카드의 그림 칸. 그림 · 그림 없음 · 초안 표시는 모든 카드 카탈로그의 `ox-card-thumb`(@operato/data-grist)가
|
|
29
|
+
* 그리고, 여기서는 그 위에 이 카탈로그의 것(태그 · 삭제)만 얹는다(ADR-0117 개정 2026-10-13).
|
|
28
30
|
*
|
|
29
31
|
* ## 왜 컴포넌트인가
|
|
30
32
|
*
|
|
@@ -45,39 +47,9 @@ function thumbnailSrc(figure) {
|
|
|
45
47
|
let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
46
48
|
static { this.styles = css `
|
|
47
49
|
:host {
|
|
48
|
-
position: relative;
|
|
49
50
|
display: block;
|
|
50
51
|
width: 100%;
|
|
51
52
|
height: 100%;
|
|
52
|
-
overflow: hidden;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
img {
|
|
56
|
-
display: block;
|
|
57
|
-
width: 100%;
|
|
58
|
-
height: 100%;
|
|
59
|
-
/* 형상을 자르지 않는다 — 긴 것(컨베이어)과 높은 것(시그널타워)이 한 줄에 선다 */
|
|
60
|
-
object-fit: contain;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/*
|
|
64
|
-
초안 표시.
|
|
65
|
-
|
|
66
|
-
발행된 것에는 아무 말도 하지 않는다 — 카탈로그에 있는 것은 대개 발행된 것이라, 카드마다
|
|
67
|
-
「발행됨」이 붙으면 아무것도 가르지 않고 자리만 먹는다. 초안만 말하면 이 표시가 있는 카드가
|
|
68
|
-
곧 「아직 쓰면 안 되는 것」이 된다.
|
|
69
|
-
*/
|
|
70
|
-
span[draft] {
|
|
71
|
-
position: absolute;
|
|
72
|
-
top: 6px;
|
|
73
|
-
left: 6px;
|
|
74
|
-
padding: 1px 7px;
|
|
75
|
-
font: var(--label-font, inherit);
|
|
76
|
-
font-size: 0.68rem;
|
|
77
|
-
line-height: 1.5;
|
|
78
|
-
color: var(--md-sys-color-on-secondary-container);
|
|
79
|
-
background-color: var(--md-sys-color-secondary-container);
|
|
80
|
-
border-radius: 4px;
|
|
81
53
|
}
|
|
82
54
|
|
|
83
55
|
/*
|
|
@@ -175,12 +147,12 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
175
147
|
}
|
|
176
148
|
const src = thumbnailSrc(figure);
|
|
177
149
|
return html `
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
150
|
+
<ox-card-thumb
|
|
151
|
+
src=${src || ''}
|
|
152
|
+
alt=${figure.name || ''}
|
|
153
|
+
badge=${figure.state === 'released' ? '' : i18next.t('figure.label.draft')}
|
|
154
|
+
>
|
|
155
|
+
<figure-thumb-mark slot="empty" .label=${i18next.t('figure.text.thumbnail-not-made-yet')}></figure-thumb-mark>
|
|
184
156
|
${figure.tags?.length
|
|
185
157
|
? html `<div tags>
|
|
186
158
|
${figure.tags.map(tag => html `
|
|
@@ -208,6 +180,7 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
|
|
|
208
180
|
>
|
|
209
181
|
<md-icon>delete</md-icon>
|
|
210
182
|
</button>
|
|
183
|
+
</ox-card-thumb>
|
|
211
184
|
`;
|
|
212
185
|
}
|
|
213
186
|
};
|
|
@@ -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,gEAAgE,CAAA;AACvE,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
|
|
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,sCAAsC,CAAA;AAC7C,OAAO,gEAAgE,CAAA;AACvE,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;;;;;;;;;;;;;;;;;;;GAmBG;AAEI,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;aACrD,WAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FlB,AA9FY,CA8FZ;IAID,MAAM;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QAC1B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAA,EAAE,CAAA;QACf,CAAC;QAED,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAA;QAEhC,OAAO,IAAI,CAAA;;cAED,GAAG,IAAI,EAAE;cACT,MAAM,CAAC,IAAI,IAAI,EAAE;gBACf,MAAM,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;;iDAEjC,OAAO,CAAC,CAAC,CAAC,oCAAoC,CAAC;QACxF,MAAM,CAAC,IAAI,EAAE,MAAM;YACnB,CAAC,CAAC,IAAI,CAAA;cACA,MAAM,CAAC,IAAI,CAAC,GAAG,CACf,GAAG,CAAC,EAAE,CAAC,IAAI,CAAA;;0BAEC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;2BACxC,CAAC,CAAQ,EAAE,EAAE;gBACpB,CAAC,CAAC,eAAe,EAAE,CAAA;gBACnB,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAClF,CAAA;YACH,CAAC;;oBAEC,GAAG;;eAER,CACF;iBACI;YACT,CAAC,CAAC,EAAE;;;;gBAII,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;iBACvC,CAAC,CAAQ,EAAE,EAAE;YACpB,sCAAsC;YACtC,CAAC,CAAC,eAAe,EAAE,CAAA;YACnB,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAA;QAC/C,CAAC;;;;;KAKJ,CAAA;IACH,CAAC;;AAlD2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;2CAAgB;AAjGhC,WAAW;IADvB,aAAa,CAAC,cAAc,CAAC;GACjB,WAAW,CAoJvB","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 '@operato/data-grist/ox-card-thumb.js'\nimport '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js'\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 * 카드의 그림 칸. 그림 · 그림 없음 · 초안 표시는 모든 카드 카탈로그의 `ox-card-thumb`(@operato/data-grist)가\n * 그리고, 여기서는 그 위에 이 카탈로그의 것(태그 · 삭제)만 얹는다(ADR-0117 개정 2026-10-13).\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 display: block;\n width: 100%;\n height: 100%;\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 <ox-card-thumb\n src=${src || ''}\n alt=${figure.name || ''}\n badge=${figure.state === 'released' ? '' : i18next.t('figure.label.draft')}\n >\n <figure-thumb-mark slot=\"empty\" .label=${i18next.t('figure.text.thumbnail-not-made-yet')}></figure-thumb-mark>\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 </ox-card-thumb>\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.161",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -41,10 +41,11 @@
|
|
|
41
41
|
"@operato/slot-layout-editor": "^10.29.25",
|
|
42
42
|
"@operato/styles": "^10.0.0",
|
|
43
43
|
"@operato/utils": "^10.0.0",
|
|
44
|
-
"@things-factory/ai-assistant": "^10.1.
|
|
45
|
-
"@things-factory/figure-service": "^10.1.
|
|
44
|
+
"@things-factory/ai-assistant": "^10.1.161",
|
|
45
|
+
"@things-factory/figure-service": "^10.1.161",
|
|
46
46
|
"@things-factory/figure-viewer": "^10.1.155",
|
|
47
|
+
"@things-factory/setting-base": "^10.1.159",
|
|
47
48
|
"three": "^0.185.1"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "d32fe0614ea40572787acd94e307cc18a681ab38"
|
|
50
51
|
}
|
package/test/links.test.ts
CHANGED
|
@@ -645,6 +645,35 @@ test('붙이기 칸은 부품 제 붙임으로 열린다 — 가운데로 붙은
|
|
|
645
645
|
assert.ok(!ownFacesOf(asset, 'roller', true).includes('centre'), 'on the floor, by a face only')
|
|
646
646
|
})
|
|
647
647
|
|
|
648
|
+
test('a worked-out gap is kept by the fastening form: opening it on the part moves nothing (the robot dog, the forklift, 2026-10-13)', async () => {
|
|
649
|
+
const { fastenGapOf } = await import('../client/modeller/links.ts')
|
|
650
|
+
const { compileFigureAsset, robotDogFigure } = await import('@hatiolab/figure-model')
|
|
651
|
+
const posesOf = (asset: FigureAsset) =>
|
|
652
|
+
Object.fromEntries((compileFigureAsset(asset).evaluate().geometry as unknown as Array<{ id: string; pose?: { t: number[] } }>).map(g => [g.id, g.pose?.t.map(v => Math.round(v * 1000) / 1000)]))
|
|
653
|
+
/* What the form sends when nothing in it is changed: the attach it would make of the part's own fastening. */
|
|
654
|
+
const reattach = (asset: FigureAsset, part: string, form: Parameters<typeof fastenGapOf>[0]) =>
|
|
655
|
+
applyFigureAuthoring(asset, { kind: 'attach', part, face: 'bottom', to: { plane: 'mounting-plane' }, ...fastenGapOf(form), twist: 0, replace: true } as never)
|
|
656
|
+
|
|
657
|
+
const dog = robotDogFigure().asset
|
|
658
|
+
const body = fasteningOf(revisionOf(dog), 'body')!
|
|
659
|
+
assert.deepEqual(body.gapKept, dog.joints!.find(j => j.id === 'body')!.origin!.gap, 'the sum of the leg lengths, as written')
|
|
660
|
+
assert.deepEqual(posesOf(reattach(dog, 'body', body)), posesOf(dog), 'the body and everything hung from it stay where they stand')
|
|
661
|
+
/* The defect it replaces: read as the number 0, the body dropped to the floor and its legs below it. */
|
|
662
|
+
assert.notDeepEqual(posesOf(reattach(dog, 'body', { gap: body.gap })), posesOf(dog))
|
|
663
|
+
|
|
664
|
+
/* A gap that is a share of the instance height, as the forklift's is. */
|
|
665
|
+
let lift = addFigureBox(createFigureAsset('Lift'), { id: 'body', size: { x: 1000, y: 800, z: 600 }, position: { x: 0, y: 500, z: 0 } })
|
|
666
|
+
lift.document.model.inputs.push({ id: 'size.y', unit: 'mm', min: 100, max: 20000, role: 'design' } as never)
|
|
667
|
+
lift.designInputs['size.y'] = 1800
|
|
668
|
+
lift = applyFigureAuthoring(lift, { kind: 'attach', part: 'body', face: 'bottom', to: { plane: 'mounting-plane' }, gap: { from: { of: 'instance', axis: 'y' }, times: 0.05 } } as never)
|
|
669
|
+
const kept = fasteningOf(revisionOf(lift), 'body')!
|
|
670
|
+
assert.deepEqual(kept.gapKept, { from: { of: 'instance', axis: 'y' }, times: 0.05 })
|
|
671
|
+
assert.deepEqual(posesOf(reattach(lift, 'body', kept)), posesOf(lift))
|
|
672
|
+
/* A number typed in the form is a number again. */
|
|
673
|
+
assert.deepEqual(fastenGapOf({ gap: 20 }), { gap: 20 })
|
|
674
|
+
assert.deepEqual(fastenGapOf({ gap: 0 }), {})
|
|
675
|
+
})
|
|
676
|
+
|
|
648
677
|
test('a follower of several leaders and a stroke that is a sum of lengths are written back unchanged by the motion form (ADR-0107)', async () => {
|
|
649
678
|
const { motionFormOf, motionJoint, motionOf } = await import('../client/modeller/links.ts')
|
|
650
679
|
const { palletizerRobotFigure } = await import('@hatiolab/figure-model')
|
package/translations/en.json
CHANGED
|
@@ -555,6 +555,7 @@
|
|
|
555
555
|
"figure.label.fasten-detail": "Details",
|
|
556
556
|
"figure.label.facing": "How faces meet",
|
|
557
557
|
"figure.text.fasten-summary-gap": "gap {mm} mm",
|
|
558
|
+
"figure.text.fasten-summary-gap-worked-out": "gap worked out by a formula",
|
|
558
559
|
"figure.text.fasten-summary-twist": "twist {degrees}°",
|
|
559
560
|
"figure.label.gap": "gap (mm)",
|
|
560
561
|
"figure.label.align": "within the face",
|
|
@@ -829,6 +830,7 @@
|
|
|
829
830
|
"figure.text.resize-move": "(moves {mm} mm on {axis})",
|
|
830
831
|
"figure.text.held-by-rule": "A rule places this part.",
|
|
831
832
|
"figure.text.held-on-plane": "{face} kept on the {place} (gap {gap} mm)",
|
|
833
|
+
"figure.text.held-on-plane-worked-out": "{face} kept on the {place} (gap worked out by a formula)",
|
|
832
834
|
"figure.text.held-on-part": "Attached by its {face} to the {otherFace} of {other}",
|
|
833
835
|
"figure.button.open-fastening": "Open attachment settings",
|
|
834
836
|
"figure.button.detach-held": "Detach",
|
|
@@ -1108,5 +1110,10 @@
|
|
|
1108
1110
|
"figure.text.motion-setting-speed-out-of-range": "The speed of {group} is 0 to 3.",
|
|
1109
1111
|
"figure.text.motion-setting-group-selection-required": "Choose which clip of {group} plays.",
|
|
1110
1112
|
"figure.text.motion-key-outside-range": "The key {value} of {input} is outside {min} to {max}; a grouped clip keys inside the range.",
|
|
1111
|
-
"figure.label.tab-short-data": "Data"
|
|
1113
|
+
"figure.label.tab-short-data": "Data",
|
|
1114
|
+
"figure.title.namespace-setting": "Figure namespace",
|
|
1115
|
+
"figure.label.namespace": "Namespace",
|
|
1116
|
+
"figure.text.namespace-fixed": "Figures have been made under this namespace, so it cannot change.",
|
|
1117
|
+
"figure.text.namespace-empty-takes-subdomain": "Left empty, the subdomain {subdomain} is used. It cannot change once a figure is made.",
|
|
1118
|
+
"figure.text.namespace-subdomain-unusable": "The subdomain {subdomain} cannot be a namespace. Enter one, for example hatiolab.com."
|
|
1112
1119
|
}
|
package/translations/es.json
CHANGED
|
@@ -555,6 +555,7 @@
|
|
|
555
555
|
"figure.label.fasten-detail": "Detalles",
|
|
556
556
|
"figure.label.facing": "Cómo se unen las caras",
|
|
557
557
|
"figure.text.fasten-summary-gap": "separación {mm} mm",
|
|
558
|
+
"figure.text.fasten-summary-gap-worked-out": "separación calculada por una fórmula",
|
|
558
559
|
"figure.text.fasten-summary-twist": "torsión {degrees}°",
|
|
559
560
|
"figure.label.gap": "separación (mm)",
|
|
560
561
|
"figure.label.align": "dentro de la cara",
|
|
@@ -829,6 +830,7 @@
|
|
|
829
830
|
"figure.text.resize-move": "(se mueve {mm} mm en {axis})",
|
|
830
831
|
"figure.text.held-by-rule": "Una regla coloca esta pieza.",
|
|
831
832
|
"figure.text.held-on-plane": "{face} mantenida en {place} (separación {gap} mm)",
|
|
833
|
+
"figure.text.held-on-plane-worked-out": "{face} mantenida en {place} (separación calculada por una fórmula)",
|
|
832
834
|
"figure.text.held-on-part": "Unida por su {face} a {otherFace} de {other}",
|
|
833
835
|
"figure.button.open-fastening": "Abrir la configuración de unión",
|
|
834
836
|
"figure.button.detach-held": "Separar",
|
|
@@ -1108,5 +1110,10 @@
|
|
|
1108
1110
|
"figure.text.motion-setting-speed-out-of-range": "La velocidad de {group} es de 0 a 3.",
|
|
1109
1111
|
"figure.text.motion-setting-group-selection-required": "Elija qué clip de {group} se reproduce.",
|
|
1110
1112
|
"figure.text.motion-key-outside-range": "La clave {value} de {input} está fuera de {min} a {max}; un clip de grupo usa claves dentro del rango.",
|
|
1111
|
-
"figure.label.tab-short-data": "Datos"
|
|
1113
|
+
"figure.label.tab-short-data": "Datos",
|
|
1114
|
+
"figure.title.namespace-setting": "Namespace de Figure",
|
|
1115
|
+
"figure.label.namespace": "Namespace",
|
|
1116
|
+
"figure.text.namespace-fixed": "Ya hay figuras creadas con este namespace, por lo que no puede cambiar.",
|
|
1117
|
+
"figure.text.namespace-empty-takes-subdomain": "Si se deja vacío, se usa el subdominio {subdomain}. No puede cambiar una vez creada una figura.",
|
|
1118
|
+
"figure.text.namespace-subdomain-unusable": "El subdominio {subdomain} no puede ser un namespace. Escriba uno, por ejemplo hatiolab.com."
|
|
1112
1119
|
}
|
package/translations/ja.json
CHANGED
|
@@ -555,6 +555,7 @@
|
|
|
555
555
|
"figure.label.fasten-detail": "詳細",
|
|
556
556
|
"figure.label.facing": "接し方",
|
|
557
557
|
"figure.text.fasten-summary-gap": "間隔 {mm} mm",
|
|
558
|
+
"figure.text.fasten-summary-gap-worked-out": "間隔は式で決まる",
|
|
558
559
|
"figure.text.fasten-summary-twist": "ひねり {degrees}°",
|
|
559
560
|
"figure.label.gap": "gap (mm)",
|
|
560
561
|
"figure.label.align": "within the face",
|
|
@@ -829,6 +830,7 @@
|
|
|
829
830
|
"figure.text.resize-move": "({axis} {mm} mm 移動)",
|
|
830
831
|
"figure.text.held-by-rule": "規則がこの部品の位置を決めます。",
|
|
831
832
|
"figure.text.held-on-plane": "{face}を{place}に保持中 (間隔 {gap} mm)",
|
|
833
|
+
"figure.text.held-on-plane-worked-out": "{face}を{place}に保持中 (間隔は式で決まる)",
|
|
832
834
|
"figure.text.held-on-part": "{other}の{otherFace}に{face}で取り付け",
|
|
833
835
|
"figure.button.open-fastening": "取り付け設定を開く",
|
|
834
836
|
"figure.button.detach-held": "取り付けを外す",
|
|
@@ -1108,5 +1110,10 @@
|
|
|
1108
1110
|
"figure.text.motion-setting-speed-out-of-range": "{group} の速度は 0 ~ 3 です。",
|
|
1109
1111
|
"figure.text.motion-setting-group-selection-required": "{group} で再生するクリップを選びます。",
|
|
1110
1112
|
"figure.text.motion-key-outside-range": "{input} のキー {value} が範囲 {min} ~ {max} の外です。グループのクリップは範囲内のキーだけを使います。",
|
|
1111
|
-
"figure.label.tab-short-data": "データ"
|
|
1113
|
+
"figure.label.tab-short-data": "データ",
|
|
1114
|
+
"figure.title.namespace-setting": "Figure 名前空間",
|
|
1115
|
+
"figure.label.namespace": "名前空間",
|
|
1116
|
+
"figure.text.namespace-fixed": "この名前空間で Figure を作成済みのため変更できません。",
|
|
1117
|
+
"figure.text.namespace-empty-takes-subdomain": "空欄ならサブドメイン {subdomain} を使います。Figure を作成すると変更できません。",
|
|
1118
|
+
"figure.text.namespace-subdomain-unusable": "サブドメイン {subdomain} は名前空間に使えません。例: hatiolab.com"
|
|
1112
1119
|
}
|
package/translations/ko.json
CHANGED
|
@@ -555,6 +555,7 @@
|
|
|
555
555
|
"figure.label.fasten-detail": "세부",
|
|
556
556
|
"figure.label.facing": "닿는 방식",
|
|
557
557
|
"figure.text.fasten-summary-gap": "간격 {mm} mm",
|
|
558
|
+
"figure.text.fasten-summary-gap-worked-out": "간격은 식으로 정해짐",
|
|
558
559
|
"figure.text.fasten-summary-twist": "비틀기 {degrees}°",
|
|
559
560
|
"figure.label.gap": "간격 (mm)",
|
|
560
561
|
"figure.label.align": "면 안 위치",
|
|
@@ -829,6 +830,7 @@
|
|
|
829
830
|
"figure.text.resize-move": "({axis} {mm} mm 이동)",
|
|
830
831
|
"figure.text.held-by-rule": "규칙이 이 부품의 자리를 정합니다.",
|
|
831
832
|
"figure.text.held-on-plane": "{face}을 {place}에 유지 중 (간격 {gap} mm)",
|
|
833
|
+
"figure.text.held-on-plane-worked-out": "{face}을 {place}에 유지 중 (간격은 식으로 정해짐)",
|
|
832
834
|
"figure.text.held-on-part": "{other}의 {otherFace}에 {face}으로 붙어 있음",
|
|
833
835
|
"figure.button.open-fastening": "붙임 설정 열기",
|
|
834
836
|
"figure.button.detach-held": "붙임 풀기",
|
|
@@ -1108,5 +1110,10 @@
|
|
|
1108
1110
|
"figure.text.motion-setting-speed-out-of-range": "{group} 그룹의 배속은 0 ~ 3입니다.",
|
|
1109
1111
|
"figure.text.motion-setting-group-selection-required": "{group} 그룹에서 재생할 클립을 고릅니다.",
|
|
1110
1112
|
"figure.text.motion-key-outside-range": "{input} 조작값의 키 {value} 값이 범위 {min} ~ {max} 밖입니다. 그룹의 클립은 범위 안의 키만 씁니다.",
|
|
1111
|
-
"figure.label.tab-short-data": "데이터"
|
|
1113
|
+
"figure.label.tab-short-data": "데이터",
|
|
1114
|
+
"figure.title.namespace-setting": "Figure 이름공간",
|
|
1115
|
+
"figure.label.namespace": "이름공간",
|
|
1116
|
+
"figure.text.namespace-fixed": "이 이름공간으로 Figure를 만들어서 바꿀 수 없습니다.",
|
|
1117
|
+
"figure.text.namespace-empty-takes-subdomain": "비워 두면 서브도메인 {subdomain} 값을 씁니다. Figure를 만들고 나면 바꿀 수 없습니다.",
|
|
1118
|
+
"figure.text.namespace-subdomain-unusable": "서브도메인 {subdomain} 값은 이름공간으로 쓸 수 없습니다. 예: hatiolab.com"
|
|
1112
1119
|
}
|
package/translations/ms.json
CHANGED
|
@@ -555,6 +555,7 @@
|
|
|
555
555
|
"figure.label.fasten-detail": "Butiran",
|
|
556
556
|
"figure.label.facing": "Cara muka bertemu",
|
|
557
557
|
"figure.text.fasten-summary-gap": "jarak {mm} mm",
|
|
558
|
+
"figure.text.fasten-summary-gap-worked-out": "jarak ditentukan oleh formula",
|
|
558
559
|
"figure.text.fasten-summary-twist": "pulasan {degrees}°",
|
|
559
560
|
"figure.label.gap": "gap (mm)",
|
|
560
561
|
"figure.label.align": "within the face",
|
|
@@ -829,6 +830,7 @@
|
|
|
829
830
|
"figure.text.resize-move": "(bergerak {mm} mm pada {axis})",
|
|
830
831
|
"figure.text.held-by-rule": "Satu peraturan meletakkan bahagian ini.",
|
|
831
832
|
"figure.text.held-on-plane": "{face} dikekalkan pada {place} (jarak {gap} mm)",
|
|
833
|
+
"figure.text.held-on-plane-worked-out": "{face} dikekalkan pada {place} (jarak ditentukan oleh formula)",
|
|
832
834
|
"figure.text.held-on-part": "Dilekatkan dengan {face} pada {otherFace} {other}",
|
|
833
835
|
"figure.button.open-fastening": "Buka tetapan lekatan",
|
|
834
836
|
"figure.button.detach-held": "Tanggalkan",
|
|
@@ -1108,5 +1110,10 @@
|
|
|
1108
1110
|
"figure.text.motion-setting-speed-out-of-range": "Kelajuan {group} ialah 0 hingga 3.",
|
|
1109
1111
|
"figure.text.motion-setting-group-selection-required": "Pilih klip {group} yang dimainkan.",
|
|
1110
1112
|
"figure.text.motion-key-outside-range": "Kunci {value} bagi {input} di luar {min} hingga {max}; klip berkumpulan mesti dalam julat.",
|
|
1111
|
-
"figure.label.tab-short-data": "Data"
|
|
1113
|
+
"figure.label.tab-short-data": "Data",
|
|
1114
|
+
"figure.title.namespace-setting": "Namespace Figure",
|
|
1115
|
+
"figure.label.namespace": "Namespace",
|
|
1116
|
+
"figure.text.namespace-fixed": "Figure telah dibuat di bawah namespace ini, jadi ia tidak boleh diubah.",
|
|
1117
|
+
"figure.text.namespace-empty-takes-subdomain": "Jika dibiarkan kosong, subdomain {subdomain} digunakan. Ia tidak boleh diubah selepas figure dibuat.",
|
|
1118
|
+
"figure.text.namespace-subdomain-unusable": "Subdomain {subdomain} tidak boleh menjadi namespace. Masukkan satu, contohnya hatiolab.com."
|
|
1112
1119
|
}
|
package/translations/zh.json
CHANGED
|
@@ -555,6 +555,7 @@
|
|
|
555
555
|
"figure.label.fasten-detail": "详细",
|
|
556
556
|
"figure.label.facing": "接触方式",
|
|
557
557
|
"figure.text.fasten-summary-gap": "间距 {mm} mm",
|
|
558
|
+
"figure.text.fasten-summary-gap-worked-out": "间距由公式决定",
|
|
558
559
|
"figure.text.fasten-summary-twist": "扭转 {degrees}°",
|
|
559
560
|
"figure.label.gap": "gap (mm)",
|
|
560
561
|
"figure.label.align": "within the face",
|
|
@@ -829,6 +830,7 @@
|
|
|
829
830
|
"figure.text.resize-move": "({axis} 方向移动 {mm} mm)",
|
|
830
831
|
"figure.text.held-by-rule": "由规则决定此部件的位置。",
|
|
831
832
|
"figure.text.held-on-plane": "{face}保持在{place}上(间距 {gap} mm)",
|
|
833
|
+
"figure.text.held-on-plane-worked-out": "{face}保持在{place}上(间距由公式决定)",
|
|
832
834
|
"figure.text.held-on-part": "以{face}附着在{other}的{otherFace}上",
|
|
833
835
|
"figure.button.open-fastening": "打开附着设置",
|
|
834
836
|
"figure.button.detach-held": "解除附着",
|
|
@@ -1108,5 +1110,10 @@
|
|
|
1108
1110
|
"figure.text.motion-setting-speed-out-of-range": "{group} 的倍速为 0 ~ 3。",
|
|
1109
1111
|
"figure.text.motion-setting-group-selection-required": "请选择 {group} 播放的片段。",
|
|
1110
1112
|
"figure.text.motion-key-outside-range": "{input} 的关键帧 {value} 超出 {min} ~ {max};组内片段只能在范围内设关键帧。",
|
|
1111
|
-
"figure.label.tab-short-data": "数据"
|
|
1113
|
+
"figure.label.tab-short-data": "数据",
|
|
1114
|
+
"figure.title.namespace-setting": "Figure 命名空间",
|
|
1115
|
+
"figure.label.namespace": "命名空间",
|
|
1116
|
+
"figure.text.namespace-fixed": "已用此命名空间创建 Figure,无法更改。",
|
|
1117
|
+
"figure.text.namespace-empty-takes-subdomain": "留空则使用子域名 {subdomain}。创建 Figure 后无法更改。",
|
|
1118
|
+
"figure.text.namespace-subdomain-unusable": "子域名 {subdomain} 不能用作命名空间。例如 hatiolab.com"
|
|
1112
1119
|
}
|