@things-factory/figure-ui 10.1.178 → 10.1.179

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.
Files changed (89) hide show
  1. package/client/figure-released.ts +6 -0
  2. package/client/graphql/index.ts +101 -25
  3. package/client/graphql/store.ts +2 -1
  4. package/client/index.ts +1 -0
  5. package/client/modeller/editor.ts +10 -2
  6. package/client/modeller/figure-first-save.ts +23 -3
  7. package/client/modeller/figure-parts.ts +53 -23
  8. package/client/modeller/figure-properties.ts +41 -7
  9. package/client/modeller/first-save.ts +2 -0
  10. package/client/modeller/product-session.ts +5 -3
  11. package/client/modeller/property-sections.ts +45 -0
  12. package/client/pages/figure-list-page.ts +52 -10
  13. package/client/pages/figure-modeller-page.ts +15 -3
  14. package/client/setting-let/figure-namespace-setting-let.ts +113 -31
  15. package/client/types.ts +8 -0
  16. package/client/viewparts/figure-detail-dock.ts +124 -9
  17. package/client/viewparts/figure-thumb.ts +6 -3
  18. package/dist-client/figure-released.d.ts +3 -0
  19. package/dist-client/figure-released.js +5 -0
  20. package/dist-client/figure-released.js.map +1 -0
  21. package/dist-client/graphql/index.d.ts +25 -13
  22. package/dist-client/graphql/index.js +85 -16
  23. package/dist-client/graphql/index.js.map +1 -1
  24. package/dist-client/graphql/store.js +2 -1
  25. package/dist-client/graphql/store.js.map +1 -1
  26. package/dist-client/index.d.ts +1 -0
  27. package/dist-client/index.js +1 -0
  28. package/dist-client/index.js.map +1 -1
  29. package/dist-client/modeller/editor.d.ts +2 -0
  30. package/dist-client/modeller/editor.js +13 -2
  31. package/dist-client/modeller/editor.js.map +1 -1
  32. package/dist-client/modeller/figure-first-save.d.ts +8 -0
  33. package/dist-client/modeller/figure-first-save.js +28 -3
  34. package/dist-client/modeller/figure-first-save.js.map +1 -1
  35. package/dist-client/modeller/figure-parts.d.ts +5 -0
  36. package/dist-client/modeller/figure-parts.js +54 -22
  37. package/dist-client/modeller/figure-parts.js.map +1 -1
  38. package/dist-client/modeller/figure-properties.d.ts +4 -0
  39. package/dist-client/modeller/figure-properties.js +45 -7
  40. package/dist-client/modeller/figure-properties.js.map +1 -1
  41. package/dist-client/modeller/first-save.d.ts +2 -0
  42. package/dist-client/modeller/first-save.js.map +1 -1
  43. package/dist-client/modeller/product-session.d.ts +4 -1
  44. package/dist-client/modeller/product-session.js +3 -3
  45. package/dist-client/modeller/product-session.js.map +1 -1
  46. package/dist-client/modeller/property-sections.d.ts +26 -0
  47. package/dist-client/modeller/property-sections.js +17 -0
  48. package/dist-client/modeller/property-sections.js.map +1 -0
  49. package/dist-client/pages/figure-list-page.d.ts +4 -1
  50. package/dist-client/pages/figure-list-page.js +56 -10
  51. package/dist-client/pages/figure-list-page.js.map +1 -1
  52. package/dist-client/pages/figure-modeller-page.d.ts +2 -0
  53. package/dist-client/pages/figure-modeller-page.js +19 -3
  54. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  55. package/dist-client/setting-let/figure-namespace-setting-let.d.ts +16 -5
  56. package/dist-client/setting-let/figure-namespace-setting-let.js +129 -31
  57. package/dist-client/setting-let/figure-namespace-setting-let.js.map +1 -1
  58. package/dist-client/tsconfig.tsbuildinfo +1 -1
  59. package/dist-client/types.d.ts +12 -0
  60. package/dist-client/types.js.map +1 -1
  61. package/dist-client/viewparts/figure-detail-dock.d.ts +33 -2
  62. package/dist-client/viewparts/figure-detail-dock.js +171 -11
  63. package/dist-client/viewparts/figure-detail-dock.js.map +1 -1
  64. package/dist-client/viewparts/figure-thumb.d.ts +2 -0
  65. package/dist-client/viewparts/figure-thumb.js +13 -3
  66. package/dist-client/viewparts/figure-thumb.js.map +1 -1
  67. package/package.json +8 -7
  68. package/test/browser/category-page.js +28 -0
  69. package/test/browser/category.check.mjs +54 -0
  70. package/test/browser/mesh-select.check.mjs +1 -1
  71. package/test/browser/namespace-page.js +75 -0
  72. package/test/browser/namespace.check.mjs +104 -0
  73. package/test/browser/parts-page.js +8 -2
  74. package/test/browser/places-mesh.check.mjs +30 -2
  75. package/test/browser/property-section-page.js +45 -0
  76. package/test/browser/property-section.check.mjs +48 -0
  77. package/test/browser/props.check.mjs +13 -2
  78. package/test/browser/public-page.js +39 -0
  79. package/test/browser/public.check.mjs +79 -0
  80. package/test/browser/retire-page.js +37 -0
  81. package/test/browser/retire.check.mjs +96 -0
  82. package/test/figure-released.test.ts +54 -0
  83. package/test/modeller-route.test.ts +2 -1
  84. package/translations/en.json +20 -5
  85. package/translations/es.json +20 -5
  86. package/translations/ja.json +20 -5
  87. package/translations/ko.json +20 -5
  88. package/translations/ms.json +20 -5
  89. package/translations/zh.json +20 -5
@@ -30,6 +30,18 @@ export interface Figure {
30
30
  source?: string;
31
31
  properties?: string;
32
32
  version?: number;
33
+ /** Retired: still released and served, no longer offered for new use. */
34
+ deprecated?: boolean;
35
+ /** The type to use instead of a retired figure, in the same domain. */
36
+ supersededBy?: string | null;
37
+ /** Public: other domains read its releases and draw it by full name (LV figure-server design G2). */
38
+ public?: boolean;
39
+ /** The domain it belongs to; carried by another domain's public figure, so a screen says whose it is. */
40
+ domain?: {
41
+ id: string;
42
+ name?: string;
43
+ subdomain?: string;
44
+ } | null;
33
45
  }
34
46
  /**
35
47
  * 발행된 판 하나.
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../client/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * 화면이 다루는 모양. 도형 자산의 타입은 `@hatiolab/figure-model` 이 정한다 — 여기서 다시 적지 않는다.\n */\n\n/** 서버가 돌려주는 Figure 한 벌. `source` 는 상세 또는 저작면을 열 때만 받는다. */\nexport interface Figure {\n id: string\n /** 컴포넌트 타입 이름. **만든 뒤에 고칠 수 없다.** */\n type: string\n /** Namespace of the full name {namespace}/{type} (ADR-0114). */\n namespace?: string | null\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 /** 도형 자산 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 /** 발행 전 초안에서만 받는다 — 발행된 뒤에는 문이 거절한다(ADR-0063 결정 4). */\n type?: string\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 * `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":"AAAA;;GAEG","sourcesContent":["/**\n * 화면이 다루는 모양. 도형 자산의 타입은 `@hatiolab/figure-model` 이 정한다 — 여기서 다시 적지 않는다.\n */\n\n/** 서버가 돌려주는 Figure 한 벌. `source` 는 상세 또는 저작면을 열 때만 받는다. */\nexport interface Figure {\n id: string\n /** 컴포넌트 타입 이름. **만든 뒤에 고칠 수 없다.** */\n type: string\n /** Namespace of the full name {namespace}/{type} (ADR-0114). */\n namespace?: string | null\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 /** 도형 자산 JSON 문자열. 목록 질의에는 실려 오지 않는다. */\n source?: string\n properties?: string\n version?: number\n /** Retired: still released and served, no longer offered for new use. */\n deprecated?: boolean\n /** The type to use instead of a retired figure, in the same domain. */\n supersededBy?: string | null\n /** Public: other domains read its releases and draw it by full name (LV figure-server design G2). */\n public?: boolean\n /** The domain it belongs to; carried by another domain's public figure, so a screen says whose it is. */\n domain?: { id: string; name?: string; subdomain?: string } | null\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 /** 발행 전 초안에서만 받는다 — 발행된 뒤에는 문이 거절한다(ADR-0063 결정 4). */\n type?: string\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 * `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"]}
@@ -5,12 +5,18 @@ import '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js';
5
5
  import '@things-factory/figure-viewer/dist-client/figure-facts-list.js';
6
6
  /** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */
7
7
  export declare const FIGURE_THUMBNAIL_CHANGED = "figure-thumbnail-changed";
8
+ /** Said on window when a figure was retired or brought back: `{ detail: { id } }`. */
9
+ export declare const FIGURE_RETIREMENT_CHANGED = "figure-retirement-changed";
10
+ /** Said on window when a figure was made public or private: `{ detail: { id } }`. */
11
+ export declare const FIGURE_PUBLIC_CHANGED = "figure-public-changed";
8
12
  export interface FigureDetailRecord {
9
13
  saved: Figure;
10
14
  released?: {
11
15
  version: number;
12
16
  source: string;
13
17
  };
18
+ /** Another domain's public figure (G2): only its release is shown, and nothing on it is changed from here. */
19
+ foreign?: boolean;
14
20
  }
15
21
  declare const FigureDetailDock_base: (new (...args: any[]) => LitElement) & typeof LitElement;
16
22
  /** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */
@@ -22,6 +28,15 @@ export declare class FigureDetailDock extends FigureDetailDock_base {
22
28
  * released figure a draft (`updateFigure`). The catalogue is told, and reads its cards again.
23
29
  */
24
30
  keepThumbnail: (id: string, dataUrl: string) => Promise<unknown>;
31
+ /** Retires a released figure or brings it back (`setFigureDeprecation`); a check gives its own. */
32
+ retire: (id: string, deprecated: boolean, supersededBy: string | null) => Promise<Figure>;
33
+ /** The types a retired figure may name instead: the domain's other figures. */
34
+ typeNamesOf: () => Promise<string[]>;
35
+ /** Makes a released figure public or private (`setFigurePublic`); a check gives its own. */
36
+ publish: (id: string, isPublic: boolean) => Promise<Figure>;
37
+ /** Another domain's public figure as the catalogue listed it, with its latest release; its draft is not this domain's to open. */
38
+ loadPublic: (row: Figure) => Promise<FigureDetailRecord>;
39
+ private publishError;
25
40
  private figureId;
26
41
  private record?;
27
42
  private asset?;
@@ -29,17 +44,33 @@ export declare class FigureDetailDock extends FigureDetailDock_base {
29
44
  private loading;
30
45
  private error;
31
46
  private cost?;
47
+ private retiring;
48
+ private instead;
49
+ private typeNames;
50
+ private retireError;
51
+ private retireBusy;
32
52
  private ticket;
33
53
  private unsubscribe?;
34
54
  private boundary?;
35
55
  private boundaryObserver?;
36
- show(id: string): Promise<void>;
56
+ show(id: string, publicRow?: Figure): Promise<void>;
37
57
  private choose;
38
58
  connectedCallback(): void;
39
59
  disconnectedCallback(): void;
40
60
  private place;
41
61
  private escape;
62
+ /**
63
+ * Retiring a released figure (it stays released and served; boards already using it keep it), naming the type to use
64
+ * instead, or bringing it back. A figure never released is deleted from the catalogue instead.
65
+ */
66
+ private retireRow;
67
+ /** Public to other domains, or not (G2): they read its releases and draw it by full name; its draft stays here. */
68
+ private publishRow;
69
+ private setPublic;
70
+ private openRetire;
71
+ private setRetired;
42
72
  render(): import("lit-html").TemplateResult<1>;
43
73
  }
44
- export declare function openFigureDetail(figure: Figure): void;
74
+ /** Opens the preview of a figure; `publicRow` for another domain's public figure, shown by its release alone. */
75
+ export declare function openFigureDetail(figure: Figure, publicRow?: boolean): void;
45
76
  export {};
@@ -10,7 +10,8 @@ import { ASSISTANT_DOCK_VIEWPART } from '@things-factory/ai-assistant/dist-clien
10
10
  import { figureCostOf, parseFigureAsset } from '@hatiolab/figure-model';
11
11
  import { capacityFrom } from '../modeller/scene-budget.js';
12
12
  import { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js';
13
- import { fetchFigure, fetchReleasedFigure, updateFigure } from '../graphql/index.js';
13
+ import { fetchFigure, fetchFigureTypeNames, fetchReleasedFigure, setFigureDeprecation, setFigurePublic, updateFigure } from '../graphql/index.js';
14
+ import { hasBeenReleased } from '../figure-released.js';
14
15
  import { modellerPathOf } from '../modeller/modeller-route.js';
15
16
  import { leftOpeningPage } from './detail-route.js';
16
17
  import '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js';
@@ -18,6 +19,10 @@ import '@things-factory/figure-viewer/dist-client/figure-facts-list.js';
18
19
  const VIEWPART = 'figure-detail';
19
20
  /** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */
20
21
  export const FIGURE_THUMBNAIL_CHANGED = 'figure-thumbnail-changed';
22
+ /** Said on window when a figure was retired or brought back: `{ detail: { id } }`. */
23
+ export const FIGURE_RETIREMENT_CHANGED = 'figure-retirement-changed';
24
+ /** Said on window when a figure was made public or private: `{ detail: { id } }`. */
25
+ export const FIGURE_PUBLIC_CHANGED = 'figure-public-changed';
21
26
  /** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */
22
27
  let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitElement) {
23
28
  constructor() {
@@ -36,10 +41,28 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
36
41
  await updateFigure(id, { thumbnail: dataUrl });
37
42
  window.dispatchEvent(new CustomEvent(FIGURE_THUMBNAIL_CHANGED, { detail: { id } }));
38
43
  };
44
+ /** Retires a released figure or brings it back (`setFigureDeprecation`); a check gives its own. */
45
+ this.retire = setFigureDeprecation;
46
+ /** The types a retired figure may name instead: the domain's other figures. */
47
+ this.typeNamesOf = fetchFigureTypeNames;
48
+ /** Makes a released figure public or private (`setFigurePublic`); a check gives its own. */
49
+ this.publish = setFigurePublic;
50
+ /** Another domain's public figure as the catalogue listed it, with its latest release; its draft is not this domain's to open. */
51
+ this.loadPublic = async (row) => ({
52
+ saved: row,
53
+ released: await fetchReleasedFigure(row.id),
54
+ foreign: true
55
+ });
56
+ this.publishError = '';
39
57
  this.figureId = '';
40
58
  this.sourceKind = 'saved';
41
59
  this.loading = false;
42
60
  this.error = '';
61
+ this.retiring = false;
62
+ this.instead = '';
63
+ this.typeNames = [];
64
+ this.retireError = '';
65
+ this.retireBusy = false;
43
66
  this.ticket = 0;
44
67
  this.place = () => {
45
68
  const bar = visibleBar(document);
@@ -99,11 +122,21 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
99
122
  p { margin: 0; line-height: 1.5; }
100
123
  [description] { padding: 10px 16px 0; color: var(--md-sys-color-on-surface-variant); }
101
124
  [status] { display: grid; gap: 8px; justify-items: start; padding: 12px 16px; }
125
+ /* Retiring: quiet words in the line under what is shown, like the source switch above it. */
126
+ [retire], [publish] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; color: var(--md-sys-color-on-surface-variant); }
127
+ [retire] button, [publish] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }
128
+ [retire] button:hover:not(:disabled), [publish] button:hover:not(:disabled) { color: var(--md-sys-color-on-surface); }
129
+ [publish] [published] { color: var(--md-sys-color-on-surface); font-weight: 600; }
130
+ [retire] button[primary] { color: var(--md-sys-color-primary); font-weight: 600; }
131
+ [retire] input { font: inherit; min-width: 0; flex: 1 1 160px; padding: 2px 6px; border: 1px solid var(--md-sys-color-outline-variant);
132
+ border-radius: 6px; background: var(--md-sys-color-surface); color: var(--md-sys-color-on-surface); }
133
+ [retire] [retired] { color: var(--md-sys-color-on-surface); font-weight: 600; }
134
+ [retire-error] { color: var(--md-sys-color-error); }
102
135
  @media (max-width: 1439.98px) {
103
136
  :host { position: fixed; inset: var(--figure-detail-top, 44px) 0 0 auto; height: calc(100dvh - var(--figure-detail-top, 44px)); z-index: 11; box-shadow: -4px 0 16px rgb(0 0 0 / 20%); }
104
137
  }
105
138
  `]; }
106
- async show(id) {
139
+ async show(id, publicRow) {
107
140
  const ticket = ++this.ticket;
108
141
  this.figureId = id;
109
142
  this.record = undefined;
@@ -111,8 +144,11 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
111
144
  this.cost = undefined;
112
145
  this.loading = true;
113
146
  this.error = '';
147
+ this.retiring = false;
148
+ this.retireError = '';
149
+ this.publishError = '';
114
150
  try {
115
- const record = await this.load(id);
151
+ const record = publicRow ? await this.loadPublic(publicRow) : await this.load(id);
116
152
  if (ticket !== this.ticket)
117
153
  return;
118
154
  this.record = record;
@@ -168,6 +204,85 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
168
204
  this.boundaryObserver?.disconnect();
169
205
  this.boundary = undefined;
170
206
  }
207
+ /**
208
+ * Retiring a released figure (it stays released and served; boards already using it keep it), naming the type to use
209
+ * instead, or bringing it back. A figure never released is deleted from the catalogue instead.
210
+ */
211
+ retireRow(saved) {
212
+ if (saved.deprecated) {
213
+ return html `<div retire>
214
+ <span retired>${saved.supersededBy
215
+ ? i18next.t('figure.text.retired-instead', { type: saved.supersededBy })
216
+ : i18next.t('figure.label.retired')}</span>
217
+ <button ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, false)}>${i18next.t('figure.button.unretire')}</button>
218
+ </div>
219
+ ${this.retireError ? html `<p retire-error role="alert">${this.retireError}</p>` : nothing}`;
220
+ }
221
+ if (!this.retiring) {
222
+ return html `<div retire><button @click=${() => this.openRetire(saved)}>${i18next.t('figure.button.retire')}</button></div>`;
223
+ }
224
+ return html `<div retire>
225
+ <input list="figure-retire-instead" aria-label=${i18next.t('figure.label.superseded-by')}
226
+ placeholder=${i18next.t('figure.label.superseded-by')} .value=${this.instead}
227
+ @input=${(e) => (this.instead = e.target.value)} />
228
+ <datalist id="figure-retire-instead">${this.typeNames.map(name => html `<option value=${name}></option>`)}</datalist>
229
+ <button primary ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, true)}>${i18next.t('figure.button.retire')}</button>
230
+ <button ?disabled=${this.retireBusy} @click=${() => { this.retiring = false; this.retireError = ''; }}>${i18next.t('figure.button.cancel')}</button>
231
+ </div>
232
+ ${this.retireError ? html `<p retire-error role="alert">${this.retireError}</p>` : nothing}`;
233
+ }
234
+ /** Public to other domains, or not (G2): they read its releases and draw it by full name; its draft stays here. */
235
+ publishRow(saved) {
236
+ return html `<div publish>
237
+ ${saved.public ? html `<span published>${i18next.t('figure.label.public')}</span>` : nothing}
238
+ <button ?disabled=${this.retireBusy} @click=${() => this.setPublic(saved, !saved.public)}>${i18next.t(saved.public ? 'figure.button.make-private' : 'figure.button.make-public')}</button>
239
+ </div>
240
+ ${this.publishError ? html `<p retire-error role="alert">${this.publishError}</p>` : nothing}`;
241
+ }
242
+ async setPublic(saved, isPublic) {
243
+ this.retireBusy = true;
244
+ this.publishError = '';
245
+ try {
246
+ const figure = await this.publish(saved.id, isPublic);
247
+ if (this.record?.saved.id === saved.id)
248
+ this.record = { ...this.record, saved: { ...this.record.saved, ...figure } };
249
+ window.dispatchEvent(new CustomEvent(FIGURE_PUBLIC_CHANGED, { detail: { id: saved.id } }));
250
+ }
251
+ catch (e) {
252
+ this.publishError = e.message;
253
+ }
254
+ finally {
255
+ this.retireBusy = false;
256
+ }
257
+ }
258
+ async openRetire(saved) {
259
+ this.retiring = true;
260
+ this.instead = '';
261
+ this.retireError = '';
262
+ try {
263
+ this.typeNames = (await this.typeNamesOf()).filter(name => name !== saved.type);
264
+ }
265
+ catch {
266
+ this.typeNames = [];
267
+ }
268
+ }
269
+ async setRetired(saved, deprecated) {
270
+ this.retireBusy = true;
271
+ this.retireError = '';
272
+ try {
273
+ const figure = await this.retire(saved.id, deprecated, deprecated ? this.instead.trim() || null : null);
274
+ if (this.record?.saved.id === saved.id)
275
+ this.record = { ...this.record, saved: { ...this.record.saved, ...figure } };
276
+ this.retiring = false;
277
+ window.dispatchEvent(new CustomEvent(FIGURE_RETIREMENT_CHANGED, { detail: { id: saved.id } }));
278
+ }
279
+ catch (e) {
280
+ this.retireError = e.message;
281
+ }
282
+ finally {
283
+ this.retireBusy = false;
284
+ }
285
+ }
171
286
  render() {
172
287
  const saved = this.record?.saved;
173
288
  const released = this.record?.released;
@@ -179,24 +294,26 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
179
294
  return html `<header>
180
295
  <div title-block><h2 title=${saved?.name || ''}>${saved?.name || i18next.t('figure.title.detail')}</h2>
181
296
  ${saved ? html `<code title=${fullName(saved)}>${fullName(saved)}</code>` : nothing}</div>
182
- <button icon edit aria-label=${i18next.t('figure.button.detail-edit')} title=${i18next.t('figure.button.detail-edit')} ?disabled=${!saved}
297
+ ${this.record?.foreign ? nothing : html `<button icon edit aria-label=${i18next.t('figure.button.detail-edit')} title=${i18next.t('figure.button.detail-edit')} ?disabled=${!saved}
183
298
  @click=${() => {
184
299
  // The modeller shows the figure itself; a preview of it beside the editor only covers the work.
185
300
  hideDetail();
186
301
  navigate(modellerPathOf(saved));
187
- }}><md-icon>edit</md-icon></button>
302
+ }}><md-icon>edit</md-icon></button>`}
188
303
  <button icon aria-label=${i18next.t('figure.button.close')} title=${i18next.t('figure.button.close')} @click=${() => hideDetail()}><md-icon>close</md-icon></button>
189
304
  </header>
190
305
  <div body>
191
306
  ${this.loading ? html `<p status role="status">${i18next.t('figure.text.detail-loading')}</p>` : nothing}
192
307
  ${this.error ? html `<div status role="alert"><p>${this.error}</p><button act @click=${() => this.show(this.figureId)}><md-icon>refresh</md-icon>${i18next.t('figure.button.detail-retry')}</button></div>` : nothing}
193
308
  ${saved ? html `<section meta>
194
- ${released
195
- ? html `<div source role="group" aria-label=${i18next.t('figure.label.detail-source')}>
309
+ ${released && this.record?.foreign
310
+ ? html `<div source><span>${i18next.t('figure.label.detail-released', { version: released.version })}</span>${saved.domain?.name ? html `<span owner>${saved.domain.name}</span>` : nothing}</div>`
311
+ : released
312
+ ? html `<div source role="group" aria-label=${i18next.t('figure.label.detail-source')}>
196
313
  <button aria-pressed=${String(this.sourceKind === 'released')} @click=${() => this.choose('released')}>${i18next.t('figure.label.detail-released', { version: released.version })}</button>
197
314
  <button aria-pressed=${String(this.sourceKind === 'saved')} @click=${() => this.choose('saved')}>${savedLabel}</button>
198
315
  </div>`
199
- : html `<div source><span title=${i18next.t('figure.text.detail-unreleased')}>${savedLabel}</span></div>`}
316
+ : html `<div source><span title=${i18next.t('figure.text.detail-unreleased')}>${savedLabel}</span></div>`}
200
317
  ${this.asset ? html `<dl stats>
201
318
  <div><dt>${i18next.t('figure.label.placement-limit')}</dt><dd>${capacity
202
319
  ? i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })
@@ -205,9 +322,11 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
205
322
  <div><dt>${i18next.t('figure.label.budget-groups')}</dt><dd>${cost?.groups ?? '—'}</dd></div>
206
323
  <div><dt>${i18next.t('figure.label.budget-parts')}</dt><dd>${this.asset.appearance.length}</dd></div>
207
324
  </dl>` : nothing}
325
+ ${hasBeenReleased(saved) && !this.record?.foreign ? this.retireRow(saved) : nothing}
326
+ ${hasBeenReleased(saved) && !this.record?.foreign ? this.publishRow(saved) : nothing}
208
327
  </section>` : nothing}
209
328
  ${this.asset ? html `<figure-asset-viewer .asset=${this.asset}
210
- .keepShot=${saved?.id ? (dataUrl) => this.keepThumbnail(saved.id, dataUrl) : undefined}>
329
+ .keepShot=${saved?.id && !this.record?.foreign ? (dataUrl) => this.keepThumbnail(saved.id, dataUrl) : undefined}>
211
330
  ${saved?.description ? html `<p slot="facts" description>${saved.description}</p>` : nothing}
212
331
  ${saved ? html `<figure-facts-list slot="facts" .asset=${this.asset} .row=${saved}></figure-facts-list>` : nothing}
213
332
  </figure-asset-viewer>` : nothing}
@@ -222,6 +341,26 @@ __decorate([
222
341
  property({ attribute: false }),
223
342
  __metadata("design:type", Function)
224
343
  ], FigureDetailDock.prototype, "keepThumbnail", void 0);
344
+ __decorate([
345
+ property({ attribute: false }),
346
+ __metadata("design:type", Function)
347
+ ], FigureDetailDock.prototype, "retire", void 0);
348
+ __decorate([
349
+ property({ attribute: false }),
350
+ __metadata("design:type", Function)
351
+ ], FigureDetailDock.prototype, "typeNamesOf", void 0);
352
+ __decorate([
353
+ property({ attribute: false }),
354
+ __metadata("design:type", Function)
355
+ ], FigureDetailDock.prototype, "publish", void 0);
356
+ __decorate([
357
+ property({ attribute: false }),
358
+ __metadata("design:type", Function)
359
+ ], FigureDetailDock.prototype, "loadPublic", void 0);
360
+ __decorate([
361
+ state(),
362
+ __metadata("design:type", Object)
363
+ ], FigureDetailDock.prototype, "publishError", void 0);
225
364
  __decorate([
226
365
  state(),
227
366
  __metadata("design:type", Object)
@@ -250,6 +389,26 @@ __decorate([
250
389
  state(),
251
390
  __metadata("design:type", void 0)
252
391
  ], FigureDetailDock.prototype, "cost", void 0);
392
+ __decorate([
393
+ state(),
394
+ __metadata("design:type", Object)
395
+ ], FigureDetailDock.prototype, "retiring", void 0);
396
+ __decorate([
397
+ state(),
398
+ __metadata("design:type", Object)
399
+ ], FigureDetailDock.prototype, "instead", void 0);
400
+ __decorate([
401
+ state(),
402
+ __metadata("design:type", Array)
403
+ ], FigureDetailDock.prototype, "typeNames", void 0);
404
+ __decorate([
405
+ state(),
406
+ __metadata("design:type", Object)
407
+ ], FigureDetailDock.prototype, "retireError", void 0);
408
+ __decorate([
409
+ state(),
410
+ __metadata("design:type", Object)
411
+ ], FigureDetailDock.prototype, "retireBusy", void 0);
253
412
  FigureDetailDock = __decorate([
254
413
  customElement('figure-detail-dock')
255
414
  ], FigureDetailDock);
@@ -275,7 +434,8 @@ let openedOn;
275
434
  function hideDetail() {
276
435
  store.dispatch({ type: UPDATE_VIEWPART, name: VIEWPART, override: { show: false } });
277
436
  }
278
- export function openFigureDetail(figure) {
437
+ /** Opens the preview of a figure; `publicRow` for another domain's public figure, shown by its release alone. */
438
+ export function openFigureDetail(figure, publicRow = false) {
279
439
  if (!dock) {
280
440
  dock = document.createElement('figure-detail-dock');
281
441
  }
@@ -283,6 +443,6 @@ export function openFigureDetail(figure) {
283
443
  store.dispatch({ type: UPDATE_VIEWPART, name: ASSISTANT_DOCK_VIEWPART, override: { show: false } });
284
444
  appendViewpart({ name: VIEWPART, position: VIEWPART_POSITION.ASIDEBAR,
285
445
  viewpart: { show: true, persistent: true, level: VIEWPART_LEVEL.TOPMOST, template: html `${dock}` } });
286
- void dock.show(figure.id);
446
+ void dock.show(figure.id, publicRow ? figure : undefined);
287
447
  }
288
448
  //# sourceMappingURL=figure-detail-dock.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"figure-detail-dock.js","sourceRoot":"","sources":["../../client/viewparts/figure-detail-dock.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { appendViewpart, UPDATE_VIEWPART, VIEWPART_POSITION, VIEWPART_LEVEL } from '@operato/layout'\nimport { navigate, store } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { ASSISTANT_DOCK_VIEWPART } from '@things-factory/ai-assistant/dist-client/components/assistant-dock.js'\nimport { figureCostOf, parseFigureAsset, type FigureAsset } from '@hatiolab/figure-model'\nimport { capacityFrom } from '../modeller/scene-budget.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { fetchFigure, fetchReleasedFigure, updateFigure } from '../graphql/index.js'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { leftOpeningPage } from './detail-route.js'\nimport type { Figure } from '../types.js'\nimport '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js'\nimport '@things-factory/figure-viewer/dist-client/figure-facts-list.js'\n\nconst VIEWPART = 'figure-detail'\n/** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */\nexport const FIGURE_THUMBNAIL_CHANGED = 'figure-thumbnail-changed'\nexport interface FigureDetailRecord {\n saved: Figure\n released?: { version: number; source: string }\n}\n\n/** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */\n@customElement('figure-detail-dock')\nexport class FigureDetailDock extends localize(i18next)(LitElement) {\n /*\n The modeller's side panel look (SidePanelStyles). What the preview is opened for sits above the model -- name, type,\n which model is shown, how many fit -- as plain text; the controls are borderless icons and quiet text, so the model\n and its numbers are what the eye meets.\n */\n static styles = [ScrollbarStyles, SidePanelStyles, css`\n :host { display: flex; flex-direction: column; width: 400px; max-width: 100vw; height: 100%; min-height: 0;\n background: var(--md-sys-color-surface); border-left: 1px solid var(--md-sys-color-outline-variant); }\n header { display: flex; align-items: flex-start; gap: 2px; padding: 12px 8px 4px 16px; }\n [title-block] { flex: 1; min-width: 0; display: grid; gap: 2px; }\n h2 { margin: 0; font-size: var(--ui-t-title, 16px); font-weight: 600; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n code { font-family: var(--ui-mono, ui-monospace, monospace); color: var(--md-sys-color-on-surface-variant); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n button { font: inherit; color: inherit; cursor: pointer; }\n button:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 1px; }\n button[icon] { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: none; border-radius: 6px;\n background: transparent; color: var(--md-sys-color-on-surface-variant); }\n button[icon]:hover:not(:disabled) { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }\n button[icon]:disabled { opacity: 0.38; cursor: default; }\n button[icon] md-icon { --md-icon-size: 18px; }\n /*\n The figure stays in view under what it is (which model, how many fit) and its tabs; only the tab's body scrolls\n (user 2026-10-15: a clip played at the bottom of a scrolled dock moved a figure no longer seen).\n */\n [body] { flex: 1; display: flex; flex-direction: column; min-height: 0; }\n [body] > * { flex: none; }\n [body] > figure-asset-viewer { flex: 1; }\n [meta] { display: grid; gap: 8px; padding: 0 16px 12px; }\n /* Which model is shown: words that change colour, not boxes. */\n [source] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; color: var(--md-sys-color-on-surface-variant); }\n [source] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }\n [source] button:hover { color: var(--md-sys-color-on-surface); }\n [source] button[aria-pressed='true'] { color: var(--md-sys-color-primary); font-weight: 600; }\n dl { margin: 0; }\n dt { color: var(--md-sys-color-on-surface-variant); }\n dd { margin: 0; color: var(--md-sys-color-on-surface); }\n dl[stats] { display: flex; flex-wrap: wrap; gap: 4px 16px; }\n dl[stats] > div { display: flex; align-items: baseline; gap: 4px; }\n dl[stats] dd { font-weight: 600; font-variant-numeric: tabular-nums; }\n p { margin: 0; line-height: 1.5; }\n [description] { padding: 10px 16px 0; color: var(--md-sys-color-on-surface-variant); }\n [status] { display: grid; gap: 8px; justify-items: start; padding: 12px 16px; }\n @media (max-width: 1439.98px) {\n :host { position: fixed; inset: var(--figure-detail-top, 44px) 0 0 auto; height: calc(100dvh - var(--figure-detail-top, 44px)); z-index: 11; box-shadow: -4px 0 16px rgb(0 0 0 / 20%); }\n }\n `]\n @property({ attribute: false }) load: (id: string) => Promise<FigureDetailRecord> = async id => {\n const [saved, released] = await Promise.all([fetchFigure(id), fetchReleasedFigure(id)])\n if (!saved?.source) throw Error(i18next.t('figure.text.detail-no-source'))\n return { saved, released }\n }\n /**\n * Keeps the picture taken in the preview as the figure's card picture. A patch of the picture alone does not make a\n * released figure a draft (`updateFigure`). The catalogue is told, and reads its cards again.\n */\n @property({ attribute: false }) keepThumbnail: (id: string, dataUrl: string) => Promise<unknown> = async (id, dataUrl) => {\n await updateFigure(id, { thumbnail: dataUrl })\n window.dispatchEvent(new CustomEvent(FIGURE_THUMBNAIL_CHANGED, { detail: { id } }))\n }\n @state() private figureId = ''\n @state() private record?: FigureDetailRecord\n @state() private asset?: FigureAsset\n @state() private sourceKind: 'saved' | 'released' = 'saved'\n @state() private loading = false\n @state() private error = ''\n @state() private cost?: ReturnType<typeof figureCostOf>\n private ticket = 0\n private unsubscribe?: () => void\n private boundary?: Element\n private boundaryObserver?: ResizeObserver\n\n async show(id: string) {\n const ticket = ++this.ticket\n this.figureId = id\n this.record = undefined\n this.asset = undefined\n this.cost = undefined\n this.loading = true\n this.error = ''\n try {\n const record = await this.load(id)\n if (ticket !== this.ticket) return\n this.record = record\n this.choose(record.released ? 'released' : 'saved')\n } catch {\n if (ticket === this.ticket) this.error = i18next.t('figure.text.detail-load-failed')\n } finally {\n if (ticket === this.ticket) this.loading = false\n }\n }\n private choose(kind: 'saved' | 'released') {\n this.sourceKind = kind\n this.asset = undefined\n this.cost = undefined\n this.error = ''\n try {\n const source = kind === 'released' ? this.record?.released?.source : this.record?.saved.source\n if (!source) throw Error('missing source')\n const asset = parseFigureAsset(source)\n this.cost = figureCostOf(asset)\n this.asset = asset\n } catch {\n this.error = i18next.t('figure.text.detail-invalid-source')\n }\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('resize', this.place)\n window.addEventListener('popstate', this.place)\n this.unsubscribe = store.subscribe(() => {\n const state = store.getState() as { layout?: { viewparts?: Record<string, { show?: boolean }> }; route?: { page?: string } }\n const parts = state.layout?.viewparts\n if (parts?.[VIEWPART]?.show && (parts?.[ASSISTANT_DOCK_VIEWPART]?.show || leftOpeningPage(openedOn, state.route?.page))) hideDetail()\n else requestAnimationFrame(this.place)\n })\n this.place()\n window.addEventListener('keydown', this.escape)\n }\n disconnectedCallback() {\n super.disconnectedCallback()\n window.removeEventListener('resize', this.place)\n window.removeEventListener('popstate', this.place)\n window.removeEventListener('keydown', this.escape)\n this.unsubscribe?.()\n this.boundaryObserver?.disconnect()\n this.boundary = undefined\n }\n private place = () => {\n const bar = visibleBar(document)\n if (bar !== this.boundary) {\n this.boundaryObserver?.disconnect()\n this.boundary = bar\n if (bar) {\n this.boundaryObserver = new ResizeObserver(this.place)\n this.boundaryObserver.observe(bar)\n }\n }\n this.style.setProperty('--figure-detail-top', `${bar?.getBoundingClientRect().bottom ?? 44}px`)\n }\n private escape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') hideDetail()\n }\n render() {\n const saved = this.record?.saved\n const released = this.record?.released\n const cost = this.cost\n const capacity = cost ? capacityFrom(cost.groups, cost.triangles) : undefined\n const savedLabel = saved\n ? `${i18next.t('figure.label.detail-saved')} · ${i18next.t(saved.state === 'released' ? 'figure.label.figure-released' : 'figure.label.draft')}`\n : ''\n return html`<header>\n <div title-block><h2 title=${saved?.name || ''}>${saved?.name || i18next.t('figure.title.detail')}</h2>\n ${saved ? html`<code title=${fullName(saved)}>${fullName(saved)}</code>` : nothing}</div>\n <button icon edit aria-label=${i18next.t('figure.button.detail-edit')} title=${i18next.t('figure.button.detail-edit')} ?disabled=${!saved}\n @click=${() => {\n // The modeller shows the figure itself; a preview of it beside the editor only covers the work.\n hideDetail()\n navigate(modellerPathOf(saved))\n }}><md-icon>edit</md-icon></button>\n <button icon aria-label=${i18next.t('figure.button.close')} title=${i18next.t('figure.button.close')} @click=${() => hideDetail()}><md-icon>close</md-icon></button>\n </header>\n <div body>\n ${this.loading ? html`<p status role=\"status\">${i18next.t('figure.text.detail-loading')}</p>` : nothing}\n ${this.error ? html`<div status role=\"alert\"><p>${this.error}</p><button act @click=${() => this.show(this.figureId)}><md-icon>refresh</md-icon>${i18next.t('figure.button.detail-retry')}</button></div>` : nothing}\n ${saved ? html`<section meta>\n ${released\n ? html`<div source role=\"group\" aria-label=${i18next.t('figure.label.detail-source')}>\n <button aria-pressed=${String(this.sourceKind === 'released')} @click=${() => this.choose('released')}>${i18next.t('figure.label.detail-released', { version: released.version })}</button>\n <button aria-pressed=${String(this.sourceKind === 'saved')} @click=${() => this.choose('saved')}>${savedLabel}</button>\n </div>`\n : html`<div source><span title=${i18next.t('figure.text.detail-unreleased')}>${savedLabel}</span></div>`}\n ${this.asset ? html`<dl stats>\n <div><dt>${i18next.t('figure.label.placement-limit')}</dt><dd>${capacity\n ? i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })\n : i18next.t('figure.text.not-measured-yet')}</dd></div>\n <div><dt>${i18next.t('figure.label.triangles')}</dt><dd>${cost?.triangles.toLocaleString() ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-groups')}</dt><dd>${cost?.groups ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-parts')}</dt><dd>${this.asset.appearance.length}</dd></div>\n </dl>` : nothing}\n </section>` : nothing}\n ${this.asset ? html`<figure-asset-viewer .asset=${this.asset}\n .keepShot=${saved?.id ? (dataUrl: string) => this.keepThumbnail(saved.id, dataUrl) : undefined}>\n ${saved?.description ? html`<p slot=\"facts\" description>${saved.description}</p>` : nothing}\n ${saved ? html`<figure-facts-list slot=\"facts\" .asset=${this.asset} .row=${saved}></figure-facts-list>` : nothing}\n </figure-asset-viewer>` : nothing}\n </div>`\n }\n}\n\n/** The name a board stores: `{namespace}/{type}` (ADR-0114). */\nfunction fullName(figure: Figure): string {\n return figure.namespace ? `${figure.namespace}/${figure.type}` : figure.type\n}\n\nfunction visibleBar(root: Document | ShadowRoot): Element | undefined {\n for (const element of root.querySelectorAll('*')) {\n if (element.tagName === 'OX-CANVAS-BAR' && element.getBoundingClientRect().height > 0) return element\n if (element.shadowRoot) {\n const found = visibleBar(element.shadowRoot)\n if (found) return found\n }\n }\n}\n\nlet dock: FigureDetailDock | undefined\n/** The page the preview was opened on; the preview belongs to that page and closes when the shell leaves it. */\nlet openedOn: string | undefined\nfunction hideDetail() {\n store.dispatch({ type: UPDATE_VIEWPART, name: VIEWPART, override: { show: false } })\n}\nexport function openFigureDetail(figure: Figure) {\n if (!dock) {\n dock = document.createElement('figure-detail-dock') as FigureDetailDock\n }\n openedOn = (store.getState() as { route?: { page?: string } }).route?.page\n store.dispatch({ type: UPDATE_VIEWPART, name: ASSISTANT_DOCK_VIEWPART, override: { show: false } })\n appendViewpart({ name: VIEWPART, position: VIEWPART_POSITION.ASIDEBAR,\n viewpart: { show: true, persistent: true, level: VIEWPART_LEVEL.TOPMOST, template: html`${dock}` } })\n void dock.show(figure.id)\n}\n"]}
1
+ {"version":3,"file":"figure-detail-dock.js","sourceRoot":"","sources":["../../client/viewparts/figure-detail-dock.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { i18next, localize } from '@operato/i18n'\nimport { appendViewpart, UPDATE_VIEWPART, VIEWPART_POSITION, VIEWPART_LEVEL } from '@operato/layout'\nimport { navigate, store } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { ASSISTANT_DOCK_VIEWPART } from '@things-factory/ai-assistant/dist-client/components/assistant-dock.js'\nimport { figureCostOf, parseFigureAsset, type FigureAsset } from '@hatiolab/figure-model'\nimport { capacityFrom } from '../modeller/scene-budget.js'\nimport { SidePanelStyles } from '@things-factory/figure-viewer/dist-client/side-panel-styles.js'\nimport { fetchFigure, fetchFigureTypeNames, fetchReleasedFigure, setFigureDeprecation, setFigurePublic, updateFigure } from '../graphql/index.js'\nimport { hasBeenReleased } from '../figure-released.js'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { leftOpeningPage } from './detail-route.js'\nimport type { Figure } from '../types.js'\nimport '@things-factory/figure-viewer/dist-client/figure-asset-viewer.js'\nimport '@things-factory/figure-viewer/dist-client/figure-facts-list.js'\n\nconst VIEWPART = 'figure-detail'\n/** Said on window when a figure's card picture was replaced from the preview: `{ detail: { id } }`. */\nexport const FIGURE_THUMBNAIL_CHANGED = 'figure-thumbnail-changed'\n/** Said on window when a figure was retired or brought back: `{ detail: { id } }`. */\nexport const FIGURE_RETIREMENT_CHANGED = 'figure-retirement-changed'\n/** Said on window when a figure was made public or private: `{ detail: { id } }`. */\nexport const FIGURE_PUBLIC_CHANGED = 'figure-public-changed'\nexport interface FigureDetailRecord {\n saved: Figure\n released?: { version: number; source: string }\n /** Another domain's public figure (G2): only its release is shown, and nothing on it is changed from here. */\n foreign?: boolean\n}\n\n/** Catalogue details stay while the catalogue is shown; choosing a size or a pose has no write path. */\n@customElement('figure-detail-dock')\nexport class FigureDetailDock extends localize(i18next)(LitElement) {\n /*\n The modeller's side panel look (SidePanelStyles). What the preview is opened for sits above the model -- name, type,\n which model is shown, how many fit -- as plain text; the controls are borderless icons and quiet text, so the model\n and its numbers are what the eye meets.\n */\n static styles = [ScrollbarStyles, SidePanelStyles, css`\n :host { display: flex; flex-direction: column; width: 400px; max-width: 100vw; height: 100%; min-height: 0;\n background: var(--md-sys-color-surface); border-left: 1px solid var(--md-sys-color-outline-variant); }\n header { display: flex; align-items: flex-start; gap: 2px; padding: 12px 8px 4px 16px; }\n [title-block] { flex: 1; min-width: 0; display: grid; gap: 2px; }\n h2 { margin: 0; font-size: var(--ui-t-title, 16px); font-weight: 600; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n code { font-family: var(--ui-mono, ui-monospace, monospace); color: var(--md-sys-color-on-surface-variant); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n button { font: inherit; color: inherit; cursor: pointer; }\n button:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 1px; }\n button[icon] { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: none; border-radius: 6px;\n background: transparent; color: var(--md-sys-color-on-surface-variant); }\n button[icon]:hover:not(:disabled) { background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface); }\n button[icon]:disabled { opacity: 0.38; cursor: default; }\n button[icon] md-icon { --md-icon-size: 18px; }\n /*\n The figure stays in view under what it is (which model, how many fit) and its tabs; only the tab's body scrolls\n (user 2026-10-15: a clip played at the bottom of a scrolled dock moved a figure no longer seen).\n */\n [body] { flex: 1; display: flex; flex-direction: column; min-height: 0; }\n [body] > * { flex: none; }\n [body] > figure-asset-viewer { flex: 1; }\n [meta] { display: grid; gap: 8px; padding: 0 16px 12px; }\n /* Which model is shown: words that change colour, not boxes. */\n [source] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; color: var(--md-sys-color-on-surface-variant); }\n [source] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }\n [source] button:hover { color: var(--md-sys-color-on-surface); }\n [source] button[aria-pressed='true'] { color: var(--md-sys-color-primary); font-weight: 600; }\n dl { margin: 0; }\n dt { color: var(--md-sys-color-on-surface-variant); }\n dd { margin: 0; color: var(--md-sys-color-on-surface); }\n dl[stats] { display: flex; flex-wrap: wrap; gap: 4px 16px; }\n dl[stats] > div { display: flex; align-items: baseline; gap: 4px; }\n dl[stats] dd { font-weight: 600; font-variant-numeric: tabular-nums; }\n p { margin: 0; line-height: 1.5; }\n [description] { padding: 10px 16px 0; color: var(--md-sys-color-on-surface-variant); }\n [status] { display: grid; gap: 8px; justify-items: start; padding: 12px 16px; }\n /* Retiring: quiet words in the line under what is shown, like the source switch above it. */\n [retire], [publish] { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; color: var(--md-sys-color-on-surface-variant); }\n [retire] button, [publish] button { padding: 0; border: none; background: none; color: var(--md-sys-color-on-surface-variant); }\n [retire] button:hover:not(:disabled), [publish] button:hover:not(:disabled) { color: var(--md-sys-color-on-surface); }\n [publish] [published] { color: var(--md-sys-color-on-surface); font-weight: 600; }\n [retire] button[primary] { color: var(--md-sys-color-primary); font-weight: 600; }\n [retire] input { font: inherit; min-width: 0; flex: 1 1 160px; padding: 2px 6px; border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px; background: var(--md-sys-color-surface); color: var(--md-sys-color-on-surface); }\n [retire] [retired] { color: var(--md-sys-color-on-surface); font-weight: 600; }\n [retire-error] { color: var(--md-sys-color-error); }\n @media (max-width: 1439.98px) {\n :host { position: fixed; inset: var(--figure-detail-top, 44px) 0 0 auto; height: calc(100dvh - var(--figure-detail-top, 44px)); z-index: 11; box-shadow: -4px 0 16px rgb(0 0 0 / 20%); }\n }\n `]\n @property({ attribute: false }) load: (id: string) => Promise<FigureDetailRecord> = async id => {\n const [saved, released] = await Promise.all([fetchFigure(id), fetchReleasedFigure(id)])\n if (!saved?.source) throw Error(i18next.t('figure.text.detail-no-source'))\n return { saved, released }\n }\n /**\n * Keeps the picture taken in the preview as the figure's card picture. A patch of the picture alone does not make a\n * released figure a draft (`updateFigure`). The catalogue is told, and reads its cards again.\n */\n @property({ attribute: false }) keepThumbnail: (id: string, dataUrl: string) => Promise<unknown> = async (id, dataUrl) => {\n await updateFigure(id, { thumbnail: dataUrl })\n window.dispatchEvent(new CustomEvent(FIGURE_THUMBNAIL_CHANGED, { detail: { id } }))\n }\n /** Retires a released figure or brings it back (`setFigureDeprecation`); a check gives its own. */\n @property({ attribute: false }) retire: (id: string, deprecated: boolean, supersededBy: string | null) => Promise<Figure> = setFigureDeprecation\n /** The types a retired figure may name instead: the domain's other figures. */\n @property({ attribute: false }) typeNamesOf: () => Promise<string[]> = fetchFigureTypeNames\n /** Makes a released figure public or private (`setFigurePublic`); a check gives its own. */\n @property({ attribute: false }) publish: (id: string, isPublic: boolean) => Promise<Figure> = setFigurePublic\n /** Another domain's public figure as the catalogue listed it, with its latest release; its draft is not this domain's to open. */\n @property({ attribute: false }) loadPublic: (row: Figure) => Promise<FigureDetailRecord> = async row => ({\n saved: row,\n released: await fetchReleasedFigure(row.id),\n foreign: true\n })\n @state() private publishError = ''\n @state() private figureId = ''\n @state() private record?: FigureDetailRecord\n @state() private asset?: FigureAsset\n @state() private sourceKind: 'saved' | 'released' = 'saved'\n @state() private loading = false\n @state() private error = ''\n @state() private cost?: ReturnType<typeof figureCostOf>\n @state() private retiring = false\n @state() private instead = ''\n @state() private typeNames: string[] = []\n @state() private retireError = ''\n @state() private retireBusy = false\n private ticket = 0\n private unsubscribe?: () => void\n private boundary?: Element\n private boundaryObserver?: ResizeObserver\n\n async show(id: string, publicRow?: Figure) {\n const ticket = ++this.ticket\n this.figureId = id\n this.record = undefined\n this.asset = undefined\n this.cost = undefined\n this.loading = true\n this.error = ''\n this.retiring = false\n this.retireError = ''\n this.publishError = ''\n try {\n const record = publicRow ? await this.loadPublic(publicRow) : await this.load(id)\n if (ticket !== this.ticket) return\n this.record = record\n this.choose(record.released ? 'released' : 'saved')\n } catch {\n if (ticket === this.ticket) this.error = i18next.t('figure.text.detail-load-failed')\n } finally {\n if (ticket === this.ticket) this.loading = false\n }\n }\n private choose(kind: 'saved' | 'released') {\n this.sourceKind = kind\n this.asset = undefined\n this.cost = undefined\n this.error = ''\n try {\n const source = kind === 'released' ? this.record?.released?.source : this.record?.saved.source\n if (!source) throw Error('missing source')\n const asset = parseFigureAsset(source)\n this.cost = figureCostOf(asset)\n this.asset = asset\n } catch {\n this.error = i18next.t('figure.text.detail-invalid-source')\n }\n }\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('resize', this.place)\n window.addEventListener('popstate', this.place)\n this.unsubscribe = store.subscribe(() => {\n const state = store.getState() as { layout?: { viewparts?: Record<string, { show?: boolean }> }; route?: { page?: string } }\n const parts = state.layout?.viewparts\n if (parts?.[VIEWPART]?.show && (parts?.[ASSISTANT_DOCK_VIEWPART]?.show || leftOpeningPage(openedOn, state.route?.page))) hideDetail()\n else requestAnimationFrame(this.place)\n })\n this.place()\n window.addEventListener('keydown', this.escape)\n }\n disconnectedCallback() {\n super.disconnectedCallback()\n window.removeEventListener('resize', this.place)\n window.removeEventListener('popstate', this.place)\n window.removeEventListener('keydown', this.escape)\n this.unsubscribe?.()\n this.boundaryObserver?.disconnect()\n this.boundary = undefined\n }\n private place = () => {\n const bar = visibleBar(document)\n if (bar !== this.boundary) {\n this.boundaryObserver?.disconnect()\n this.boundary = bar\n if (bar) {\n this.boundaryObserver = new ResizeObserver(this.place)\n this.boundaryObserver.observe(bar)\n }\n }\n this.style.setProperty('--figure-detail-top', `${bar?.getBoundingClientRect().bottom ?? 44}px`)\n }\n private escape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') hideDetail()\n }\n /**\n * Retiring a released figure (it stays released and served; boards already using it keep it), naming the type to use\n * instead, or bringing it back. A figure never released is deleted from the catalogue instead.\n */\n private retireRow(saved: Figure) {\n if (saved.deprecated) {\n return html`<div retire>\n <span retired>${saved.supersededBy\n ? i18next.t('figure.text.retired-instead', { type: saved.supersededBy })\n : i18next.t('figure.label.retired')}</span>\n <button ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, false)}>${i18next.t('figure.button.unretire')}</button>\n </div>\n ${this.retireError ? html`<p retire-error role=\"alert\">${this.retireError}</p>` : nothing}`\n }\n if (!this.retiring) {\n return html`<div retire><button @click=${() => this.openRetire(saved)}>${i18next.t('figure.button.retire')}</button></div>`\n }\n return html`<div retire>\n <input list=\"figure-retire-instead\" aria-label=${i18next.t('figure.label.superseded-by')}\n placeholder=${i18next.t('figure.label.superseded-by')} .value=${this.instead}\n @input=${(e: Event) => (this.instead = (e.target as HTMLInputElement).value)} />\n <datalist id=\"figure-retire-instead\">${this.typeNames.map(name => html`<option value=${name}></option>`)}</datalist>\n <button primary ?disabled=${this.retireBusy} @click=${() => this.setRetired(saved, true)}>${i18next.t('figure.button.retire')}</button>\n <button ?disabled=${this.retireBusy} @click=${() => { this.retiring = false; this.retireError = '' }}>${i18next.t('figure.button.cancel')}</button>\n </div>\n ${this.retireError ? html`<p retire-error role=\"alert\">${this.retireError}</p>` : nothing}`\n }\n /** Public to other domains, or not (G2): they read its releases and draw it by full name; its draft stays here. */\n private publishRow(saved: Figure) {\n return html`<div publish>\n ${saved.public ? html`<span published>${i18next.t('figure.label.public')}</span>` : nothing}\n <button ?disabled=${this.retireBusy} @click=${() => this.setPublic(saved, !saved.public)}>${i18next.t(saved.public ? 'figure.button.make-private' : 'figure.button.make-public')}</button>\n </div>\n ${this.publishError ? html`<p retire-error role=\"alert\">${this.publishError}</p>` : nothing}`\n }\n private async setPublic(saved: Figure, isPublic: boolean) {\n this.retireBusy = true\n this.publishError = ''\n try {\n const figure = await this.publish(saved.id, isPublic)\n if (this.record?.saved.id === saved.id) this.record = { ...this.record, saved: { ...this.record.saved, ...figure } }\n window.dispatchEvent(new CustomEvent(FIGURE_PUBLIC_CHANGED, { detail: { id: saved.id } }))\n } catch (e) {\n this.publishError = (e as Error).message\n } finally {\n this.retireBusy = false\n }\n }\n private async openRetire(saved: Figure) {\n this.retiring = true\n this.instead = ''\n this.retireError = ''\n try {\n this.typeNames = (await this.typeNamesOf()).filter(name => name !== saved.type)\n } catch {\n this.typeNames = []\n }\n }\n private async setRetired(saved: Figure, deprecated: boolean) {\n this.retireBusy = true\n this.retireError = ''\n try {\n const figure = await this.retire(saved.id, deprecated, deprecated ? this.instead.trim() || null : null)\n if (this.record?.saved.id === saved.id) this.record = { ...this.record, saved: { ...this.record.saved, ...figure } }\n this.retiring = false\n window.dispatchEvent(new CustomEvent(FIGURE_RETIREMENT_CHANGED, { detail: { id: saved.id } }))\n } catch (e) {\n this.retireError = (e as Error).message\n } finally {\n this.retireBusy = false\n }\n }\n render() {\n const saved = this.record?.saved\n const released = this.record?.released\n const cost = this.cost\n const capacity = cost ? capacityFrom(cost.groups, cost.triangles) : undefined\n const savedLabel = saved\n ? `${i18next.t('figure.label.detail-saved')} · ${i18next.t(saved.state === 'released' ? 'figure.label.figure-released' : 'figure.label.draft')}`\n : ''\n return html`<header>\n <div title-block><h2 title=${saved?.name || ''}>${saved?.name || i18next.t('figure.title.detail')}</h2>\n ${saved ? html`<code title=${fullName(saved)}>${fullName(saved)}</code>` : nothing}</div>\n ${this.record?.foreign ? nothing : html`<button icon edit aria-label=${i18next.t('figure.button.detail-edit')} title=${i18next.t('figure.button.detail-edit')} ?disabled=${!saved}\n @click=${() => {\n // The modeller shows the figure itself; a preview of it beside the editor only covers the work.\n hideDetail()\n navigate(modellerPathOf(saved))\n }}><md-icon>edit</md-icon></button>`}\n <button icon aria-label=${i18next.t('figure.button.close')} title=${i18next.t('figure.button.close')} @click=${() => hideDetail()}><md-icon>close</md-icon></button>\n </header>\n <div body>\n ${this.loading ? html`<p status role=\"status\">${i18next.t('figure.text.detail-loading')}</p>` : nothing}\n ${this.error ? html`<div status role=\"alert\"><p>${this.error}</p><button act @click=${() => this.show(this.figureId)}><md-icon>refresh</md-icon>${i18next.t('figure.button.detail-retry')}</button></div>` : nothing}\n ${saved ? html`<section meta>\n ${released && this.record?.foreign\n ? html`<div source><span>${i18next.t('figure.label.detail-released', { version: released.version })}</span>${saved.domain?.name ? html`<span owner>${saved.domain.name}</span>` : nothing}</div>`\n : released\n ? html`<div source role=\"group\" aria-label=${i18next.t('figure.label.detail-source')}>\n <button aria-pressed=${String(this.sourceKind === 'released')} @click=${() => this.choose('released')}>${i18next.t('figure.label.detail-released', { version: released.version })}</button>\n <button aria-pressed=${String(this.sourceKind === 'saved')} @click=${() => this.choose('saved')}>${savedLabel}</button>\n </div>`\n : html`<div source><span title=${i18next.t('figure.text.detail-unreleased')}>${savedLabel}</span></div>`}\n ${this.asset ? html`<dl stats>\n <div><dt>${i18next.t('figure.label.placement-limit')}</dt><dd>${capacity\n ? i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })\n : i18next.t('figure.text.not-measured-yet')}</dd></div>\n <div><dt>${i18next.t('figure.label.triangles')}</dt><dd>${cost?.triangles.toLocaleString() ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-groups')}</dt><dd>${cost?.groups ?? '—'}</dd></div>\n <div><dt>${i18next.t('figure.label.budget-parts')}</dt><dd>${this.asset.appearance.length}</dd></div>\n </dl>` : nothing}\n ${hasBeenReleased(saved) && !this.record?.foreign ? this.retireRow(saved) : nothing}\n ${hasBeenReleased(saved) && !this.record?.foreign ? this.publishRow(saved) : nothing}\n </section>` : nothing}\n ${this.asset ? html`<figure-asset-viewer .asset=${this.asset}\n .keepShot=${saved?.id && !this.record?.foreign ? (dataUrl: string) => this.keepThumbnail(saved.id, dataUrl) : undefined}>\n ${saved?.description ? html`<p slot=\"facts\" description>${saved.description}</p>` : nothing}\n ${saved ? html`<figure-facts-list slot=\"facts\" .asset=${this.asset} .row=${saved}></figure-facts-list>` : nothing}\n </figure-asset-viewer>` : nothing}\n </div>`\n }\n}\n\n/** The name a board stores: `{namespace}/{type}` (ADR-0114). */\nfunction fullName(figure: Figure): string {\n return figure.namespace ? `${figure.namespace}/${figure.type}` : figure.type\n}\n\nfunction visibleBar(root: Document | ShadowRoot): Element | undefined {\n for (const element of root.querySelectorAll('*')) {\n if (element.tagName === 'OX-CANVAS-BAR' && element.getBoundingClientRect().height > 0) return element\n if (element.shadowRoot) {\n const found = visibleBar(element.shadowRoot)\n if (found) return found\n }\n }\n}\n\nlet dock: FigureDetailDock | undefined\n/** The page the preview was opened on; the preview belongs to that page and closes when the shell leaves it. */\nlet openedOn: string | undefined\nfunction hideDetail() {\n store.dispatch({ type: UPDATE_VIEWPART, name: VIEWPART, override: { show: false } })\n}\n/** Opens the preview of a figure; `publicRow` for another domain's public figure, shown by its release alone. */\nexport function openFigureDetail(figure: Figure, publicRow = false) {\n if (!dock) {\n dock = document.createElement('figure-detail-dock') as FigureDetailDock\n }\n openedOn = (store.getState() as { route?: { page?: string } }).route?.page\n store.dispatch({ type: UPDATE_VIEWPART, name: ASSISTANT_DOCK_VIEWPART, override: { show: false } })\n appendViewpart({ name: VIEWPART, position: VIEWPART_POSITION.ASIDEBAR,\n viewpart: { show: true, persistent: true, level: VIEWPART_LEVEL.TOPMOST, template: html`${dock}` } })\n void dock.show(figure.id, publicRow ? figure : undefined)\n}\n"]}
@@ -27,6 +27,8 @@ declare const FigureThumb_base: (new (...args: any[]) => LitElement) & typeof Li
27
27
  export declare class FigureThumb extends FigureThumb_base {
28
28
  static styles: import("lit").CSSResult;
29
29
  figure: Figure;
30
+ /** Another domain's public figure (G2): its card says whose it is and offers no delete. */
31
+ readonly: boolean;
30
32
  render(): import("lit-html").TemplateResult<1>;
31
33
  }
32
34
  export {};
@@ -6,6 +6,7 @@ import { i18next, localize } from '@operato/i18n';
6
6
  import '@operato/data-grist/ox-card-thumb.js';
7
7
  import '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js';
8
8
  import { getPathInfo } from '@operato/utils';
9
+ import { hasBeenReleased } from '../figure-released.js';
9
10
  /**
10
11
  * 그림을 부르는 주소.
11
12
  *
@@ -45,6 +46,11 @@ function thumbnailSrc(figure) {
45
46
  * 위, 손이 갔을 때만 나오는 자리에 둔다.
46
47
  */
47
48
  let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
49
+ constructor() {
50
+ super(...arguments);
51
+ /** Another domain's public figure (G2): its card says whose it is and offers no delete. */
52
+ this.readonly = false;
53
+ }
48
54
  static { this.styles = css `
49
55
  :host {
50
56
  display: block;
@@ -150,7 +156,7 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
150
156
  <ox-card-thumb
151
157
  src=${src || ''}
152
158
  alt=${figure.name || ''}
153
- badge=${figure.state === 'released' ? '' : i18next.t('figure.label.draft')}
159
+ badge=${this.readonly ? figure.domain?.name ?? '' : figure.deprecated ? i18next.t('figure.label.retired') : figure.state === 'released' ? '' : i18next.t('figure.label.draft')}
154
160
  >
155
161
  <figure-thumb-mark slot="empty" .label=${i18next.t('figure.text.thumbnail-not-made-yet')}></figure-thumb-mark>
156
162
  ${figure.tags?.length
@@ -169,7 +175,7 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
169
175
  </div>`
170
176
  : ''}
171
177
 
172
- <button
178
+ ${this.readonly || hasBeenReleased(figure) ? '' : html `<button
173
179
  remove
174
180
  title=${i18next.t('figure.button.delete-figure')}
175
181
  @click=${(e) => {
@@ -179,7 +185,7 @@ let FigureThumb = class FigureThumb extends localize(i18next)(LitElement) {
179
185
  }}
180
186
  >
181
187
  <md-icon>delete</md-icon>
182
- </button>
188
+ </button>`}
183
189
  </ox-card-thumb>
184
190
  `;
185
191
  }
@@ -188,6 +194,10 @@ __decorate([
188
194
  property({ type: Object }),
189
195
  __metadata("design:type", Object)
190
196
  ], FigureThumb.prototype, "figure", void 0);
197
+ __decorate([
198
+ property({ type: Boolean }),
199
+ __metadata("design:type", Object)
200
+ ], FigureThumb.prototype, "readonly", void 0);
191
201
  FigureThumb = __decorate([
192
202
  customElement('figure-thumb')
193
203
  ], FigureThumb);