@things-factory/figure-library 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.
@@ -1,5 +1,5 @@
1
1
  import '@operato/data-grist';
2
- import '../viewparts/figure-card-meta.js';
2
+ import '@operato/data-grist/ox-card-thumb.js';
3
3
  import '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js';
4
4
  import '../viewparts/figure-releases-popup.js';
5
5
  import { PageView } from '@operato/shell';
@@ -23,7 +23,15 @@ export declare class FigureLibraryPage extends FigureLibraryPage_base {
23
23
  private fetchHandler;
24
24
  /** The filter options, from what the figures that draw carry. */
25
25
  private facets;
26
+ /** The picture cell every catalog card draws, with the one marker this figure needs. */
26
27
  private thumbnailOf;
28
+ /**
29
+ * The one thing a card says before it is opened, the gravest first: nothing here can draw it, it does not draw its
30
+ * latest release, or the release it draws is deprecated. A figure drawing its latest says nothing.
31
+ */
32
+ private markerOf;
33
+ /** The line under the name: the full name and the version it draws with, as a board card's 「v1 · 3일 전」. */
34
+ private metaOf;
27
35
  private openReleases;
28
36
  private chooseFile;
29
37
  private importFile;
@@ -1,8 +1,9 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import '@operato/data-grist';
3
- import '../viewparts/figure-card-meta.js';
3
+ import '@operato/data-grist/ox-card-thumb.js';
4
4
  import '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js';
5
5
  import '../viewparts/figure-releases-popup.js';
6
+ import { versionText } from '../viewparts/figure-version.js';
6
7
  import gql from 'graphql-tag';
7
8
  import { css, html, nothing } from 'lit';
8
9
  import { customElement, query, state } from 'lit/decorators.js';
@@ -11,16 +12,17 @@ import { client } from '@operato/graphql';
11
12
  import { i18next, localize } from '@operato/i18n';
12
13
  import { openPopup } from '@operato/popup';
13
14
  import { PageView } from '@operato/shell';
14
- import { CommonGristStyles, CommonHeaderStyles, FabStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
15
+ import { CardCatalogStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles';
15
16
  import { getPathInfo } from '@operato/utils';
16
17
  /*
17
- * The board server's figures (ADR-0114, amended 2026-10-12), as a card catalogue: one card per imported figure, its
18
- * thumbnail first, the release it draws with and the latest imported. A card whose figure does not draw with its latest
19
- * says so. Clicking a card opens its releases side by side to compare them and choose the one it draws with.
18
+ * The board server's figures (ADR-0114, amended 2026-10-12), as a card catalogue with the board catalog's card: the
19
+ * thumbnail, the name, and one line - the full name and the version it draws with. The one thing to know before opening
20
+ * a card is its corner marker: cannot be read here, not drawing the latest, or deprecated. Clicking a card opens its
21
+ * releases side by side to compare them and choose the one it draws with.
20
22
  *
21
23
  * The library is expected to grow to many figures, so the list pages from the server and the filters (category,
22
- * capability) are drop-down lists in the filter bar beside the name search. Upload, the decision of the screen, sits in
23
- * the shell's action bar; what it did opens in a popup.
24
+ * capability) are drop-down lists in the filter bar beside the name search. Upload, the screen's one primary action, is
25
+ * the FAB; what it did opens in a popup.
24
26
  */
25
27
  const FIGURES = gql `
26
28
  query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
@@ -124,19 +126,12 @@ let FigureLibraryPage = class FigureLibraryPage extends localize(i18next)(PageVi
124
126
  ScrollbarStyles,
125
127
  CommonGristStyles,
126
128
  CommonHeaderStyles,
127
- FabStyles,
129
+ /* The card, its picture cell and the upload FAB: every card catalog's (ADR-0117 amendment 2026-10-13). */
130
+ CardCatalogStyles,
128
131
  css `
129
132
  ox-filters-form {
130
133
  flex: 1;
131
134
  }
132
-
133
- ox-grist {
134
- /* Cards of a row stand at one height; a card with an extra fact row does not stick out below the others. */
135
- --data-card-record-card-height: auto;
136
- --data-card-thumbnail-aspect: 4 / 3;
137
- --data-card-thumbnail-background: var(--md-sys-color-surface-container-lowest);
138
- }
139
-
140
135
  `
141
136
  ]; }
142
137
  get context() {
@@ -197,7 +192,7 @@ let FigureLibraryPage = class FigureLibraryPage extends localize(i18next)(PageVi
197
192
  {
198
193
  type: 'string',
199
194
  name: 'meta',
200
- record: { editable: false, renderer: (_value, _column, record) => html `<figure-card-meta .figure=${record}></figure-card-meta>` }
195
+ record: { editable: false, renderer: (_value, _column, record) => this.metaOf(record) }
201
196
  },
202
197
  { type: 'string', name: 'name', hidden: true, record: { editable: false }, filter: 'search' },
203
198
  {
@@ -246,12 +241,37 @@ let FigureLibraryPage = class FigureLibraryPage extends localize(i18next)(PageVi
246
241
  ]
247
242
  };
248
243
  }
244
+ /** The picture cell every catalog card draws, with the one marker this figure needs. */
249
245
  thumbnailOf(record) {
250
246
  const hash = (record?.release ?? record?.latest)?.dependencies?.find((d) => d.role === 'thumbnail')?.hash;
251
- if (!hash)
252
- return html `<figure-thumb-mark .label=${i18next.t('figure-library.text.no-thumbnail')}></figure-thumb-mark>`;
253
247
  const { contextPath } = getPathInfo(location.pathname);
254
- return html `<img alt="" src=${`${contextPath}/figure-library-file/${hash}`} />`;
248
+ const [badge, tone] = this.markerOf(record);
249
+ return html `<ox-card-thumb
250
+ src=${hash ? `${contextPath}/figure-library-file/${hash}` : ''}
251
+ alt=${(record?.release ?? record?.latest)?.name ?? ''}
252
+ badge=${badge}
253
+ tone=${tone}
254
+ >
255
+ <figure-thumb-mark slot="empty" .label=${i18next.t('figure-library.text.no-thumbnail')}></figure-thumb-mark>
256
+ </ox-card-thumb>`;
257
+ }
258
+ /**
259
+ * The one thing a card says before it is opened, the gravest first: nothing here can draw it, it does not draw its
260
+ * latest release, or the release it draws is deprecated. A figure drawing its latest says nothing.
261
+ */
262
+ markerOf(record) {
263
+ const release = record?.release;
264
+ if (!release)
265
+ return [i18next.t('figure-library.text.format-unsupported'), 'crit'];
266
+ if (release.version !== record.latestVersion)
267
+ return [i18next.t('figure-library.text.not-latest'), 'warn'];
268
+ if (release.deprecated)
269
+ return [i18next.t('figure-library.text.deprecated'), 'plain'];
270
+ return ['', 'plain'];
271
+ }
272
+ /** The line under the name: the full name and the version it draws with, as a board card's 「v1 · 3일 전」. */
273
+ metaOf(record) {
274
+ return record?.release ? `${record.name} · ${versionText(record.release.version)}` : record?.name ?? '';
255
275
  }
256
276
  openReleases(record) {
257
277
  openPopup(html `<figure-releases-popup .name=${record.name} .drawing=${record.release?.id ?? ''} @saved=${() => this.grist.fetch()}></figure-releases-popup>`, {
@@ -1 +1 @@
1
- {"version":3,"file":"figure-library-page.js","sourceRoot":"","sources":["../../client/pages/figure-library-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist'\nimport '../viewparts/figure-card-meta.js'\nimport '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js'\nimport '../viewparts/figure-releases-popup.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/popup'\nimport { PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, FabStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { getPathInfo } from '@operato/utils'\n\n/*\n * The board server's figures (ADR-0114, amended 2026-10-12), as a card catalogue: one card per imported figure, its\n * thumbnail first, the release it draws with and the latest imported. A card whose figure does not draw with its latest\n * says so. Clicking a card opens its releases side by side to compare them and choose the one it draws with.\n *\n * The library is expected to grow to many figures, so the list pages from the server and the filters (category,\n * capability) are drop-down lists in the filter bar beside the name search. Upload, the decision of the screen, sits in\n * the shell's action bar; what it did opens in a popup.\n */\n\nconst FIGURES = gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n responses: figureLibraryFigures(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n name\n latestVersion\n releaseCount\n release {\n id\n name\n version\n category\n capabilities\n formatSupported\n deprecated\n dependencies\n }\n latest {\n id\n name\n version\n formatSupported\n dependencies\n }\n }\n total\n }\n }\n`\n\nconst FACETS = gql`\n query {\n figureLibraryFacets {\n categories\n uncategorized\n capabilities\n incapable\n }\n }\n`\n\nconst IMPORT = gql`\n mutation ($file: Upload!) {\n importFigurePackage(file: $file) {\n problems {\n code\n path\n }\n outcomes {\n ref\n outcome\n code\n differs\n }\n }\n }\n`\n\n/** The filter value for a figure with no category, or one that declares no capability. */\nconst NONE = '-'\n\ntype ImportResult = { problems: { code: string; path?: string }[]; outcomes: { ref: string; outcome: string; code?: string; differs?: string[] }[] }\n\n@customElement('figure-library-page')\nexport class FigureLibraryPage extends localize(i18next)(PageView) {\n static styles = [\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n FabStyles,\n css`\n ox-filters-form {\n flex: 1;\n }\n\n ox-grist {\n /* Cards of a row stand at one height; a card with an extra fact row does not stick out below the others. */\n --data-card-record-card-height: auto;\n --data-card-thumbnail-aspect: 4 / 3;\n --data-card-thumbnail-background: var(--md-sys-color-surface-container-lowest);\n }\n\n `\n ]\n\n @state() private gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('figure-library.title'),\n search: {\n placeholder: i18next.t('figure-library.text.search'),\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n }\n }\n }\n\n render() {\n return html`\n <input id=\"file\" type=\"file\" accept=\".figpkg,.zip\" hidden @change=${this.importFile} />\n <ox-grist class=\"page-fill\" mode=\"CARD\" .config=${this.gristConfig} .fetchHandler=${this.fetchHandler}>\n <div slot=\"headroom\">\n <ox-filters-form autofocus without-search></ox-filters-form>\n </div>\n </ox-grist>\n\n <button fab @click=${() => this.chooseFile()}>\n <md-icon>upload_file</md-icon>${i18next.t('figure-library.button.upload')}\n </button>\n `\n }\n\n async pageInitialized() {\n await this.configure()\n }\n\n /*\n * The filter options are read once here (and again after an import), never per render: an async options function\n * runs on every render of the filter bar and replaces the open list's options under the pointer.\n */\n private async configure() {\n const { categories, capabilities } = await this.facets()\n this.gristConfig = {\n list: {\n thumbnail: 'thumbnail',\n fields: ['title', 'meta'],\n details: []\n },\n columns: [\n {\n type: 'image',\n name: 'thumbnail',\n record: { editable: false, renderEmpty: true, renderer: (_value, _column, record) => this.thumbnailOf(record) }\n },\n {\n type: 'string',\n name: 'title',\n record: {\n editable: false,\n renderer: (_value, _column, record) => (record.release ?? record.latest).name\n }\n },\n {\n type: 'string',\n name: 'meta',\n record: { editable: false, renderer: (_value, _column, record) => html`<figure-card-meta .figure=${record}></figure-card-meta>` }\n },\n { type: 'string', name: 'name', hidden: true, record: { editable: false }, filter: 'search' },\n {\n type: 'select',\n name: 'category',\n header: i18next.t('figure-library.label.category'),\n hidden: true,\n record: { editable: false },\n filter: { operator: 'eq', options: categories }\n },\n {\n type: 'select',\n name: 'capability',\n header: i18next.t('figure-library.label.capability'),\n hidden: true,\n record: { editable: false },\n filter: { operator: 'eq', options: capabilities }\n }\n ],\n rows: {\n selectable: false,\n appendable: false,\n handlers: {\n click: (_columns, _data, _column, record) => record && this.openReleases(record)\n }\n },\n pagination: { pages: [24, 48, 96] }\n }\n }\n\n async pageUpdated() {\n if (this.active) this.grist?.fetch()\n }\n\n private fetchHandler = async ({ page = 1, limit = 24, filters = [] }: FetchOption) => {\n const response = await client.query({\n query: FIGURES,\n variables: {\n /* The name search arrives as %text%; the server matches parts itself. */\n filters: filters\n .filter(filter => filter.value !== undefined && filter.value !== '' && !(Array.isArray(filter.value) && !filter.value.length))\n .map(filter => ({ ...filter, value: typeof filter.value === 'string' ? filter.value.replace(/^%|%$/g, '') : filter.value })),\n pagination: { page, limit }\n },\n fetchPolicy: 'network-only'\n })\n return { total: response.data.responses.total || 0, records: response.data.responses.items || [] }\n }\n\n /** The filter options, from what the figures that draw carry. */\n private async facets() {\n const response = await client.query({ query: FACETS, fetchPolicy: 'network-only' })\n const facets = response.data?.figureLibraryFacets ?? { categories: [], capabilities: [] }\n return {\n categories: [\n ...facets.categories.map((value: string) => ({ display: value, value })),\n ...(facets.uncategorized ? [{ display: i18next.t('figure-library.text.no-category'), value: NONE }] : [])\n ],\n capabilities: [\n ...facets.capabilities.map((value: string) => ({ display: i18next.t(`figure.label.role-${value}`, { defaultValue: value }), value })),\n ...(facets.incapable ? [{ display: i18next.t('figure-library.text.no-capability'), value: NONE }] : [])\n ]\n }\n }\n\n private thumbnailOf(record: any) {\n const hash = (record?.release ?? record?.latest)?.dependencies?.find((d: any) => d.role === 'thumbnail')?.hash\n if (!hash) return html`<figure-thumb-mark .label=${i18next.t('figure-library.text.no-thumbnail')}></figure-thumb-mark>`\n const { contextPath } = getPathInfo(location.pathname)\n return html`<img alt=\"\" src=${`${contextPath}/figure-library-file/${hash}`} />`\n }\n\n private openReleases(record: any) {\n openPopup(\n html`<figure-releases-popup .name=${record.name} .drawing=${record.release?.id ?? ''} @saved=${() => this.grist.fetch()}></figure-releases-popup>`,\n {\n backdrop: true,\n size: 'large',\n title: i18next.t('figure-library.title.releases-of', { name: (record.release ?? record.latest).name, count: record.releaseCount })\n }\n )\n }\n\n private chooseFile() {\n ;(this.renderRoot.querySelector('#file') as HTMLInputElement | null)?.click()\n }\n\n private importFile = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const file = input.files?.[0]\n if (!file) return\n let result: ImportResult\n try {\n const response = await client.mutate({ mutation: IMPORT, variables: { file }, context: { hasUpload: true } })\n if (response.errors?.length) throw new Error(response.errors[0].message)\n result = response.data.importFigurePackage\n } catch (e: any) {\n result = { problems: [{ code: e?.message ?? 'error' }], outcomes: [] }\n } finally {\n input.value = ''\n }\n await this.configure()\n this.grist.fetch()\n openPopup(this.renderImported(result), { backdrop: true, size: 'small', closable: true, title: i18next.t('figure-library.title.import-result') })\n }\n\n private renderImported(result: ImportResult) {\n return html`\n <div style=\"padding: 16px; display: flex; flex-direction: column; gap: 8px; font-size: var(--ui-t-body, 13px);\">\n ${result.problems.length\n ? html`<p style=\"margin: 0; color: var(--ui-crit, var(--md-sys-color-error));\" role=\"alert\">\n ${i18next.t('figure-library.text.package-refused', { code: result.problems.map(p => p.code).join(', ') })}\n </p>`\n : result.outcomes.map(\n o => html`<div>\n <span style=\"font-family: var(--ui-mono, monospace);\">${o.ref}</span> ·\n <strong style=${o.outcome === 'refused' ? 'color: var(--ui-crit, var(--md-sys-color-error))' : ''}>${i18next.t(`figure-library.outcome.${o.outcome}`)}</strong>\n ${o.differs?.length ? html`<div>${i18next.t('figure-library.text.differs', { fields: o.differs.join(', ') })}</div>` : nothing}\n ${o.outcome === 'refused' && o.code ? html`<div>${o.code}</div>` : nothing}\n </div>`\n )}\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"figure-library-page.js","sourceRoot":"","sources":["../../client/pages/figure-library-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist'\nimport '@operato/data-grist/ox-card-thumb.js'\nimport '@things-factory/figure-viewer/dist-client/figure-thumb-mark.js'\nimport '../viewparts/figure-releases-popup.js'\nimport { versionText } from '../viewparts/figure-version.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport { customElement, query, state } from 'lit/decorators.js'\n\nimport { DataGrist, FetchOption } from '@operato/data-grist'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { openPopup } from '@operato/popup'\nimport { PageView } from '@operato/shell'\nimport { CardCatalogStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { getPathInfo } from '@operato/utils'\n\n/*\n * The board server's figures (ADR-0114, amended 2026-10-12), as a card catalogue with the board catalog's card: the\n * thumbnail, the name, and one line - the full name and the version it draws with. The one thing to know before opening\n * a card is its corner marker: cannot be read here, not drawing the latest, or deprecated. Clicking a card opens its\n * releases side by side to compare them and choose the one it draws with.\n *\n * The library is expected to grow to many figures, so the list pages from the server and the filters (category,\n * capability) are drop-down lists in the filter bar beside the name search. Upload, the screen's one primary action, is\n * the FAB; what it did opens in a popup.\n */\n\nconst FIGURES = gql`\n query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {\n responses: figureLibraryFigures(filters: $filters, pagination: $pagination, sortings: $sortings) {\n items {\n name\n latestVersion\n releaseCount\n release {\n id\n name\n version\n category\n capabilities\n formatSupported\n deprecated\n dependencies\n }\n latest {\n id\n name\n version\n formatSupported\n dependencies\n }\n }\n total\n }\n }\n`\n\nconst FACETS = gql`\n query {\n figureLibraryFacets {\n categories\n uncategorized\n capabilities\n incapable\n }\n }\n`\n\nconst IMPORT = gql`\n mutation ($file: Upload!) {\n importFigurePackage(file: $file) {\n problems {\n code\n path\n }\n outcomes {\n ref\n outcome\n code\n differs\n }\n }\n }\n`\n\n/** The filter value for a figure with no category, or one that declares no capability. */\nconst NONE = '-'\n\ntype ImportResult = { problems: { code: string; path?: string }[]; outcomes: { ref: string; outcome: string; code?: string; differs?: string[] }[] }\n\n@customElement('figure-library-page')\nexport class FigureLibraryPage extends localize(i18next)(PageView) {\n static styles = [\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n /* The card, its picture cell and the upload FAB: every card catalog's (ADR-0117 amendment 2026-10-13). */\n CardCatalogStyles,\n css`\n ox-filters-form {\n flex: 1;\n }\n `\n ]\n\n @state() private gristConfig: any\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('figure-library.title'),\n search: {\n placeholder: i18next.t('figure-library.text.search'),\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || ''\n }\n }\n }\n\n render() {\n return html`\n <input id=\"file\" type=\"file\" accept=\".figpkg,.zip\" hidden @change=${this.importFile} />\n <ox-grist class=\"page-fill\" mode=\"CARD\" .config=${this.gristConfig} .fetchHandler=${this.fetchHandler}>\n <div slot=\"headroom\">\n <ox-filters-form autofocus without-search></ox-filters-form>\n </div>\n </ox-grist>\n\n <button fab @click=${() => this.chooseFile()}>\n <md-icon>upload_file</md-icon>${i18next.t('figure-library.button.upload')}\n </button>\n `\n }\n\n async pageInitialized() {\n await this.configure()\n }\n\n /*\n * The filter options are read once here (and again after an import), never per render: an async options function\n * runs on every render of the filter bar and replaces the open list's options under the pointer.\n */\n private async configure() {\n const { categories, capabilities } = await this.facets()\n this.gristConfig = {\n list: {\n thumbnail: 'thumbnail',\n fields: ['title', 'meta'],\n details: []\n },\n columns: [\n {\n type: 'image',\n name: 'thumbnail',\n record: { editable: false, renderEmpty: true, renderer: (_value, _column, record) => this.thumbnailOf(record) }\n },\n {\n type: 'string',\n name: 'title',\n record: {\n editable: false,\n renderer: (_value, _column, record) => (record.release ?? record.latest).name\n }\n },\n {\n type: 'string',\n name: 'meta',\n record: { editable: false, renderer: (_value, _column, record) => this.metaOf(record) }\n },\n { type: 'string', name: 'name', hidden: true, record: { editable: false }, filter: 'search' },\n {\n type: 'select',\n name: 'category',\n header: i18next.t('figure-library.label.category'),\n hidden: true,\n record: { editable: false },\n filter: { operator: 'eq', options: categories }\n },\n {\n type: 'select',\n name: 'capability',\n header: i18next.t('figure-library.label.capability'),\n hidden: true,\n record: { editable: false },\n filter: { operator: 'eq', options: capabilities }\n }\n ],\n rows: {\n selectable: false,\n appendable: false,\n handlers: {\n click: (_columns, _data, _column, record) => record && this.openReleases(record)\n }\n },\n pagination: { pages: [24, 48, 96] }\n }\n }\n\n async pageUpdated() {\n if (this.active) this.grist?.fetch()\n }\n\n private fetchHandler = async ({ page = 1, limit = 24, filters = [] }: FetchOption) => {\n const response = await client.query({\n query: FIGURES,\n variables: {\n /* The name search arrives as %text%; the server matches parts itself. */\n filters: filters\n .filter(filter => filter.value !== undefined && filter.value !== '' && !(Array.isArray(filter.value) && !filter.value.length))\n .map(filter => ({ ...filter, value: typeof filter.value === 'string' ? filter.value.replace(/^%|%$/g, '') : filter.value })),\n pagination: { page, limit }\n },\n fetchPolicy: 'network-only'\n })\n return { total: response.data.responses.total || 0, records: response.data.responses.items || [] }\n }\n\n /** The filter options, from what the figures that draw carry. */\n private async facets() {\n const response = await client.query({ query: FACETS, fetchPolicy: 'network-only' })\n const facets = response.data?.figureLibraryFacets ?? { categories: [], capabilities: [] }\n return {\n categories: [\n ...facets.categories.map((value: string) => ({ display: value, value })),\n ...(facets.uncategorized ? [{ display: i18next.t('figure-library.text.no-category'), value: NONE }] : [])\n ],\n capabilities: [\n ...facets.capabilities.map((value: string) => ({ display: i18next.t(`figure.label.role-${value}`, { defaultValue: value }), value })),\n ...(facets.incapable ? [{ display: i18next.t('figure-library.text.no-capability'), value: NONE }] : [])\n ]\n }\n }\n\n /** The picture cell every catalog card draws, with the one marker this figure needs. */\n private thumbnailOf(record: any) {\n const hash = (record?.release ?? record?.latest)?.dependencies?.find((d: any) => d.role === 'thumbnail')?.hash\n const { contextPath } = getPathInfo(location.pathname)\n const [badge, tone] = this.markerOf(record)\n return html`<ox-card-thumb\n src=${hash ? `${contextPath}/figure-library-file/${hash}` : ''}\n alt=${(record?.release ?? record?.latest)?.name ?? ''}\n badge=${badge}\n tone=${tone}\n >\n <figure-thumb-mark slot=\"empty\" .label=${i18next.t('figure-library.text.no-thumbnail')}></figure-thumb-mark>\n </ox-card-thumb>`\n }\n\n /**\n * The one thing a card says before it is opened, the gravest first: nothing here can draw it, it does not draw its\n * latest release, or the release it draws is deprecated. A figure drawing its latest says nothing.\n */\n private markerOf(record: any): [string, 'plain' | 'warn' | 'crit'] {\n const release = record?.release\n if (!release) return [i18next.t('figure-library.text.format-unsupported'), 'crit']\n if (release.version !== record.latestVersion) return [i18next.t('figure-library.text.not-latest'), 'warn']\n if (release.deprecated) return [i18next.t('figure-library.text.deprecated'), 'plain']\n return ['', 'plain']\n }\n\n /** The line under the name: the full name and the version it draws with, as a board card's 「v1 · 3일 전」. */\n private metaOf(record: any): string {\n return record?.release ? `${record.name} · ${versionText(record.release.version)}` : record?.name ?? ''\n }\n\n private openReleases(record: any) {\n openPopup(\n html`<figure-releases-popup .name=${record.name} .drawing=${record.release?.id ?? ''} @saved=${() => this.grist.fetch()}></figure-releases-popup>`,\n {\n backdrop: true,\n size: 'large',\n title: i18next.t('figure-library.title.releases-of', { name: (record.release ?? record.latest).name, count: record.releaseCount })\n }\n )\n }\n\n private chooseFile() {\n ;(this.renderRoot.querySelector('#file') as HTMLInputElement | null)?.click()\n }\n\n private importFile = async (event: Event) => {\n const input = event.target as HTMLInputElement\n const file = input.files?.[0]\n if (!file) return\n let result: ImportResult\n try {\n const response = await client.mutate({ mutation: IMPORT, variables: { file }, context: { hasUpload: true } })\n if (response.errors?.length) throw new Error(response.errors[0].message)\n result = response.data.importFigurePackage\n } catch (e: any) {\n result = { problems: [{ code: e?.message ?? 'error' }], outcomes: [] }\n } finally {\n input.value = ''\n }\n await this.configure()\n this.grist.fetch()\n openPopup(this.renderImported(result), { backdrop: true, size: 'small', closable: true, title: i18next.t('figure-library.title.import-result') })\n }\n\n private renderImported(result: ImportResult) {\n return html`\n <div style=\"padding: 16px; display: flex; flex-direction: column; gap: 8px; font-size: var(--ui-t-body, 13px);\">\n ${result.problems.length\n ? html`<p style=\"margin: 0; color: var(--ui-crit, var(--md-sys-color-error));\" role=\"alert\">\n ${i18next.t('figure-library.text.package-refused', { code: result.problems.map(p => p.code).join(', ') })}\n </p>`\n : result.outcomes.map(\n o => html`<div>\n <span style=\"font-family: var(--ui-mono, monospace);\">${o.ref}</span> ·\n <strong style=${o.outcome === 'refused' ? 'color: var(--ui-crit, var(--md-sys-color-error))' : ''}>${i18next.t(`figure-library.outcome.${o.outcome}`)}</strong>\n ${o.differs?.length ? html`<div>${i18next.t('figure-library.text.differs', { fields: o.differs.join(', ') })}</div>` : nothing}\n ${o.outcome === 'refused' && o.code ? html`<div>${o.code}</div>` : nothing}\n </div>`\n )}\n </div>\n `\n }\n}\n"]}