@things-factory/figure-ui 10.1.192 → 10.1.194

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.
@@ -51,6 +51,7 @@ export declare class FigureDetailDock extends FigureDetailDock_base {
51
51
  private retireBusy;
52
52
  private ticket;
53
53
  private unsubscribe?;
54
+ private stopAddress?;
54
55
  private boundary?;
55
56
  private boundaryObserver?;
56
57
  show(id: string, publicRow?: Figure): Promise<void>;
@@ -5,6 +5,7 @@ import { customElement, property, state } from 'lit/decorators.js';
5
5
  import { i18next, localize } from '@operato/i18n';
6
6
  import { appendViewpart, UPDATE_VIEWPART, VIEWPART_POSITION, VIEWPART_LEVEL } from '@operato/layout';
7
7
  import { navigate, store } from '@operato/shell';
8
+ import { addressController } from '@operato/utils/address.js';
8
9
  import { ScrollbarStyles } from '@operato/styles';
9
10
  import { ASSISTANT_DOCK_VIEWPART } from '@things-factory/ai-assistant/dist-client/components/assistant-dock.js';
10
11
  import { figureCostOf, parseFigureAsset } from '@hatiolab/figure-model';
@@ -183,7 +184,8 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
183
184
  connectedCallback() {
184
185
  super.connectedCallback();
185
186
  window.addEventListener('resize', this.place);
186
- window.addEventListener('popstate', this.place);
187
+ /* Every committed address, a link or a navigate() as much as a traversal - the address layer fires no popstate for its own writes. */
188
+ this.stopAddress = addressController().subscribe(() => this.place(), false);
187
189
  this.unsubscribe = store.subscribe(() => {
188
190
  const state = store.getState();
189
191
  const parts = state.layout?.viewparts;
@@ -198,7 +200,7 @@ let FigureDetailDock = class FigureDetailDock extends localize(i18next)(LitEleme
198
200
  disconnectedCallback() {
199
201
  super.disconnectedCallback();
200
202
  window.removeEventListener('resize', this.place);
201
- window.removeEventListener('popstate', this.place);
203
+ this.stopAddress?.();
202
204
  window.removeEventListener('keydown', this.escape);
203
205
  this.unsubscribe?.();
204
206
  this.boundaryObserver?.disconnect();
@@ -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, 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"]}
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 { addressController } from '@operato/utils/address.js'\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 stopAddress?: () => 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 /* Every committed address, a link or a navigate() as much as a traversal - the address layer fires no popstate for its own writes. */\n this.stopAddress = addressController().subscribe(() => this.place(), false)\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 this.stopAddress?.()\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/figure-ui",
3
- "version": "10.1.192",
3
+ "version": "10.1.194",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "@things-factory:registry": "https://registry.npmjs.org"
@@ -18,7 +18,7 @@
18
18
  "scripts": {
19
19
  "build": "npm run build:server && npm run build:client",
20
20
  "test": "FIGURE_FREEZE_COMPILED=1 node --import ./test/resolve-ts-siblings.mjs --test test/*.test.ts",
21
- "test:browser": "node --test test/browser/build.check.mjs test/browser/modeller.check.mjs test/browser/modeller-revision.check.mjs test/browser/editing.check.mjs test/browser/links.check.mjs test/browser/section.check.mjs test/browser/screen.check.mjs test/browser/product-page.check.mjs test/browser/parts.check.mjs test/browser/places-mesh.check.mjs test/browser/part-ids.check.mjs test/browser/fix-alignment.check.mjs test/browser/mesh-select.check.mjs test/browser/row-preview.check.mjs test/browser/detail-tabs.check.mjs test/browser/props.check.mjs test/browser/retire.check.mjs test/browser/namespace.check.mjs test/browser/public.check.mjs test/browser/category.check.mjs test/browser/property-section.check.mjs",
21
+ "test:browser": "node --test test/browser/build.check.mjs test/browser/modeller.check.mjs test/browser/modeller-revision.check.mjs test/browser/editing.check.mjs test/browser/links.check.mjs test/browser/section.check.mjs test/browser/screen.check.mjs test/browser/product-page.check.mjs test/browser/parts.check.mjs test/browser/places-mesh.check.mjs test/browser/part-ids.check.mjs test/browser/fix-alignment.check.mjs test/browser/mesh-select.check.mjs test/browser/row-preview.check.mjs test/browser/detail-tabs.check.mjs test/browser/props.check.mjs test/browser/retire.check.mjs test/browser/namespace.check.mjs test/browser/public.check.mjs test/browser/category.check.mjs test/browser/property-section.check.mjs test/browser/list.check.mjs",
22
22
  "copy:files": "copyfiles -e \"./client/**/*.{ts,js,json}\" -u 1 \"./client/**/*\" dist-client",
23
23
  "build:client": "npm run copy:files && npx tsc --p ./client/tsconfig.json",
24
24
  "build:server": "npx tsc --p ./server/tsconfig.json",
@@ -30,23 +30,23 @@
30
30
  "@hatiolab/figure-model": "^0.7.2",
31
31
  "@hatiolab/things-scene": "^10.1.92",
32
32
  "@material/web": "^2.0.0",
33
- "@operato/data-grist": "^10.29.39",
34
- "@operato/graphql": "^10.29.39",
33
+ "@operato/data-grist": "^10.29.43",
34
+ "@operato/graphql": "^10.29.43",
35
35
  "@operato/i18n": "^10.0.0",
36
- "@operato/input": "^10.29.41",
37
- "@operato/layout": "^10.29.39",
36
+ "@operato/input": "^10.29.43",
37
+ "@operato/layout": "^10.29.43",
38
38
  "@operato/markdown": "^10.23.0",
39
- "@operato/p13n": "^10.29.39",
40
- "@operato/popup": "^10.29.39",
41
- "@operato/shell": "^10.29.39",
42
- "@operato/slot-layout-editor": "^10.29.39",
39
+ "@operato/p13n": "^10.29.43",
40
+ "@operato/popup": "^10.29.43",
41
+ "@operato/shell": "^10.29.43",
42
+ "@operato/slot-layout-editor": "^10.29.43",
43
43
  "@operato/styles": "^10.0.0",
44
- "@operato/utils": "^10.29.39",
44
+ "@operato/utils": "^10.29.43",
45
45
  "@things-factory/ai-assistant": "^10.1.192",
46
46
  "@things-factory/figure-service": "^10.1.192",
47
47
  "@things-factory/figure-viewer": "^10.1.185",
48
48
  "@things-factory/setting-base": "^10.1.192",
49
49
  "three": "^0.185.1"
50
50
  },
51
- "gitHead": "c44fe3ae4646dc4ffad7947785322acdb84c7374"
51
+ "gitHead": "489dd836ae99063bfed6706367ea1d7cd7ed004a"
52
52
  }
@@ -0,0 +1,35 @@
1
+ /*
2
+ * The catalogue and the public figures, as the rail opens them (user 2026-10-19): the domain's own figures with the
3
+ * new-figure button and the draft · released choice, and other domains' public figures with neither. The server is a
4
+ * stand-in that answers each list with one card and says which list was asked for.
5
+ */
6
+ import 'figure-ui-client/pages/figure-list-page.js'
7
+ import 'figure-ui-client/pages/figure-public-list-page.js'
8
+ import { client } from '@operato/graphql'
9
+
10
+ const asked = []
11
+ const card = { id: 'one', name: 'Mixer', type: 'mixer', namespace: 'acme.example', state: 'released', version: 1, public: true, tags: [] }
12
+ client.query = async ({ query }) => {
13
+ const field = query.definitions.find(d => d.kind === 'OperationDefinition')?.selectionSet.selections[0]?.name.value
14
+ asked.push(field)
15
+ if (field === 'figures' || field === 'publicFigures')
16
+ return { data: { [field]: { items: [field === 'publicFigures' ? { ...card, domain: { id: 'd2', name: 'Other works', subdomain: 'other' } } : card], total: 1 } } }
17
+ /* No kept preferences: the page as it first opens. */
18
+ if (field === 'myPageAllPreferences') return { data: { myPageAllPreferences: [] } }
19
+ return { data: {} }
20
+ }
21
+ client.mutate = async () => ({ data: {} })
22
+
23
+ window.harness = {
24
+ asked,
25
+ mount(tag) {
26
+ document.body.replaceChildren()
27
+ const page = document.createElement(tag)
28
+ page.style.cssText = 'display:block;height:800px'
29
+ /* What the shell gives a page it routes to: the page's name, read for its kept preferences. */
30
+ page.lifecycle = { page: tag === 'figure-public-list-page' ? 'figure-public-list' : 'figure-list' }
31
+ document.body.append(page)
32
+ page.active = true
33
+ return page.localName
34
+ }
35
+ }
@@ -0,0 +1,68 @@
1
+ /*
2
+ * The rail's two lists (user 2026-10-19): the catalogue is where figures are made and changed, the public figures of
3
+ * other domains only looked at. Each is a screen of its own; the catalogue has no switch between them, and the public
4
+ * list offers no new figure and no draft · released choice.
5
+ *
6
+ * Run: node --test test/browser/list.check.mjs
7
+ */
8
+ import { test, describe, before, after } from 'node:test'
9
+ import assert from 'node:assert/strict'
10
+
11
+ import { buildPage } from './build.mjs'
12
+ import { openPage } from './serve.mjs'
13
+
14
+ const sleep = ms => new Promise(done => setTimeout(done, ms))
15
+
16
+ let opened
17
+ before(async () => {
18
+ opened = await openPage(await buildPage('list-page.js'), { width: 1400, height: 900 })
19
+ })
20
+ after(async () => {
21
+ await opened?.close()
22
+ })
23
+
24
+ const page = () => opened.page
25
+ const read = (fn, ...args) => page().evaluate(fn, ...args)
26
+ async function until(fn, what, ms = 15000) {
27
+ const deadline = Date.now() + ms
28
+ for (;;) {
29
+ if (await read(fn).catch(() => false)) return
30
+ if (Date.now() > deadline) assert.fail(`waited for ${what}`)
31
+ await sleep(50)
32
+ }
33
+ }
34
+ /* What a list page shows: its title, its FAB, the choices in its filter bar, a scope switch, and the list it asked for. */
35
+ const look = () =>
36
+ read(() => {
37
+ const host = document.body.firstElementChild
38
+ const root = host.shadowRoot
39
+ const deep = (s, r = root, o = []) => {
40
+ o.push(...r.querySelectorAll(s))
41
+ for (const e of r.querySelectorAll('*')) if (e.shadowRoot) deep(s, e.shadowRoot, o)
42
+ return o
43
+ }
44
+ return {
45
+ title: host.context.title,
46
+ fab: !!root.querySelector('button[fab]'),
47
+ scopeSwitch: !!root.querySelector('button[scope]'),
48
+ stateChoice: deep('ox-input-select-buttons[name=state]').length,
49
+ asked: harness.asked.at(-1)
50
+ }
51
+ })
52
+
53
+ describe('the rail\'s two lists', () => {
54
+ test('the catalogue: its own figures, the new-figure button and the draft · released choice, no switch to the public ones', async () => {
55
+ await read(() => harness.mount('figure-list-page'))
56
+ await until(() => harness.asked.includes('figures'), 'the own list asked for')
57
+ await until(() => !!document.body.firstElementChild.shadowRoot.querySelector('ox-grist'), 'the grid')
58
+ await sleep(500)
59
+ assert.deepEqual(await look(), { title: 'figure.title.figures', fab: true, scopeSwitch: false, stateChoice: 1, asked: 'figures' })
60
+ })
61
+
62
+ test('the public figures: other domains\' list, no new figure, no draft · released choice', async () => {
63
+ await read(() => harness.mount('figure-public-list-page'))
64
+ await until(() => harness.asked.includes('publicFigures'), 'the public list asked for')
65
+ await sleep(500)
66
+ assert.deepEqual(await look(), { title: 'figure.title.public-figures', fab: false, scopeSwitch: false, stateChoice: 0, asked: 'publicFigures' })
67
+ })
68
+ })
@@ -49,6 +49,6 @@ test("another domain's public figure: its card offers no delete and names its do
49
49
  assert.match(thumb, /badge=\$\{this\.readonly \? figure\.domain\?\.name/)
50
50
  for (const lang of ['en', 'ko', 'ja', 'zh', 'ms', 'es']) {
51
51
  const words = JSON.parse(read(`../translations/${lang}.json`))
52
- for (const key of ['figure.label.public', 'figure.button.make-public', 'figure.button.make-private', 'figure.button.public-figures']) assert.ok(words[key], `${lang} ${key}`)
52
+ for (const key of ['figure.label.public', 'figure.button.make-public', 'figure.button.make-private', 'figure.title.public-figures']) assert.ok(words[key], `${lang} ${key}`)
53
53
  }
54
54
  })
@@ -5,6 +5,7 @@ export default {
5
5
  route,
6
6
  routes: [
7
7
  { tagname: 'figure-list-page', page: 'figure-list' },
8
+ { tagname: 'figure-public-list-page', page: 'figure-public-list' },
8
9
  /* The shell builds the DOM from this table. */
9
10
  { tagname: 'figure-modeller-page', page: 'figure-modeller' }
10
11
  ],
@@ -507,6 +507,7 @@
507
507
  "figure.text.what-a-figure-is": "a Figure is a 3D component you author. compose it from simple shapes and it becomes a component you can place on a drawing, sharing shape and material across every instance.",
508
508
  "figure.text.what-changed-in-this-release": "what changed in this release",
509
509
  "figure.title.figures": "figure catalog",
510
+ "figure.title.public-figures": "public figures",
510
511
  "figure.title.new-figure": "new figure",
511
512
  "figure.title.save-new-figure": "save figure draft",
512
513
  "figure.text.no-parts-yet": "No parts yet. Pick a shape above to place one.",
@@ -1207,7 +1208,6 @@
1207
1208
  "figure.label.public": "public",
1208
1209
  "figure.button.make-public": "make public",
1209
1210
  "figure.button.make-private": "make private",
1210
- "figure.button.public-figures": "public figures of other domains",
1211
1211
  "figure.text.category-choose": "choose a category",
1212
1212
  "figure.text.property-section-failed": "the section {section} could not be drawn: {reason}"
1213
1213
  }
@@ -507,6 +507,7 @@
507
507
  "figure.text.what-a-figure-is": "una Figure es un componente 3D que usted crea. compóngala a partir de formas sencillas y se convierte en un componente que puede colocar en un plano, compartiendo forma y material entre todas las instancias.",
508
508
  "figure.text.what-changed-in-this-release": "qué cambió en esta publicación",
509
509
  "figure.title.figures": "catálogo de figuras",
510
+ "figure.title.public-figures": "figuras públicas",
510
511
  "figure.title.new-figure": "nueva figura",
511
512
  "figure.title.save-new-figure": "guardar borrador de figura",
512
513
  "figure.text.no-parts-yet": "Aún no hay piezas. Elija una forma arriba para colocar una.",
@@ -1207,7 +1208,6 @@
1207
1208
  "figure.label.public": "pública",
1208
1209
  "figure.button.make-public": "hacer pública",
1209
1210
  "figure.button.make-private": "dejar de publicar",
1210
- "figure.button.public-figures": "figuras públicas de otros dominios",
1211
1211
  "figure.text.category-choose": "elegir categoría",
1212
1212
  "figure.text.property-section-failed": "no se pudo dibujar la sección {section}: {reason}"
1213
1213
  }
@@ -507,6 +507,7 @@
507
507
  "figure.text.what-a-figure-is": "a Figure is a 3D component you author. Compose it from simple shapes and it becomes a component you can place on a drawing, sharing shape and material across every instance.",
508
508
  "figure.text.what-changed-in-this-release": "このバージョンで変わったこと",
509
509
  "figure.title.figures": "Figure カタログ",
510
+ "figure.title.public-figures": "公開 Figure",
510
511
  "figure.title.new-figure": "new figure",
511
512
  "figure.title.save-new-figure": "Figure の下書きを保存",
512
513
  "figure.text.no-parts-yet": "No parts yet. Pick a shape above to place one.",
@@ -1207,7 +1208,6 @@
1207
1208
  "figure.label.public": "公開",
1208
1209
  "figure.button.make-public": "公開する",
1209
1210
  "figure.button.make-private": "公開をやめる",
1210
- "figure.button.public-figures": "他ドメインの公開 Figure",
1211
1211
  "figure.text.category-choose": "分類を選ぶ",
1212
1212
  "figure.text.property-section-failed": "{section} の区画を描けませんでした: {reason}"
1213
1213
  }
@@ -507,6 +507,7 @@
507
507
  "figure.text.what-a-figure-is": "Figure 는 직접 저작한 3D 컴포넌트입니다. 단순한 도형을 조합해 만들면 도면에 배치할 수 있는 컴포넌트가 되고, 여러 개를 배치해도 형상과 재질을 공유합니다.",
508
508
  "figure.text.what-changed-in-this-release": "이번 버전에서 바뀐 것",
509
509
  "figure.title.figures": "Figure 카탈로그",
510
+ "figure.title.public-figures": "공개 Figure",
510
511
  "figure.title.new-figure": "새 Figure",
511
512
  "figure.title.save-new-figure": "Figure 초안 저장",
512
513
  "figure.text.no-parts-yet": "아직 부품이 없습니다. 위에서 도형을 선택하여 놓으세요.",
@@ -1207,7 +1208,6 @@
1207
1208
  "figure.label.public": "공개",
1208
1209
  "figure.button.make-public": "공개하기",
1209
1210
  "figure.button.make-private": "공개 끄기",
1210
- "figure.button.public-figures": "다른 도메인의 공개 Figure",
1211
1211
  "figure.text.category-choose": "분류 고르기",
1212
1212
  "figure.text.property-section-failed": "{section} 구역을 그리지 못했습니다: {reason}"
1213
1213
  }
@@ -507,6 +507,7 @@
507
507
  "figure.text.what-a-figure-is": "a Figure is a 3D component you author. Compose it from simple shapes and it becomes a component you can place on a drawing, sharing shape and material across every instance.",
508
508
  "figure.text.what-changed-in-this-release": "apa yang berubah dalam terbitan ini",
509
509
  "figure.title.figures": "katalog figure",
510
+ "figure.title.public-figures": "figure awam",
510
511
  "figure.title.new-figure": "new figure",
511
512
  "figure.title.save-new-figure": "simpan draf figure",
512
513
  "figure.text.no-parts-yet": "No parts yet. Pick a shape above to place one.",
@@ -1207,7 +1208,6 @@
1207
1208
  "figure.label.public": "awam",
1208
1209
  "figure.button.make-public": "jadikan awam",
1209
1210
  "figure.button.make-private": "jadikan peribadi",
1210
- "figure.button.public-figures": "figure awam domain lain",
1211
1211
  "figure.text.category-choose": "pilih kategori",
1212
1212
  "figure.text.property-section-failed": "bahagian {section} tidak dapat dilukis: {reason}"
1213
1213
  }
@@ -507,6 +507,7 @@
507
507
  "figure.text.what-a-figure-is": "a Figure is a 3D component you author. Compose it from simple shapes and it becomes a component you can place on a drawing, sharing shape and material across every instance.",
508
508
  "figure.text.what-changed-in-this-release": "此版本的变更",
509
509
  "figure.title.figures": "Figure 目录",
510
+ "figure.title.public-figures": "公开 Figure",
510
511
  "figure.title.new-figure": "new figure",
511
512
  "figure.title.save-new-figure": "保存 Figure 草稿",
512
513
  "figure.text.no-parts-yet": "No parts yet. Pick a shape above to place one.",
@@ -1207,7 +1208,6 @@
1207
1208
  "figure.label.public": "公开",
1208
1209
  "figure.button.make-public": "公开",
1209
1210
  "figure.button.make-private": "取消公开",
1210
- "figure.button.public-figures": "其他域的公开图形",
1211
1211
  "figure.text.category-choose": "选择分类",
1212
1212
  "figure.text.property-section-failed": "无法绘制 {section} 区块:{reason}"
1213
1213
  }