@things-factory/figure-ui 10.1.117 → 10.1.122

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 (39) hide show
  1. package/client/graphql/index.ts +1 -0
  2. package/client/modeller/figure-first-save.ts +1 -1
  3. package/client/modeller/figure-history-panel.ts +21 -0
  4. package/client/modeller/first-save.ts +6 -6
  5. package/client/pages/figure-list-page.ts +3 -1
  6. package/client/pages/figure-modeller-page.ts +5 -2
  7. package/client/types.ts +2 -0
  8. package/client/viewparts/figure-detail-dock.ts +14 -6
  9. package/dist-client/graphql/index.js +1 -0
  10. package/dist-client/graphql/index.js.map +1 -1
  11. package/dist-client/modeller/figure-first-save.js +1 -1
  12. package/dist-client/modeller/figure-first-save.js.map +1 -1
  13. package/dist-client/modeller/figure-history-panel.d.ts +4 -0
  14. package/dist-client/modeller/figure-history-panel.js +23 -0
  15. package/dist-client/modeller/figure-history-panel.js.map +1 -1
  16. package/dist-client/modeller/first-save.d.ts +2 -2
  17. package/dist-client/modeller/first-save.js +6 -6
  18. package/dist-client/modeller/first-save.js.map +1 -1
  19. package/dist-client/pages/figure-list-page.js +2 -1
  20. package/dist-client/pages/figure-list-page.js.map +1 -1
  21. package/dist-client/pages/figure-modeller-page.js +4 -1
  22. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  23. package/dist-client/tsconfig.tsbuildinfo +1 -1
  24. package/dist-client/types.d.ts +2 -0
  25. package/dist-client/types.js.map +1 -1
  26. package/dist-client/viewparts/figure-detail-dock.d.ts +3 -2
  27. package/dist-client/viewparts/figure-detail-dock.js +13 -5
  28. package/dist-client/viewparts/figure-detail-dock.js.map +1 -1
  29. package/docs/product-cutover.md +7 -7
  30. package/package.json +6 -6
  31. package/test/detail-dock.test.ts +31 -0
  32. package/test/first-save-suggest.test.ts +5 -5
  33. package/test/first-save.test.ts +15 -13
  34. package/test/package-download.test.ts +16 -0
  35. package/translations/en.json +2 -1
  36. package/translations/ja.json +2 -1
  37. package/translations/ko.json +2 -1
  38. package/translations/ms.json +2 -1
  39. package/translations/zh.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"figure-history-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-history-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { openPopup } from '@operato/layout'\nimport './figure-revert-choice.js'\nimport type { RevertChoice } from './figure-revert-choice.js'\nimport '@material/web/icon/icon.js'\n\nimport { gateFindingStyles, renderGateFinding } from './gate-findings.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { fetchFigureVersions, fetchReplacedDrafts, inspectFigure, releaseFigure, revertFigureVersion, undoFigureRevert } from '../graphql/index.js'\nimport type { KeptDraft } from '../graphql/index.js'\nimport { afterRevertAnswer, revertOutcomeUnknown, revertRequestFor } from './revert-request.js'\nimport type { RevertRequest } from './revert-request.js'\nimport type { FigureGateFinding, FigureInspection, FigureVersion } from '../types.js'\nimport { OxPrompt } from '@operato/popup'\n\n/**\n * 이력 — **발행하는 자리이자, 발행된 판들을 보는 자리.**\n *\n * ## 왜 저장 옆이 아니라 여기인가\n *\n * 저장은 하루에 수십 번이고 발행은 드물다. 두 단추를 나란히 두면 저장하려다 발행을 누른다.\n * 그리고 발행은 **소비처에게 하는 약속**이라, 무엇을 약속하는지(지금 몇 판인가 · 저장 안 한 것이\n * 있나 · 지난 판이 무엇이었나)를 함께 보여 주는 자리에 있어야 한다.\n *\n * ## 저장하지 않은 것이 있으면 발행하지 않는다\n *\n * 발행은 **저장된 것**을 내보낸다. 화면에 고친 것이 남아 있는 채로 발행하면, 저작자가 보고 있는\n * 것과 약속한 것이 다르다. 그래서 막고, 왜 막혔는지 적는다.\n *\n * ## 되돌리기는 초안으로 돌아온다\n *\n * 옛 판의 정본·속성·그림이 돌아오고 이름·설명은 그대로다. 되살렸다고 그 판이 다시 발행되지는\n * 않는다 — 다시 내보내려면 발행을 한 번 더 하고, 그때 번호는 이미 쓴 다음 번호를 받는다.\n *\n * 덮이는 초안은 서버가 같은 트랜잭션에서 보관하고, 「되돌리기 전 초안」에 남는다(ADR-0097 §5.6, 디자이너 2026-09-27).\n * 발행 판과 섞지 않는다. 다시 접속해도 거기서 찾고, 가장 최근 것은 「되돌리기 취소」로 되살린다 — 되돌린 뒤 모델이\n * 바뀌었으면 서버가 거절하고 보관본은 남는다.\n */\n@customElement('figure-history-panel')\nexport class FigureHistoryPanel extends localize(i18next)(LitElement) {\n static styles = [\n gateFindingStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n background-color: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.76rem;\n }\n\n :host([off]) {\n display: none;\n }\n\n section {\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n h3 {\n margin: 0 0 6px 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /* 지금 무엇인가 — 발행을 누르기 전에 읽는 한 줄 */\n div[now] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 8px;\n }\n\n span[state] {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--md-sys-color-on-secondary-container);\n background-color: var(--md-sys-color-secondary-container);\n }\n\n span[state][released] {\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n }\n\n span[ver] {\n font-family: var(--mono-font, monospace);\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n input,\n button {\n font: var(--input-field-font, inherit);\n font-size: 0.76rem;\n }\n\n input[note] {\n width: 100%;\n box-sizing: border-box;\n padding: 5px 8px;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n\n button[release] {\n width: 100%;\n margin-top: 6px;\n padding: 6px 0;\n font-weight: 700;\n color: var(--ui-on-accent, #fff);\n background-color: var(--md-sys-color-primary);\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n }\n\n button[release][disabled] {\n opacity: 0.45;\n cursor: default;\n }\n\n /* 왜 못 누르는지 — 눌러 보고 알게 하지 않는다 */\n p[why] {\n margin: 6px 0 0 0;\n font-size: 0.7rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n p[failed] {\n margin: 6px 0 0 0;\n font-size: 0.7rem;\n line-height: 1.5;\n color: var(--md-sys-color-error);\n }\n\n ul {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n li {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 4px 8px;\n align-items: baseline;\n padding: 7px 0;\n border-bottom: 1px dashed var(--md-sys-color-outline-variant);\n }\n\n li:last-child {\n border-bottom: 0;\n }\n\n li span[no] {\n font-family: var(--mono-font, monospace);\n font-weight: 700;\n }\n\n li span[when] {\n grid-column: 2 / 4;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n li span[comment] {\n grid-column: 1 / 4;\n line-height: 1.5;\n }\n\n li button[revert] {\n padding: 2px 8px;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n\n li button[revert]:hover {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n /*\n 게이트 사유. 막는 것은 색으로도 말하고, 알리는 것은 조용히 둔다 — 둘을 같은 톤으로 두면\n 「이 중 어느 것이 발행을 막았나」를 사람이 다시 세어야 한다.\n */\n\n p[quiet] {\n margin: 0;\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n `\n ]\n\n /** 지금 열려 있는 도형. 없으면(새로 만드는 중) 발행할 것이 없다. */\n @property({ type: String }) figureId?: string\n @property({ type: String }) figureState?: 'draft' | 'released'\n @property({ type: Number }) figureVersion = 0\n /** 저장하지 않은 것이 있나 — 있으면 발행을 막는다. */\n @property({ type: Boolean }) dirty = false\n /** Saves the edits in hand before reverting; `ok` only when the save is known to have happened. */\n @property({ attribute: false }) saveFirst?: () => Promise<{ ok: boolean; reason?: string }>\n /** The source the server is confirmed to hold: a revert goes on only while the server still holds it. */\n @property({ attribute: false }) serverSource?: () => string | undefined\n /**\n * 행의 타입. 판정 하나(`type-is-placeholder`)가 이것을 본다 — 자산은 타입을 원본에 적지 않아서, 타입을 바꾸면\n * 원본도 dirty 도 그대로다. 그래서 이 값이 바뀌면 판정을 다시 묻는다. 안 그러면 타입을 정한 뒤에도 「임시 타입」\n * 판정이 남아 발행 단추가 잠긴 채다.\n */\n @property({ type: String }) figureType?: string\n\n @state() private versions: FigureVersion[] = []\n /** Drafts reverts kept, newest first -- apart from the released versions. */\n @state() private kept: KeptDraft[] = []\n /**\n * A revert or undo whose answer never came back. Asked again, it goes with the same request id, so the server\n * answers with what it did rather than doing it twice. Any answer -- done or refused -- lets it go.\n */\n private pending?: RevertRequest\n @state() private busy = false\n @state() private failure = ''\n /**\n * 발행 판정. **저장된 정본에 대한 것**이라 저장할 때마다 다시 묻는다(`figureVersion` 이 아니라\n * `dirty` 가 풀리는 순간이 그때다).\n */\n @state() private inspection?: FigureInspection\n\n updated(changes: Map<string, unknown>) {\n if (changes.has('figureId') || changes.has('figureVersion')) {\n void this.reload()\n }\n\n /*\n 저장이 끝난 순간(dirty 가 풀림) 정본이 바뀌었으니 판정을 다시 묻는다. 저작 중(dirty)에는\n 묻지 않는다 — 서버에 있는 것은 저장 전 정본이라, 그 답은 화면에 있는 것과 다른 것을 말한다.\n */\n if (changes.has('figureId') || changes.has('dirty') || changes.has('figureType')) {\n void this.inspect()\n }\n }\n\n private async inspect() {\n if (!this.figureId || this.dirty) {\n this.inspection = undefined\n return\n }\n\n try {\n this.inspection = await inspectFigure(this.figureId)\n } catch (e) {\n /* 판정을 못 받은 것을 「통과」로 두지 않는다 — 모르는 것이다. 단추는 서버가 막는다. */\n this.inspection = undefined\n this.failure = (e as Error).message\n }\n }\n\n private async reload() {\n if (!this.figureId) {\n this.versions = []\n this.kept = []\n return\n }\n\n try {\n ;[this.versions, this.kept] = await Promise.all([fetchFigureVersions(this.figureId), fetchReplacedDrafts(this.figureId)])\n this.failure = ''\n } catch (e) {\n this.failure = (e as Error).message\n }\n }\n\n /** The release section in view, focus on it: where the top bar's \"release\" leads (ADR-0097 §5.6, Q-D2). */\n async showRelease() {\n await this.updateComplete\n const section = this.renderRoot.querySelector<HTMLElement>('section[release]')\n section?.scrollIntoView({ block: 'start', behavior: 'smooth' })\n ;(section?.querySelector<HTMLElement>('input[note]:not([disabled]), button[release]:not([disabled])') ?? section)?.focus({ preventScroll: true })\n }\n\n render() {\n return html`\n <section release tabindex=\"-1\">\n <h3>${i18next.t('figure.label.release')}</h3>\n ${this.figureId ? this.renderRelease() : html`<p quiet>${i18next.t('figure.text.save-the-figure-before-releasing')}</p>`}\n </section>\n\n <section>\n <h3>${i18next.t('figure.label.released-versions')}</h3>\n ${this.versions.length\n ? html`<ul>\n ${this.versions.map(version => this.renderVersion(version))}\n </ul>`\n : html`<p quiet>${i18next.t('figure.text.nothing-released-yet')}</p>`}\n </section>\n\n ${this.kept.length\n ? html`<section kept>\n <h3>${i18next.t('figure.label.kept-drafts')}</h3>\n <ul>\n ${this.kept.map((kept, index) => this.renderKept(kept, index === 0))}\n </ul>\n </section>`\n : nothing}\n `\n }\n\n private renderRelease() {\n const released = this.figureState === 'released'\n\n /*\n 게이트가 막는 것과 알리는 것을 함께 낸다. **막는 것은 단추를 잠근다** — 눌러 보고 서버에게\n 거절당하는 것으로 알게 하지 않는다. 알리는 것은 잠그지 않고 그대로 보여 준다.\n */\n const blocking = (this.inspection?.findings ?? []).filter(finding => finding.blocking)\n const advisory = (this.inspection?.findings ?? []).filter(finding => !finding.blocking)\n\n /*\n ── 「발행되나」에 **한 번만** 답한다 ─────────────────────────────────────\n 전에는 막는 목록의 머리가 「이것들이 발행을 막습니다」, 알리는 목록의 머리가 「발행은 되지만\n 확인해 보십시오」였다. 두 줄이 나란히 서서 화면이 「막힌다」와 「된다」를 함께 말했고, 사람이\n 「그래서 되는 건가」를 되물었다.\n\n 답은 이 한 줄이 하고, 아래 목록들은 **이유**일 뿐이다 — 그래서 목록 머리는 문장이 아니라\n 이름씨다(「고쳐야 하는 것」·「막지는 않는 것」).\n */\n const why = this.dirty\n ? i18next.t('figure.text.release-needs-a-save-first')\n : released\n ? i18next.t('figure.text.already-released-edit-to-release-again')\n : blocking.length\n ? i18next.t('figure.text.cannot-release-now')\n : ''\n\n return html`\n <div now>\n <span state ?released=${released}>${i18next.t(released ? 'figure.label.figure-released' : 'figure.label.draft')}</span>\n <span ver>${i18next.t('figure.text.version-number', { version: this.figureVersion ?? 0 })}</span>\n </div>\n\n <input note placeholder=${i18next.t('figure.text.what-changed-in-this-release')} ?disabled=${!!why || this.busy} />\n\n <button release ?disabled=${!!why || this.busy} @click=${() => this.release()}>\n ${this.busy ? i18next.t('figure.text.releasing') : i18next.t('figure.button.release-figure')}\n </button>\n\n ${why ? html`<p why>${why}</p>` : nothing}\n ${blocking.length\n ? html`<ul gate>\n <li head>${i18next.t('figure.text.must-fix-to-release')}</li>\n ${blocking.map(finding => this.renderFinding(finding, true))}\n </ul>`\n : nothing}\n ${advisory.length\n ? html`<ul gate>\n <li head quiet>${i18next.t('figure.text.does-not-block')}</li>\n ${advisory.map(finding => this.renderFinding(finding, false))}\n </ul>`\n : nothing}\n ${this.failure ? html`<p failed>${this.failure}</p>` : nothing}\n `\n }\n\n /**\n * 사유 한 줄 — **잰 것 · 왜 · 어떻게** 셋을 함께 말한다.\n *\n * 잰 것만 보여 줬더니 「1 조각이 2 조각으로 갈라진다」에서 사람이 멈췄다. 왜 나쁜지도, 무엇을\n * 바꾸면 되는지도 알 수 없었다. 그 셋은 형식(`figure-model` 의 gate)이 만들고 여기서는 자리만\n * 준다 — 화면이 문장을 지으면 서버가 거절할 때와 다른 말을 하게 된다.\n */\n private renderFinding(finding: FigureGateFinding, blocking: boolean) {\n return renderGateFinding(finding, blocking, html`\n ${this.offersFit(finding.code)\n ? html`<button fit-base @click=${() => this.fitBase()}>\n ${i18next.t('figure.button.fit-base-to-parts')}\n </button>`\n : nothing}\n `)\n }\n\n /**\n * 넘친 것을 담는 길 하나를 여기서도 내민다 — **같은 동작이 속성 패널에도 있다**(「부품에 맞추기」).\n *\n * 그 단추를 아는 저작자는 거기서 누른다. 발행이 막혔다는 말을 여기서 읽은 사람은 그 단추가 있는\n * 것을 모르고, 문구는 그것을 가리킬 수 없다 — 문장은 형식이 쓰고 형식은 화면의 단추를 모른다.\n * 그래서 화면이 자기 자리에서 같은 동작을 낸다. 누르면 상자만 커지고 부품은 제자리이며, 저장은\n * 여전히 저작자가 따로 한다.\n */\n /**\n * 「부품에 맞추기」를 내미는 판정: 공간을 부품에서 다시 선언한다(`declare-occupancy`) — 부품은 그대로 두고 선언만\n * 바꾼다. 공간을 아직 선언하지 않은 도형도 같은 단추로 선언한다: 새로 만든 도형은 전부 그 판정으로 막히고, 화면에\n * 다른 길이 없다. 장착면이 떠 있는 것은 여기 없다 — 부품 하나를 평면에 붙여도 거기 붙지 않은 것은 따라오지 않는다.\n * 판정의 「어떻게」가 할 일을 말한다.\n */\n private offersFit(code: string): boolean {\n return code === 'part-outside-occupancy' || code === 'occupancy-undeclared'\n }\n\n private fitBase() {\n this.dispatchEvent(new CustomEvent('fit-base-to-parts', { bubbles: true, composed: true }))\n }\n\n private renderVersion(version: FigureVersion) {\n return html`\n <li>\n <span no>${i18next.t('figure.text.version-number', { version: version.version })}</span>\n <span when>\n ${version.updatedAt ? new Date(version.updatedAt).toLocaleString() : ''}\n ${version.updater?.name ? ` · ${version.updater.name}` : ''}\n </span>\n <button revert ?disabled=${this.busy} @click=${() => this.revert(version.version)}>\n ${i18next.t('figure.button.revert')}\n </button>\n ${version.comment ? html`<span comment>${version.comment}</span>` : nothing}\n </li>\n `\n }\n\n /*\n Only the newest can be taken back: an undo holds only while the figure is what that revert wrote, and a later\n revert or undo has written something else since. The older ones stay listed -- found, not offered.\n */\n private renderKept(kept: KeptDraft, newest: boolean) {\n const what =\n kept.kind === 'undo'\n ? i18next.t('figure.text.kept-before-undo')\n : i18next.t('figure.text.kept-before-revert', { version: String(kept.revertedToVersion ?? '') })\n return html`\n <li>\n <span no>${what}</span>\n <span when>\n ${kept.createdAt ? new Date(kept.createdAt).toLocaleString() : ''} ${kept.creator?.name ? ` · ${kept.creator.name}` : ''}\n ${kept.restoredAt ? ` · ${i18next.t('figure.text.kept-restored')}` : ''}\n </span>\n ${newest && !kept.restoredAt\n ? html`<button revert undo ?disabled=${this.busy} @click=${() => this.undo(kept)}>\n ${i18next.t(kept.kind === 'undo' ? 'figure.button.redo-revert' : 'figure.button.undo-revert')}\n </button>`\n : nothing}\n </li>\n `\n }\n\n private async release() {\n const input = this.renderRoot.querySelector('input[note]') as HTMLInputElement\n const comment = input?.value?.trim()\n\n this.busy = true\n this.failure = ''\n\n try {\n const figure = await releaseFigure(this.figureId!, comment || undefined)\n if (input) input.value = ''\n /* 정본을 든 자리가 갱신한다 — 여기서 페이지 상태를 고쳐 쓰지 않는다. */\n this.dispatchEvent(new CustomEvent('figure-released', { detail: { figure }, bubbles: true, composed: true }))\n await this.reload()\n await this.inspect()\n } catch (e) {\n this.failure = (e as Error).message\n } finally {\n this.busy = false\n }\n }\n\n /**\n * Edits not saved yet: saved first, let go, or kept -- asked with the three (designer 2026-09-27, Q-D1). Reverting and\n * taking a revert back both reopen the figure from the server, so edits not saved would go without a word. `true` to\n * go on.\n */\n private async settleUnsaved(purpose: 'revert' | 'undo', version = 0): Promise<boolean> {\n const choice = await openPopup<RevertChoice>(\n html`<figure-revert-choice .version=${version} .purpose=${purpose}></figure-revert-choice>`,\n { backdrop: true, size: 'small', title: i18next.t(purpose === 'undo' ? 'figure.button.undo-revert' : 'figure.button.revert') }\n ).result\n if (!choice) return false\n if (choice === 'save') {\n /* Saved, and known to be: a save that failed or whose outcome is not known goes no further. */\n const saved = await this.saveFirst?.()\n if (!saved?.ok) {\n this.failure = i18next.t('figure.text.revert-held-unsaved', { reason: saved?.reason ?? '' })\n return false\n }\n }\n return true\n }\n\n private async revert(version: number) {\n this.failure = ''\n if (this.dirty) {\n if (!(await this.settleUnsaved('revert', version))) return\n } else {\n /* 지금 초안은 서버가 보관하지만, 화면의 것이 그 판으로 바뀐다 — 이름으로 확인한다. */\n const agreed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.button.revert'),\n text: i18next.t('figure.text.revert-to-version-confirm', { version }),\n confirmButton: { text: i18next.t('figure.button.revert') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n })\n if (!agreed) {\n return\n }\n }\n\n /* Read after any save above: what the server is now confirmed to hold. */\n const expected = this.serverSource?.()\n if (expected === undefined) {\n this.failure = i18next.t('figure.text.revert-needs-server-source')\n return\n }\n await this.send(`revert:${version}:${expected}`, requestId => revertFigureVersion(this.figureId!, version, requestId, expected))\n }\n\n private async undo(kept: KeptDraft) {\n this.failure = ''\n if (this.dirty && !(await this.settleUnsaved('undo'))) return\n await this.send(`undo:${kept.id}`, requestId => undoFigureRevert(kept.id, requestId))\n }\n\n /** One revert or undo: a new request id, or the one whose answer was lost when the same is asked again. */\n private async send(key: string, request: (requestId: string) => Promise<unknown>) {\n const sent = revertRequestFor(this.pending, key)\n this.pending = sent\n this.busy = true\n this.failure = ''\n\n try {\n const result = await request(sent.requestId)\n this.pending = undefined\n this.dispatchEvent(new CustomEvent('figure-reverted', { detail: result, bubbles: true, composed: true }))\n await this.reload()\n /*\n **되돌리면 정본이 바뀌므로 판정도 바뀐다.** 이것을 안 하면 옛 판의 사유가 새 정본 위에\n 남아 화면이 거짓말한다 — 되돌리기는 `dirty` 도 `figureVersion` 도 건드리지 않으므로\n `updated` 의 감시로는 다시 묻지 않는다.\n */\n await this.inspect()\n } catch (e) {\n const code = (e as { code?: string })?.code\n /* Not known whether it happened: kept, so the next press asks with the same id. Anything the server said: let go. */\n this.pending = afterRevertAnswer(sent, code)\n if (revertOutcomeUnknown(code)) this.failure = i18next.t('figure.text.revert-unconfirmed')\n else {\n /* Refused: the kept list may have changed under us (another session's undo), so it is read again. */\n await this.reload()\n this.failure = (e as Error).message\n }\n } finally {\n this.busy = false\n }\n }\n}\n"]}
1
+ {"version":3,"file":"figure-history-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-history-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { openPopup } from '@operato/layout'\nimport './figure-revert-choice.js'\nimport type { RevertChoice } from './figure-revert-choice.js'\nimport '@material/web/icon/icon.js'\n\nimport { gateFindingStyles, renderGateFinding } from './gate-findings.js'\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport { getPathInfo } from '@operato/utils'\nimport { fetchFigureVersions, fetchReplacedDrafts, inspectFigure, releaseFigure, revertFigureVersion, undoFigureRevert } from '../graphql/index.js'\nimport type { KeptDraft } from '../graphql/index.js'\nimport { afterRevertAnswer, revertOutcomeUnknown, revertRequestFor } from './revert-request.js'\nimport type { RevertRequest } from './revert-request.js'\nimport type { FigureGateFinding, FigureInspection, FigureVersion } from '../types.js'\nimport { OxPrompt } from '@operato/popup'\n\n/**\n * 이력 — **발행하는 자리이자, 발행된 판들을 보는 자리.**\n *\n * ## 왜 저장 옆이 아니라 여기인가\n *\n * 저장은 하루에 수십 번이고 발행은 드물다. 두 단추를 나란히 두면 저장하려다 발행을 누른다.\n * 그리고 발행은 **소비처에게 하는 약속**이라, 무엇을 약속하는지(지금 몇 판인가 · 저장 안 한 것이\n * 있나 · 지난 판이 무엇이었나)를 함께 보여 주는 자리에 있어야 한다.\n *\n * ## 저장하지 않은 것이 있으면 발행하지 않는다\n *\n * 발행은 **저장된 것**을 내보낸다. 화면에 고친 것이 남아 있는 채로 발행하면, 저작자가 보고 있는\n * 것과 약속한 것이 다르다. 그래서 막고, 왜 막혔는지 적는다.\n *\n * ## 되돌리기는 초안으로 돌아온다\n *\n * 옛 판의 정본·속성·그림이 돌아오고 이름·설명은 그대로다. 되살렸다고 그 판이 다시 발행되지는\n * 않는다 — 다시 내보내려면 발행을 한 번 더 하고, 그때 번호는 이미 쓴 다음 번호를 받는다.\n *\n * 덮이는 초안은 서버가 같은 트랜잭션에서 보관하고, 「되돌리기 전 초안」에 남는다(ADR-0097 §5.6, 디자이너 2026-09-27).\n * 발행 판과 섞지 않는다. 다시 접속해도 거기서 찾고, 가장 최근 것은 「되돌리기 취소」로 되살린다 — 되돌린 뒤 모델이\n * 바뀌었으면 서버가 거절하고 보관본은 남는다.\n */\n@customElement('figure-history-panel')\nexport class FigureHistoryPanel extends localize(i18next)(LitElement) {\n static styles = [\n gateFindingStyles,\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow-y: auto;\n background-color: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: var(--label-font, inherit);\n font-size: 0.76rem;\n }\n\n :host([off]) {\n display: none;\n }\n\n section {\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n\n h3 {\n margin: 0 0 6px 0;\n font-size: var(--ui-t-micro, 11px);\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n /* 지금 무엇인가 — 발행을 누르기 전에 읽는 한 줄 */\n div[now] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 8px;\n }\n\n span[state] {\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 0.7rem;\n font-weight: 700;\n color: var(--md-sys-color-on-secondary-container);\n background-color: var(--md-sys-color-secondary-container);\n }\n\n span[state][released] {\n color: var(--md-sys-color-on-primary);\n background-color: var(--md-sys-color-primary);\n }\n\n span[ver] {\n font-family: var(--mono-font, monospace);\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n input,\n button {\n font: var(--input-field-font, inherit);\n font-size: 0.76rem;\n }\n\n input[note] {\n width: 100%;\n box-sizing: border-box;\n padding: 5px 8px;\n color: var(--md-sys-color-on-surface);\n background-color: var(--md-sys-color-surface-container-lowest);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n }\n\n button[release] {\n width: 100%;\n margin-top: 6px;\n padding: 6px 0;\n font-weight: 700;\n color: var(--ui-on-accent, #fff);\n background-color: var(--md-sys-color-primary);\n border: 0;\n border-radius: 6px;\n cursor: pointer;\n }\n\n button[release][disabled] {\n opacity: 0.45;\n cursor: default;\n }\n\n /* 왜 못 누르는지 — 눌러 보고 알게 하지 않는다 */\n p[why] {\n margin: 6px 0 0 0;\n font-size: 0.7rem;\n line-height: 1.5;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n p[failed] {\n margin: 6px 0 0 0;\n font-size: 0.7rem;\n line-height: 1.5;\n color: var(--md-sys-color-error);\n }\n\n ul {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n li {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 4px 8px;\n align-items: baseline;\n padding: 7px 0;\n border-bottom: 1px dashed var(--md-sys-color-outline-variant);\n }\n\n li:last-child {\n border-bottom: 0;\n }\n\n li span[no] {\n font-family: var(--mono-font, monospace);\n font-weight: 700;\n }\n\n li span[when] {\n grid-column: 2 / 4;\n font-size: 0.7rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n\n li span[comment] {\n grid-column: 1 / 4;\n line-height: 1.5;\n }\n\n li button[revert] {\n padding: 2px 8px;\n color: var(--md-sys-color-on-surface);\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n cursor: pointer;\n }\n\n li a[package] {\n padding: 2px 8px;\n color: var(--md-sys-color-on-surface);\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 6px;\n text-decoration: none;\n }\n\n li button[revert]:hover {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n\n /*\n 게이트 사유. 막는 것은 색으로도 말하고, 알리는 것은 조용히 둔다 — 둘을 같은 톤으로 두면\n 「이 중 어느 것이 발행을 막았나」를 사람이 다시 세어야 한다.\n */\n\n p[quiet] {\n margin: 0;\n line-height: 1.6;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n }\n `\n ]\n\n /** 지금 열려 있는 도형. 없으면(새로 만드는 중) 발행할 것이 없다. */\n @property({ type: String }) figureId?: string\n @property({ type: String }) figureState?: 'draft' | 'released'\n @property({ type: Number }) figureVersion = 0\n /** 저장하지 않은 것이 있나 — 있으면 발행을 막는다. */\n @property({ type: Boolean }) dirty = false\n /** Saves the edits in hand before reverting; `ok` only when the save is known to have happened. */\n @property({ attribute: false }) saveFirst?: () => Promise<{ ok: boolean; reason?: string }>\n /** The source the server is confirmed to hold: a revert goes on only while the server still holds it. */\n @property({ attribute: false }) serverSource?: () => string | undefined\n /**\n * 행의 타입. 판정 하나(`type-is-placeholder`)가 이것을 본다 — 자산은 타입을 원본에 적지 않아서, 타입을 바꾸면\n * 원본도 dirty 도 그대로다. 그래서 이 값이 바뀌면 판정을 다시 묻는다. 안 그러면 타입을 정한 뒤에도 「임시 타입」\n * 판정이 남아 발행 단추가 잠긴 채다.\n */\n @property({ type: String }) figureType?: string\n /** The namespace of the full name: with it a released version can be downloaded as a package (ADR-0114). */\n @property({ type: String }) figureNamespace?: string\n\n @state() private versions: FigureVersion[] = []\n /** Drafts reverts kept, newest first -- apart from the released versions. */\n @state() private kept: KeptDraft[] = []\n /**\n * A revert or undo whose answer never came back. Asked again, it goes with the same request id, so the server\n * answers with what it did rather than doing it twice. Any answer -- done or refused -- lets it go.\n */\n private pending?: RevertRequest\n @state() private busy = false\n @state() private failure = ''\n /**\n * 발행 판정. **저장된 정본에 대한 것**이라 저장할 때마다 다시 묻는다(`figureVersion` 이 아니라\n * `dirty` 가 풀리는 순간이 그때다).\n */\n @state() private inspection?: FigureInspection\n\n updated(changes: Map<string, unknown>) {\n if (changes.has('figureId') || changes.has('figureVersion')) {\n void this.reload()\n }\n\n /*\n 저장이 끝난 순간(dirty 가 풀림) 정본이 바뀌었으니 판정을 다시 묻는다. 저작 중(dirty)에는\n 묻지 않는다 — 서버에 있는 것은 저장 전 정본이라, 그 답은 화면에 있는 것과 다른 것을 말한다.\n */\n if (changes.has('figureId') || changes.has('dirty') || changes.has('figureType')) {\n void this.inspect()\n }\n }\n\n private async inspect() {\n if (!this.figureId || this.dirty) {\n this.inspection = undefined\n return\n }\n\n try {\n this.inspection = await inspectFigure(this.figureId)\n } catch (e) {\n /* 판정을 못 받은 것을 「통과」로 두지 않는다 — 모르는 것이다. 단추는 서버가 막는다. */\n this.inspection = undefined\n this.failure = (e as Error).message\n }\n }\n\n private async reload() {\n if (!this.figureId) {\n this.versions = []\n this.kept = []\n return\n }\n\n try {\n ;[this.versions, this.kept] = await Promise.all([fetchFigureVersions(this.figureId), fetchReplacedDrafts(this.figureId)])\n this.failure = ''\n } catch (e) {\n this.failure = (e as Error).message\n }\n }\n\n /** The release section in view, focus on it: where the top bar's \"release\" leads (ADR-0097 §5.6, Q-D2). */\n async showRelease() {\n await this.updateComplete\n const section = this.renderRoot.querySelector<HTMLElement>('section[release]')\n section?.scrollIntoView({ block: 'start', behavior: 'smooth' })\n ;(section?.querySelector<HTMLElement>('input[note]:not([disabled]), button[release]:not([disabled])') ?? section)?.focus({ preventScroll: true })\n }\n\n render() {\n return html`\n <section release tabindex=\"-1\">\n <h3>${i18next.t('figure.label.release')}</h3>\n ${this.figureId ? this.renderRelease() : html`<p quiet>${i18next.t('figure.text.save-the-figure-before-releasing')}</p>`}\n </section>\n\n <section>\n <h3>${i18next.t('figure.label.released-versions')}</h3>\n ${this.versions.length\n ? html`<ul>\n ${this.versions.map(version => this.renderVersion(version))}\n </ul>`\n : html`<p quiet>${i18next.t('figure.text.nothing-released-yet')}</p>`}\n </section>\n\n ${this.kept.length\n ? html`<section kept>\n <h3>${i18next.t('figure.label.kept-drafts')}</h3>\n <ul>\n ${this.kept.map((kept, index) => this.renderKept(kept, index === 0))}\n </ul>\n </section>`\n : nothing}\n `\n }\n\n private renderRelease() {\n const released = this.figureState === 'released'\n\n /*\n 게이트가 막는 것과 알리는 것을 함께 낸다. **막는 것은 단추를 잠근다** — 눌러 보고 서버에게\n 거절당하는 것으로 알게 하지 않는다. 알리는 것은 잠그지 않고 그대로 보여 준다.\n */\n const blocking = (this.inspection?.findings ?? []).filter(finding => finding.blocking)\n const advisory = (this.inspection?.findings ?? []).filter(finding => !finding.blocking)\n\n /*\n ── 「발행되나」에 **한 번만** 답한다 ─────────────────────────────────────\n 전에는 막는 목록의 머리가 「이것들이 발행을 막습니다」, 알리는 목록의 머리가 「발행은 되지만\n 확인해 보십시오」였다. 두 줄이 나란히 서서 화면이 「막힌다」와 「된다」를 함께 말했고, 사람이\n 「그래서 되는 건가」를 되물었다.\n\n 답은 이 한 줄이 하고, 아래 목록들은 **이유**일 뿐이다 — 그래서 목록 머리는 문장이 아니라\n 이름씨다(「고쳐야 하는 것」·「막지는 않는 것」).\n */\n const why = this.dirty\n ? i18next.t('figure.text.release-needs-a-save-first')\n : released\n ? i18next.t('figure.text.already-released-edit-to-release-again')\n : blocking.length\n ? i18next.t('figure.text.cannot-release-now')\n : ''\n\n return html`\n <div now>\n <span state ?released=${released}>${i18next.t(released ? 'figure.label.figure-released' : 'figure.label.draft')}</span>\n <span ver>${i18next.t('figure.text.version-number', { version: this.figureVersion ?? 0 })}</span>\n </div>\n\n <input note placeholder=${i18next.t('figure.text.what-changed-in-this-release')} ?disabled=${!!why || this.busy} />\n\n <button release ?disabled=${!!why || this.busy} @click=${() => this.release()}>\n ${this.busy ? i18next.t('figure.text.releasing') : i18next.t('figure.button.release-figure')}\n </button>\n\n ${why ? html`<p why>${why}</p>` : nothing}\n ${blocking.length\n ? html`<ul gate>\n <li head>${i18next.t('figure.text.must-fix-to-release')}</li>\n ${blocking.map(finding => this.renderFinding(finding, true))}\n </ul>`\n : nothing}\n ${advisory.length\n ? html`<ul gate>\n <li head quiet>${i18next.t('figure.text.does-not-block')}</li>\n ${advisory.map(finding => this.renderFinding(finding, false))}\n </ul>`\n : nothing}\n ${this.failure ? html`<p failed>${this.failure}</p>` : nothing}\n `\n }\n\n /**\n * 사유 한 줄 — **잰 것 · 왜 · 어떻게** 셋을 함께 말한다.\n *\n * 잰 것만 보여 줬더니 「1 조각이 2 조각으로 갈라진다」에서 사람이 멈췄다. 왜 나쁜지도, 무엇을\n * 바꾸면 되는지도 알 수 없었다. 그 셋은 형식(`figure-model` 의 gate)이 만들고 여기서는 자리만\n * 준다 — 화면이 문장을 지으면 서버가 거절할 때와 다른 말을 하게 된다.\n */\n private renderFinding(finding: FigureGateFinding, blocking: boolean) {\n return renderGateFinding(finding, blocking, html`\n ${this.offersFit(finding.code)\n ? html`<button fit-base @click=${() => this.fitBase()}>\n ${i18next.t('figure.button.fit-base-to-parts')}\n </button>`\n : nothing}\n `)\n }\n\n /**\n * 넘친 것을 담는 길 하나를 여기서도 내민다 — **같은 동작이 속성 패널에도 있다**(「부품에 맞추기」).\n *\n * 그 단추를 아는 저작자는 거기서 누른다. 발행이 막혔다는 말을 여기서 읽은 사람은 그 단추가 있는\n * 것을 모르고, 문구는 그것을 가리킬 수 없다 — 문장은 형식이 쓰고 형식은 화면의 단추를 모른다.\n * 그래서 화면이 자기 자리에서 같은 동작을 낸다. 누르면 상자만 커지고 부품은 제자리이며, 저장은\n * 여전히 저작자가 따로 한다.\n */\n /**\n * 「부품에 맞추기」를 내미는 판정: 공간을 부품에서 다시 선언한다(`declare-occupancy`) — 부품은 그대로 두고 선언만\n * 바꾼다. 공간을 아직 선언하지 않은 도형도 같은 단추로 선언한다: 새로 만든 도형은 전부 그 판정으로 막히고, 화면에\n * 다른 길이 없다. 장착면이 떠 있는 것은 여기 없다 — 부품 하나를 평면에 붙여도 거기 붙지 않은 것은 따라오지 않는다.\n * 판정의 「어떻게」가 할 일을 말한다.\n */\n private offersFit(code: string): boolean {\n return code === 'part-outside-occupancy' || code === 'occupancy-undeclared'\n }\n\n private fitBase() {\n this.dispatchEvent(new CustomEvent('fit-base-to-parts', { bubbles: true, composed: true }))\n }\n\n /** Where one released version downloads as a package file, or undefined before the figure has a full name. */\n private packageHref(version: number): string | undefined {\n if (!this.figureNamespace || !this.figureType) return undefined\n const { contextPath } = getPathInfo(location.pathname)\n return `${contextPath}/figure-package?refs=${encodeURIComponent(`${this.figureNamespace}/${this.figureType}@${version}`)}`\n }\n\n private renderVersion(version: FigureVersion) {\n return html`\n <li>\n <span no>${i18next.t('figure.text.version-number', { version: version.version })}</span>\n <span when>\n ${version.updatedAt ? new Date(version.updatedAt).toLocaleString() : ''}\n ${version.updater?.name ? ` · ${version.updater.name}` : ''}\n </span>\n <button revert ?disabled=${this.busy} @click=${() => this.revert(version.version)}>\n ${i18next.t('figure.button.revert')}\n </button>\n ${this.packageHref(version.version)\n ? html`<a package download href=${this.packageHref(version.version)!}>${i18next.t('figure.button.download-package')}</a>`\n : nothing}\n ${version.comment ? html`<span comment>${version.comment}</span>` : nothing}\n </li>\n `\n }\n\n /*\n Only the newest can be taken back: an undo holds only while the figure is what that revert wrote, and a later\n revert or undo has written something else since. The older ones stay listed -- found, not offered.\n */\n private renderKept(kept: KeptDraft, newest: boolean) {\n const what =\n kept.kind === 'undo'\n ? i18next.t('figure.text.kept-before-undo')\n : i18next.t('figure.text.kept-before-revert', { version: String(kept.revertedToVersion ?? '') })\n return html`\n <li>\n <span no>${what}</span>\n <span when>\n ${kept.createdAt ? new Date(kept.createdAt).toLocaleString() : ''} ${kept.creator?.name ? ` · ${kept.creator.name}` : ''}\n ${kept.restoredAt ? ` · ${i18next.t('figure.text.kept-restored')}` : ''}\n </span>\n ${newest && !kept.restoredAt\n ? html`<button revert undo ?disabled=${this.busy} @click=${() => this.undo(kept)}>\n ${i18next.t(kept.kind === 'undo' ? 'figure.button.redo-revert' : 'figure.button.undo-revert')}\n </button>`\n : nothing}\n </li>\n `\n }\n\n private async release() {\n const input = this.renderRoot.querySelector('input[note]') as HTMLInputElement\n const comment = input?.value?.trim()\n\n this.busy = true\n this.failure = ''\n\n try {\n const figure = await releaseFigure(this.figureId!, comment || undefined)\n if (input) input.value = ''\n /* 정본을 든 자리가 갱신한다 — 여기서 페이지 상태를 고쳐 쓰지 않는다. */\n this.dispatchEvent(new CustomEvent('figure-released', { detail: { figure }, bubbles: true, composed: true }))\n await this.reload()\n await this.inspect()\n } catch (e) {\n this.failure = (e as Error).message\n } finally {\n this.busy = false\n }\n }\n\n /**\n * Edits not saved yet: saved first, let go, or kept -- asked with the three (designer 2026-09-27, Q-D1). Reverting and\n * taking a revert back both reopen the figure from the server, so edits not saved would go without a word. `true` to\n * go on.\n */\n private async settleUnsaved(purpose: 'revert' | 'undo', version = 0): Promise<boolean> {\n const choice = await openPopup<RevertChoice>(\n html`<figure-revert-choice .version=${version} .purpose=${purpose}></figure-revert-choice>`,\n { backdrop: true, size: 'small', title: i18next.t(purpose === 'undo' ? 'figure.button.undo-revert' : 'figure.button.revert') }\n ).result\n if (!choice) return false\n if (choice === 'save') {\n /* Saved, and known to be: a save that failed or whose outcome is not known goes no further. */\n const saved = await this.saveFirst?.()\n if (!saved?.ok) {\n this.failure = i18next.t('figure.text.revert-held-unsaved', { reason: saved?.reason ?? '' })\n return false\n }\n }\n return true\n }\n\n private async revert(version: number) {\n this.failure = ''\n if (this.dirty) {\n if (!(await this.settleUnsaved('revert', version))) return\n } else {\n /* 지금 초안은 서버가 보관하지만, 화면의 것이 그 판으로 바뀐다 — 이름으로 확인한다. */\n const agreed = await OxPrompt.open({\n type: 'warning',\n title: i18next.t('figure.button.revert'),\n text: i18next.t('figure.text.revert-to-version-confirm', { version }),\n confirmButton: { text: i18next.t('figure.button.revert') },\n cancelButton: { text: i18next.t('figure.button.keep-editing') }\n })\n if (!agreed) {\n return\n }\n }\n\n /* Read after any save above: what the server is now confirmed to hold. */\n const expected = this.serverSource?.()\n if (expected === undefined) {\n this.failure = i18next.t('figure.text.revert-needs-server-source')\n return\n }\n await this.send(`revert:${version}:${expected}`, requestId => revertFigureVersion(this.figureId!, version, requestId, expected))\n }\n\n private async undo(kept: KeptDraft) {\n this.failure = ''\n if (this.dirty && !(await this.settleUnsaved('undo'))) return\n await this.send(`undo:${kept.id}`, requestId => undoFigureRevert(kept.id, requestId))\n }\n\n /** One revert or undo: a new request id, or the one whose answer was lost when the same is asked again. */\n private async send(key: string, request: (requestId: string) => Promise<unknown>) {\n const sent = revertRequestFor(this.pending, key)\n this.pending = sent\n this.busy = true\n this.failure = ''\n\n try {\n const result = await request(sent.requestId)\n this.pending = undefined\n this.dispatchEvent(new CustomEvent('figure-reverted', { detail: result, bubbles: true, composed: true }))\n await this.reload()\n /*\n **되돌리면 정본이 바뀌므로 판정도 바뀐다.** 이것을 안 하면 옛 판의 사유가 새 정본 위에\n 남아 화면이 거짓말한다 — 되돌리기는 `dirty` 도 `figureVersion` 도 건드리지 않으므로\n `updated` 의 감시로는 다시 묻지 않는다.\n */\n await this.inspect()\n } catch (e) {\n const code = (e as { code?: string })?.code\n /* Not known whether it happened: kept, so the next press asks with the same id. Anything the server said: let go. */\n this.pending = afterRevertAnswer(sent, code)\n if (revertOutcomeUnknown(code)) this.failure = i18next.t('figure.text.revert-unconfirmed')\n else {\n /* Refused: the kept list may have changed under us (another session's undo), so it is read again. */\n await this.reload()\n this.failure = (e as Error).message\n }\n } finally {\n this.busy = false\n }\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- /** A type as typed, made into the shape a type has: letters, digits and underscores, upper case. */
1
+ /** A type as typed, made into the shape a type has: lower-case letters and digits joined by single hyphens. */
2
2
  export declare function typeOf(raw: string): string;
3
3
  /**
4
4
  * Whether this name could be released, by the format's own rules (`type-not-identifier`, `type-is-placeholder`).
@@ -9,7 +9,7 @@ export declare function releasableType(type: string): boolean;
9
9
  * An identifier offered for a new figure, so the first save asks for a name and has a valid identifier ready (designer
10
10
  * 2026-09-27, Q-D3). A name in plain letters is used as it is when it makes a free, releasable identifier; any other
11
11
  * name -- Korean, say -- gets a valid prefix and a random part: no transliteration is guessed. Never the temporary
12
- * shape (`FIGURE_` and eight), which the release gate refuses; never one this domain already uses.
12
+ * shape (`figure-` and eight), which the release gate refuses; never one this domain already uses.
13
13
  */
14
14
  export declare function suggestTypeOf(name: string, taken: readonly string[], random?: () => number): string;
15
15
  /** Why the first save cannot go on with this type, as an i18n key, or null when it can. */
@@ -4,9 +4,9 @@ import { isPlaceholderType, TYPE_LENGTH, TYPE_PATTERN } from '@hatiolab/figure-m
4
4
  * the figure by; it freezes when the figure is released (ADR-0063 decision 4). The modeller once saved a new figure
5
5
  * under a temporary machine name and asked nothing -- the author had to find the field afterwards (user, 2026-09-25).
6
6
  */
7
- /** A type as typed, made into the shape a type has: letters, digits and underscores, upper case. */
7
+ /** A type as typed, made into the shape a type has: lower-case letters and digits joined by single hyphens. */
8
8
  export function typeOf(raw) {
9
- return raw.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase();
9
+ return raw.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
10
10
  }
11
11
  /**
12
12
  * Whether this name could be released, by the format's own rules (`type-not-identifier`, `type-is-placeholder`).
@@ -19,15 +19,15 @@ export function releasableType(type) {
19
19
  * An identifier offered for a new figure, so the first save asks for a name and has a valid identifier ready (designer
20
20
  * 2026-09-27, Q-D3). A name in plain letters is used as it is when it makes a free, releasable identifier; any other
21
21
  * name -- Korean, say -- gets a valid prefix and a random part: no transliteration is guessed. Never the temporary
22
- * shape (`FIGURE_` and eight), which the release gate refuses; never one this domain already uses.
22
+ * shape (`figure-` and eight), which the release gate refuses; never one this domain already uses.
23
23
  */
24
24
  export function suggestTypeOf(name, taken, random = Math.random) {
25
- const fromName = /^[A-Za-z][A-Za-z0-9 _-]*$/.test(name.trim()) ? typeOf(name).replace(/_+/g, '_').replace(/_$/, '') : '';
25
+ const fromName = /^[A-Za-z][A-Za-z0-9 _-]*$/.test(name.trim()) ? typeOf(name) : '';
26
26
  if (fromName && releasableType(fromName) && !taken.includes(fromName))
27
27
  return fromName;
28
- const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
28
+ const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz';
29
29
  for (;;) {
30
- const made = `FIG_${Array.from({ length: 6 }, () => alphabet[Math.floor(random() * alphabet.length)]).join('')}`;
30
+ const made = `fig-${Array.from({ length: 6 }, () => alphabet[Math.floor(random() * alphabet.length)]).join('')}`;
31
31
  if (releasableType(made) && !taken.includes(made))
32
32
  return made;
33
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"first-save.js","sourceRoot":"","sources":["../../client/modeller/first-save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAErF;;;;GAIG;AAEH,oGAAoG;AACpG,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,WAAW,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,IAAI,WAAW,CAAC,GAAG,CAAA;AAChI,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAwB,EAAE,SAAuB,IAAI,CAAC,MAAM;IACtG,MAAM,QAAQ,GAAG,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACxH,IAAI,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAA;IACtF,MAAM,QAAQ,GAAG,sCAAsC,CAAA;IACvD,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAA;QAChH,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;IAChE,CAAC;AACH,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,KAAwB;IACvE,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,GAAG,EAAE,+BAA+B,EAAE,CAAA;IAC1D,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,qCAAqC,EAAE,IAAI,EAAE,CAAA;IACrF,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import { isPlaceholderType, TYPE_LENGTH, TYPE_PATTERN } from '@hatiolab/figure-model'\n\n/*\n * The first save of a new figure asks for its type and name. The type is the name a board places\n * the figure by; it freezes when the figure is released (ADR-0063 decision 4). The modeller once saved a new figure\n * under a temporary machine name and asked nothing -- the author had to find the field afterwards (user, 2026-09-25).\n */\n\n/** A type as typed, made into the shape a type has: letters, digits and underscores, upper case. */\nexport function typeOf(raw: string): string {\n return raw.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n}\n\n/**\n * Whether this name could be released, by the format's own rules (`type-not-identifier`, `type-is-placeholder`).\n * Saving under it is not blocked -- it can still change before release -- but the author hears it now.\n */\nexport function releasableType(type: string): boolean {\n return !isPlaceholderType(type) && TYPE_PATTERN.test(type) && type.length >= TYPE_LENGTH.min && type.length <= TYPE_LENGTH.max\n}\n\n/**\n * An identifier offered for a new figure, so the first save asks for a name and has a valid identifier ready (designer\n * 2026-09-27, Q-D3). A name in plain letters is used as it is when it makes a free, releasable identifier; any other\n * name -- Korean, say -- gets a valid prefix and a random part: no transliteration is guessed. Never the temporary\n * shape (`FIGURE_` and eight), which the release gate refuses; never one this domain already uses.\n */\nexport function suggestTypeOf(name: string, taken: readonly string[], random: () => number = Math.random): string {\n const fromName = /^[A-Za-z][A-Za-z0-9 _-]*$/.test(name.trim()) ? typeOf(name).replace(/_+/g, '_').replace(/_$/, '') : ''\n if (fromName && releasableType(fromName) && !taken.includes(fromName)) return fromName\n const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'\n for (;;) {\n const made = `FIG_${Array.from({ length: 6 }, () => alphabet[Math.floor(random() * alphabet.length)]).join('')}`\n if (releasableType(made) && !taken.includes(made)) return made\n }\n}\n\n/** Why the first save cannot go on with this type, as an i18n key, or null when it can. */\nexport function firstSaveRefusalOf(type: string, taken: readonly string[]): { key: string; type?: string } | null {\n if (!type) return { key: 'figure.text.enter-a-type-name' }\n if (taken.includes(type)) return { key: 'figure.text.type-name-already-taken', type }\n return null\n}\n\n/** What the first save asks for. */\nexport interface FirstSave {\n type: string\n name: string\n}\n"]}
1
+ {"version":3,"file":"first-save.js","sourceRoot":"","sources":["../../client/modeller/first-save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAErF;;;;GAIG;AAEH,+GAA+G;AAC/G,MAAM,UAAU,MAAM,CAAC,GAAW;IAChC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;AACrF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,WAAW,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,IAAI,WAAW,CAAC,GAAG,CAAA;AAChI,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAwB,EAAE,SAAuB,IAAI,CAAC,MAAM;IACtG,MAAM,QAAQ,GAAG,2BAA2B,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAClF,IAAI,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAA;IACtF,MAAM,QAAQ,GAAG,sCAAsC,CAAA;IACvD,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAA;QAChH,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;IAChE,CAAC;AACH,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,KAAwB;IACvE,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,GAAG,EAAE,+BAA+B,EAAE,CAAA;IAC1D,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,qCAAqC,EAAE,IAAI,EAAE,CAAA;IACrF,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import { isPlaceholderType, TYPE_LENGTH, TYPE_PATTERN } from '@hatiolab/figure-model'\n\n/*\n * The first save of a new figure asks for its type and name. The type is the name a board places\n * the figure by; it freezes when the figure is released (ADR-0063 decision 4). The modeller once saved a new figure\n * under a temporary machine name and asked nothing -- the author had to find the field afterwards (user, 2026-09-25).\n */\n\n/** A type as typed, made into the shape a type has: lower-case letters and digits joined by single hyphens. */\nexport function typeOf(raw: string): string {\n return raw.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '')\n}\n\n/**\n * Whether this name could be released, by the format's own rules (`type-not-identifier`, `type-is-placeholder`).\n * Saving under it is not blocked -- it can still change before release -- but the author hears it now.\n */\nexport function releasableType(type: string): boolean {\n return !isPlaceholderType(type) && TYPE_PATTERN.test(type) && type.length >= TYPE_LENGTH.min && type.length <= TYPE_LENGTH.max\n}\n\n/**\n * An identifier offered for a new figure, so the first save asks for a name and has a valid identifier ready (designer\n * 2026-09-27, Q-D3). A name in plain letters is used as it is when it makes a free, releasable identifier; any other\n * name -- Korean, say -- gets a valid prefix and a random part: no transliteration is guessed. Never the temporary\n * shape (`figure-` and eight), which the release gate refuses; never one this domain already uses.\n */\nexport function suggestTypeOf(name: string, taken: readonly string[], random: () => number = Math.random): string {\n const fromName = /^[A-Za-z][A-Za-z0-9 _-]*$/.test(name.trim()) ? typeOf(name) : ''\n if (fromName && releasableType(fromName) && !taken.includes(fromName)) return fromName\n const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz'\n for (;;) {\n const made = `fig-${Array.from({ length: 6 }, () => alphabet[Math.floor(random() * alphabet.length)]).join('')}`\n if (releasableType(made) && !taken.includes(made)) return made\n }\n}\n\n/** Why the first save cannot go on with this type, as an i18n key, or null when it can. */\nexport function firstSaveRefusalOf(type: string, taken: readonly string[]): { key: string; type?: string } | null {\n if (!type) return { key: 'figure.text.enter-a-type-name' }\n if (taken.includes(type)) return { key: 'figure.text.type-name-already-taken', type }\n return null\n}\n\n/** What the first save asks for. */\nexport interface FirstSave {\n type: string\n name: string\n}\n"]}
@@ -294,7 +294,8 @@ let FigureListPage = class FigureListPage extends FigureListPageBase {
294
294
  header: i18next.t('figure.label.figure-type'),
295
295
  record: {
296
296
  editable: false,
297
- renderer: (value) => html `<code>${value}</code>`
297
+ /* The full name a board stores: {namespace}/{type} (ADR-0114). */
298
+ renderer: (value, column, record) => html `<code>${record?.namespace ? `${record.namespace}/${value}` : value}</code>`
298
299
  },
299
300
  filter: 'search',
300
301
  sortable: true,
@@ -1 +1 @@
1
- {"version":3,"file":"figure-list-page.js","sourceRoot":"","sources":["../../client/pages/figure-list-page.ts"],"names":[],"mappings":";AAAA,OAAO,qBAAqB,CAAA;AAC5B,OAAO,4BAA4B,CAAA;AACnC,OAAO,8BAA8B,CAAA;AAErC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAGlE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,uCAAuC,CAAA;AAC5D,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAE/G,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAErE;;;;;GAKG;AACH,MAAM,eAAe,GAAG,EAAE,CAAA;AAE1B;;;;;;;GAOG;AACH,MAAM,QAAQ,GAAG,uDAAuD,CAAA;AACxE,MAAM,UAAU,GAAG,yDAAyD,CAAA;AAC5E,MAAM,QAAQ,GAAG,uDAAuD,CAAA;AACxE,MAAM,KAAK,GAAG,sCAAsC,CAAA;AACpD;;;;;;;;;;GAUG;AACH,MAAM,KAAK,GAAG,qCAAqC,CAAA;AACnD,MAAM,OAAO,GACX,6FAA6F;IAC7F,kEAAkE,CAAA;AAEpE;;;GAGG;AACH,MAAM,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;AAE5D;;;;;;;;;;;;;;;GAeG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,kBAAkB;aAC7C,WAAM,GAAG;QACd,6FAA6F;QAC7F,qBAAqB;QACrB,eAAe;QACf,iBAAiB;QACjB,kBAAkB;QAClB,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAoFF;KACF,AA3FY,CA2FZ;IAQD,IAAI,OAAO;QACT,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;YACxC,MAAM,EAAE;gBACN,OAAO,EAAE,CAAC,MAAc,EAAE,EAAE;oBAC1B,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAA;gBAChC,CAAC;gBACD,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,IAAI,EAAE;gBACnC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC;aACpD;YACD,MAAM,EAAE;gBACN,OAAO,EAAE,GAAG,EAAE;oBACZ,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAA;gBAC7B,CAAC;aACF;YACD;;;;;;cAME;SACH,CAAA;IACH,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,CAAA;;;;sBAIO,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;kBACpD,IAAI,CAAC,WAAW;wBACV,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;kCAClB,IAAI,CAAC,yBAAyB,CAAC,UAAU,CAAE;;;;;;;;;;;0BAWnD,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE;gCACnE,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;;KAEhE,CAAA;IACH,CAAC;IAED;;;;;;;;OAQG;IACK,SAAS,CAAC,GAAW;QAC3B,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAA;QAC3B,mDAAmD;QACnD,IAAI,CAAC,aAAa,EAAE,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC,WAAW,GAAG;YACjB;;;cAGE;YACF,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE;YAEnC;;;;cAIE;YACF,SAAS,EAAE;gBACT,IAAI,EAAE,eAAe;gBACrB,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC;gBAC7C,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;aACvD;YAED,IAAI,EAAE;gBACJ,mEAAmE;gBACnE,SAAS,EAAE,WAAW;gBACtB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;gBACxB;;;;;;;;;;;kBAWE;gBACF,OAAO,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,WAAW,CAAC;aAC5C;YAED,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE;gBACvD;oBACE;;;sBAGE;oBACF,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,QAAQ;oBACpB,IAAI,EAAE,QAAQ;oBACd,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;oBAC/C,MAAM,EAAE,IAAI;oBACZ,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,EAAE;oBACT,QAAQ,EAAE;wBACR,KAAK,EAAE,CAAC,QAAa,EAAE,KAAU,EAAE,OAAY,EAAE,MAAc,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;qBAC9F;iBACF;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,MAAM;oBACZ,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;oBACtC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,MAAM,EAAE,QAAQ;oBAChB,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE;;;sBAGE;oBACF,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,MAAM;oBACZ,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC;oBAC7C,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAA,SAAS,KAAK,SAAS;qBACzD;oBACD,MAAM,EAAE,QAAQ;oBAChB,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE;;;;;;;;;;;;sBAYE;oBACF,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,MAAM;oBACZ,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;oBACtC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,CAAC,KAAsB,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;qBAChE;oBACD,MAAM,EAAE,QAAQ;oBAChB,QAAQ,EAAE,KAAK;oBACf,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;oBACvC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf;;;;;;;0BAOE;wBACF,QAAQ,EAAE,CAAC,KAAa,EAAE,OAAY,EAAE,MAAc,EAAE,SAAiB,EAAE,KAAc,EAAE,EAAE;4BAC3F,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;gCACzB,OAAO,IAAI,CAAA,eAAe,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,SAAS,CAAA;4BAC7E,CAAC;4BAED,oDAAoD;4BACpD,OAAO,KAAK,EAAE,OAAO,KAAK,eAAe;gCACvC,CAAC,CAAC,EAAE;gCACJ,CAAC,CAAC,IAAI,CAAA,eAAe,QAAQ;uBACtB,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;sBAC1C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,4BAA4B,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;oBAC5F,CAAA;wBACR,CAAC;qBACF;oBACD;;;;;sBAKE;oBACF,MAAM,EAAE;wBACN,IAAI,EAAE,gBAAgB;wBACtB,QAAQ,EAAE,IAAI;wBACd,KAAK,EAAE,EAAE;wBACT,OAAO,EAAE;4BACP,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE;4BAC5D,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE;yBAC1E;qBACF;oBACD,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE;;;;;sBAKE;oBACF,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;oBACjD,QAAQ,EAAE,KAAK;oBACf,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,CAAC,MAAe,EAAE,OAAY,EAAE,MAAc,EAAE,EAAE;4BAC1D,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC,CAAA;4BAExE,OAAO,QAAQ;gCACb,CAAC,CAAC,IAAI,CAAA,eAAe,QAAQ,UAAU,OAAO,CAAC,CAAC,CAAC,mCAAmC,CAAC;sBAC/E,OAAO,CAAC,CAAC,CAAC,8BAA8B,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,CAAC;0BACjF;gCACV,CAAC,CAAC,IAAI,CAAA,eAAe,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC,SAAS,CAAA;wBACzF,CAAC;qBACF;oBACD,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,OAAO;oBACb,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;oBAC1C,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf;;;;;;;;;;;0BAWE;wBACF,QAAQ,EAAE,CAAC,KAAoB,EAAE,EAAE,CACjC,KAAK,IAAI,IAAI;4BACX,CAAC,CAAC,IAAI,CAAA,eAAe,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC,SAAS;4BACrF,CAAC,CAAC,IAAI,CAAA,eAAe,KAAK,UAAU,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;sBAChE,KAAK,GAAG,eAAe,CAAC,CAAC,CAAC,IAAI,CAAA,YAAY,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;0BAC7E;qBACf;oBACD,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,EAAE;iBACV;gBACD;oBACE,IAAI,EAAE,SAAS;oBACf,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;oBAC3C,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf;;;;0BAIE;wBACF,QAAQ,EAAE,CAAC,KAAoB,EAAE,EAAE,CACjC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;qBACjF;oBACD,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,SAAS;oBACf,IAAI,EAAE,QAAQ;oBACd,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,4BAA4B,CAAC;oBAC/C,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,MAAM,EAAE,IAAI;oBACZ,KAAK,EAAE,CAAC;iBACT;gBACD;oBACE;;;;;;;;;;sBAUE;oBACF,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC;oBACpC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,KAAK,EAAE,QAAQ;wBACf,IAAI,EAAE,IAAI;wBACV,WAAW,EAAE,IAAI;wBACjB,QAAQ,EAAE,CAAC,MAAe,EAAE,OAAY,EAAE,MAAc,EAAE,EAAE,CAAC,IAAI,CAAA;sCACvC,MAAM,YAAY,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;aAC1E;qBACF;oBACD,MAAM,EAAE,IAAI;oBACZ,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE,WAAW;oBACjB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC;oBACrC,KAAK,EAAE,IAAI;oBACX,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;oBAC3B,QAAQ,EAAE,IAAI;oBACd,KAAK,EAAE,GAAG;iBACX;gBACD;oBACE;;;;sBAIE;oBACF,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,SAAS;oBACf,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;oBAClC,MAAM,EAAE;wBACN,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,CAAC,KAA+B,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE;qBACjE;oBACD,MAAM,EAAE,IAAI;oBACZ,KAAK,EAAE,GAAG;iBACX;aACF;YAED,IAAI,EAAE;gBACJ,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE;oBACR,KAAK,EAAE,CAAC,QAAa,EAAE,KAAU,EAAE,OAAY,EAAE,MAAc,EAAE,EAAE;wBACjE,IAAI,MAAM,EAAE,EAAE,EAAE,CAAC;4BACf,gBAAgB,CAAC,MAAM,CAAC,CAAA;wBAC1B,CAAC;oBACH,CAAC;iBACF;aACF;YAED,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;SAC7C,CAAA;IACH,CAAC;IAED,iBAAiB;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QAEzB;;;;;;;UAOE;QACF,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE;YACjB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;YACrB,CAAC;QACH,CAAC,CAAA;QACD,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACjC,CAAC;IAED,oBAAoB;QAClB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;YAClC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAA;QACzB,CAAC;QACD,KAAK,CAAC,oBAAoB,EAAE,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QACpB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAe;QACnF,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;QAElF,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,EAAE,CAAA;IACpD,CAAC;IAED,2GAA2G;IACnG,YAAY,CAAC,MAAe;QAClC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAA;IAClC,CAAC;IAED,sDAAsD;IAC9C,KAAK,CAAC,YAAY,CAAC,MAAc;QACvC,qCAAqC;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,mCAAmC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;QACrF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,OAAM;QACR,CAAC;QAED,MAAM,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;;AA9a2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;mDAAiB;AAEjB;IAA1B,KAAK,CAAC,UAAU,CAAC;;6CAA0B;AAhGjC,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CA6gB1B","sourcesContent":["import '@operato/data-grist'\nimport '@material/web/icon/icon.js'\nimport '../viewparts/figure-thumb.js'\n\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport type { DataGrist, FetchOption } from '@operato/data-grist'\nimport { i18next, localize } from '@operato/i18n'\nimport { p13n } from '@operato/p13n'\nimport { auth } from '@things-factory/auth-base/dist-client'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\n\nimport { capacityFrom } from '../modeller/scene-budget.js'\nimport { deleteFigure, fetchFigureList } from '../graphql/index.js'\nimport type { Figure } from '../types.js'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { openFigureDetail } from '../viewparts/figure-detail-dock.js'\n\n/**\n * 이 값 아래면 점수를 색으로 눈에 띄게 한다.\n *\n * **등급 경계가 아니다.** 등급(A~D)은 `figure-model` 의 `scoreOf` 가 정하고 화면은 그것을\n * 베끼지 않는다. 이것은 「눈에 걸리게 할 선」이고, 화면의 판단이다.\n */\nconst ATTENTION_BELOW = 50\n\n/*\n * ── renderer 가 그리는 것은 이 파일의 CSS 가 닿지 않는다 ──────────────────────\n *\n * 컬럼 renderer 의 결과는 `ox-card-field` 의 shadow root 안에 그려진다. 이 페이지의 `static\n * styles` 는 그 경계를 넘지 못하므로 `code[figure-type]` 같은 선택자는 **아무 일도 하지 않는다.**\n * 넘어가는 것은 custom property 뿐이라, 색과 글꼴은 여기서 인라인으로 준다 — 값 자체는 테마\n * 토큰을 가리키므로 밝음·어둠은 따라온다. data-grist 의 image renderer 도 같은 방식이다.\n */\nconst CAPACITY = 'font-weight: 700; color: var(--md-sys-color-primary);'\nconst UNMEASURED = 'font-style: italic; color: var(--md-sys-color-outline);'\nconst RELEASED = 'font-weight: 700; color: var(--md-sys-color-primary);'\nconst DRAFT = 'color: var(--md-sys-color-tertiary);'\n/**\n * 조용히 적는 숫자. 낮은 것만 점으로 눈에 걸리게 한다.\n *\n * **`inline-flex` 를 쓰지 않는다.** 썼더니 점이 뜨는 카드에서만 이 값이 옆의 두 값과 밑줄이\n * 어긋났다 — inline-flex 상자의 밑줄은 **첫 항목**에서 오는데, 첫 항목이 글자 없는 점이라\n * 밑줄이 없어 브라우저가 상자 아래를 밑줄로 삼는다. 점이 없는 카드(50 이상)는 멀쩡했으므로\n * 한 줄에 세 값이 나란히 설 때만 눈에 띄었다.\n *\n * 그냥 인라인으로 두면 이 상자의 밑줄이 곧 숫자의 밑줄이라 옆의 둘과 맞는다. 점은 제 몫으로\n * 글자 가운데에 맞춘다.\n */\nconst SCORE = 'font-variant-numeric: tabular-nums;'\nconst LOW_DOT =\n 'display: inline-block; width: 5px; height: 5px; margin-right: 4px; vertical-align: middle; ' +\n 'border-radius: 50%; background-color: var(--md-sys-color-error);'\n\n/*\n * `as typeof PageView` 로 조이지 않는다 — 그렇게 하면 p13n 이 얹어 주는\n * `getPagePreferenceProvider` 가 타입에서 사라져 개인 설정을 붙일 수 없다.\n */\nconst FigureListPageBase = p13n(localize(i18next)(PageView))\n\n/**\n * Figure 카탈로그.\n *\n * 카드를 고르면 상세 패널에서 저장본과 발행판을 살펴본다. 크기와 움직임을 시험한 뒤\n * 「편집 화면 열기」로 저작면에 들어간다. 목록 질의는 자산 원본을 싣지 않는다.\n *\n * ## 왜 손으로 만든 카드가 아니라 격자인가\n *\n * 전에는 카드 격자와 걸러 보기 줄과 빈 화면을 이 파일이 직접 그렸다(200줄). 그 자리에서 빠져\n * 있던 것들은 하나같이 `ox-grist` 가 이미 가진 것이다 — 쪽 나누기, 정렬, 컬럼 걸러 보기,\n * 개인 설정(모드·정렬·쪽 크기를 사람마다 기억한다), 표 모드로 바꿔 보기, 내보내기.\n * 손으로 만든 카드는 그 중 아무것도 없었고, 목록이 늘어나면 전부 한 번에 그렸다.\n *\n * 카드에 실을 것은 `list` 가 정하고, 무엇을 어떻게 그릴지는 컬럼의 renderer 가 정한다. 같은\n * 설정이 표 모드에서도 그대로 쓰이므로 **두 모드가 다른 말을 하지 않는다.**\n */\n@customElement('figure-list-page')\nexport class FigureListPage extends FigureListPageBase {\n static styles = [\n /* The frame owns the page shell; the grist is the content it sizes (ADR-0060 decision 8). */\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n css`\n :host {\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n /* 떠 있는 단추의 기준 상자 */\n position: relative;\n }\n\n /*\n ── 만들기 단추 ────────────────────────────────────────────────────────\n 이 화면의 주된 동작 하나. 목록 위에 떠 있고 자리를 먹지 않는다.\n\n 글자를 함께 둔다(extended FAB). 아이콘만 두면 처음 온 사람은 + 표가 무엇을 만드는지\n 모른다 — 이 화면에서 만들 수 있는 것이 무엇인지가 그 글자다.\n */\n button[fab] {\n position: absolute;\n right: var(--spacing-large, 12px);\n bottom: var(--spacing-large, 12px);\n z-index: 10;\n\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 10px 18px 10px 14px;\n font: var(--label-font, inherit);\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border: none;\n /* 떠 있는 것의 모서리는 알약이다 — 담는 면(6px)을 따르지 않는다 */\n border-radius: var(--md-sys-shape-corner-full, 999px);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);\n cursor: pointer;\n }\n button[fab]:hover {\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n }\n button[fab] md-icon {\n --md-icon-size: 20px;\n }\n\n /* 격자가 바깥에 요구하는 것은 확정된 높이 하나뿐이다 — 스크롤은 격자가 안에서 한다 */\n /*\n ── 그림이 주인공인 카탈로그 ────────────────────────────────────────\n 카드 기본값은 글이 많은 목록(직원·결재함)에 맞춰져 있다. 여기서는 사람이 먼저 보는 것이\n 형상 그림이므로, 그림 칸을 비율로 잡아 카드 폭이 달라져도 그림이 나란히 읽히게 한다.\n\n 그림은 카드 끝까지 닿는다 — 위쪽 모서리만 카드와 같은 6px 로 깎아 한 장처럼 보이게 하고,\n 그림 안에 얹히는 초안 표시·삭제는 figure-thumb 이 그린다.\n */\n ox-grist {\n --data-card-min-width: 240px;\n --data-card-record-card-border-radius: 6px;\n --data-card-record-card-padding: 0;\n --data-card-thumbnail-height: auto;\n --data-card-thumbnail-aspect: 4 / 3;\n --data-card-thumbnail-padding: 0;\n --data-card-thumbnail-radius: 6px 6px 0 0;\n --data-card-thumbnail-background: var(--md-sys-color-surface-container);\n\n /*\n 카드 기본 글꼴은 굵다(제목 bold 1.1em). 글이 정보를 나르는 목록에서는 맞지만, 여기서\n 정보를 나르는 것은 그림이다. 굵은 제목이 카드마다 서면 그림보다 글이 먼저 읽힌다 —\n 이름은 알아볼 정도로만, 타입은 곁글로 둔다.\n */\n --data-card-item-name-font: 500 0.9rem/1.35 var(--theme-font, 'Roboto');\n --data-card-item-name-color: var(--md-sys-color-on-surface);\n --data-card-item-disc-font: normal 0.72rem/1.2 var(--theme-font, 'Roboto');\n --data-card-item-disc-color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n --data-card-item-etc-font: normal 0.72rem/1.2 var(--theme-font, 'Roboto');\n\n /* 상태와 성숙도는 짧은 표식이다 — 두 줄로 세우지 않고 한 줄에 뉜다 */\n --data-card-detail-direction: row;\n\n /*\n 맨 위로 가는 화살표를 만들기 단추 **위로** 올린다. 둘 다 오른쪽 아래에 떠 있으므로\n 그냥 두면 겹친다 — 단추(36px + 아래 여백 12px) 위에 12px 을 띄운 자리다.\n */\n --data-card-fab-position-vertical: 64px;\n --data-list-fab-position-vertical: 64px;\n }\n `\n ]\n\n @property({ type: Object }) gristConfig: any\n\n @query('ox-grist') private grist!: DataGrist\n\n private listen?: () => void\n\n get context() {\n return {\n title: i18next.t('figure.title.figures'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || '',\n placeholder: i18next.t('figure.text.search-figure')\n },\n filter: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n }\n /*\n **만들기는 `actions` 가 아니라 FAB 이다.**\n\n `context.actions` 로 두면 페이지 바닥 줄에 글자 단추로 서는데, 그 줄은 「지금 화면에 대해\n 할 일」을 늘어놓는 자리다. 만들기는 이 화면의 유일한 주된 동작이므로 줄에 섞이지 않고\n 떠 있어야 한다. 카드를 가리지 않는 오른쪽 아래, 손이 가는 자리다(아래 render 의 [fab]).\n */\n }\n }\n\n render() {\n return html`\n <ox-grist\n class=\"page-fill\"\n mode=\"CARD\"\n @search-tag=${(e: CustomEvent) => this.findByTag(e.detail.tag)}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <ox-filters-form autofocus without-search></ox-filters-form>\n </div>\n </div>\n\n <ox-grist-personalizer slot=\"setting\"></ox-grist-personalizer>\n </ox-grist>\n\n <button fab title=${i18next.t('figure.button.new-figure')} @click=${() => this.openModeller()}>\n <md-icon>add</md-icon>${i18next.t('figure.button.new-figure')}\n </button>\n `\n }\n\n /**\n * 카드의 태그 알약을 눌렀다 — **그 태그로 목록을 거른다.**\n *\n * 검색창에 그 말을 채워 넣는다. 격자에 조건만 걸고 창을 비워 두면 저작자는 **왜 목록이\n * 줄었는지 모른 채** 보게 되고, 되돌리려면 무엇을 지워야 하는지도 알 수 없다.\n *\n * 서버가 `tags` 뿐 아니라 이름·타입·설명·분류를 함께 훑으므로, 그 말이 다른 칸에 든 도형도\n * 같이 온다. 태그만 정확히 거르는 것이 아니라 **찾기**다.\n */\n private findByTag(tag: string) {\n this.grist.searchText = tag\n /* 창의 값은 `context` 에서 나온다 — 다시 알려야 헤더가 새 값을 그린다. */\n this.updateContext()\n }\n\n async pageInitialized() {\n this.gristConfig = {\n /*\n **쪽 나누기는 선택이 아니다.** 격자에 가상 스크롤이 없어 받은 것을 전부 DOM 에 그린다.\n 카드 모드는 끝까지 내려가면 다음 쪽을 이어 붙이므로, 한 쪽이 작아도 목록은 계속 읽힌다.\n */\n pagination: { pages: [24, 48, 96] },\n\n /*\n 아직 아무것도 없을 때 하는 말. **이 자리가 이 화면에서 가장 중요하다** — 처음 온 사람은\n 여기서 이 도구가 무엇을 만드는 것인지 알게 된다. 조건이 걸려 비었을 때는 격자가 스스로\n 다른 말을 한다(「찾는 것이 없다」). 그 둘을 섞지 않는 것이 격자 쪽에 이미 들어 있다.\n */\n emptyNote: {\n icon: 'deployed_code',\n title: i18next.t('figure.text.no-figure-yet'),\n description: i18next.t('figure.text.what-a-figure-is')\n },\n\n list: {\n /* 그림 칸은 그 컬럼의 renderer 가 그린다 — 그림 없을 때의 안내까지 `figure-thumb` 안이다 */\n thumbnail: 'thumbnail',\n fields: ['name', 'type'],\n /*\n ── 무게를 모른 채 고르게 두지 않는다 ────────────────────────────────\n 이름과 그림만 실으면 저작자는 **이 형상이 무거운지 모른 채** 고른다. 그것을 저작면에\n 들어가서야 아는 것은 늦다. 그래서 격자로 옮기기 전 손 카드가 실었던 셋을 그대로 싣는다 —\n 몇 개까지 놓을 수 있나(배치 한도) · 성숙도 · 삼각형 수.\n\n 한 줄에 뉜다(`--data-card-detail-direction: row`). 이름표는 달지 않는다 — 값 자체가\n 「625개까지」·「삼각형 440개」로 읽히고, 성숙도만 숫자 하나여서 손이 갔을 때 이름이 뜬다.\n\n 상태는 이 줄에 없다. 그림 위 초안 표시가 그 일을 한다(`figure-thumb`) — 발행된 것에는\n 아무 말도 하지 않으므로 빈 칸이 생기지 않는다.\n */\n details: ['capacity', 'score', 'triangles']\n },\n\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n /*\n 표 모드에서만 쓰는 삭제 칸. 카드에서는 `figure-thumb` 이 그림 위에 그리므로\n `forList: false` 로 내린다 — 카드 아래 단추 줄은 감춰도 자리가 남는다.\n */\n type: 'gutter',\n gutterName: 'button',\n icon: 'delete',\n title: i18next.t('figure.button.delete-figure'),\n danger: true,\n forList: false,\n width: 40,\n handlers: {\n click: (_columns: any, _data: any, _column: any, record: Figure) => this.removeFigure(record)\n }\n },\n {\n type: 'string',\n name: 'name',\n header: i18next.t('figure.label.name'),\n record: { editable: false },\n filter: 'search',\n sortable: true,\n width: 200\n },\n {\n /*\n 타입 이름은 **저장되는 식별자**다. 표시 이름과 다르게 보이게 두어야 도면이 무엇으로\n 이 형상을 찾는지 헷갈리지 않는다.\n */\n type: 'string',\n name: 'type',\n header: i18next.t('figure.label.figure-type'),\n record: {\n editable: false,\n renderer: (value: string) => html`<code>${value}</code>`\n },\n filter: 'search',\n sortable: true,\n width: 160\n },\n {\n /*\n 태그 — **카드에서 곁줄로 보이고 검색에 걸린다.**\n\n 서버가 이미 `type`·`name`·`description`·`category`·`tags` 를 함께 훑는다. 그런데\n 화면에는 태그가 어디에도 안 보여서, 저작자가 적은 태그가 **자기 눈에 돌아오지 않았다** —\n 적어 둔 것이 쓰이는지 알 수 없으면 아무도 적지 않는다.\n\n 정렬은 걸지 않는다. 배열이라 무엇을 기준으로 줄을 세우는지 답할 수 없다.\n\n **카드에서는 이 컬럼이 그리지 않는다.** 본문에 한 줄로 두었더니 태그가 있는 카드만\n 높아져 격자가 어긋났다 — 개수가 도형마다 다르므로 본문에 두는 한 남는 문제다.\n 카드에서는 `figure-thumb` 이 그림 위에 얹는다. 여기 남은 것은 표 모드의 칸이다.\n */\n type: 'string',\n name: 'tags',\n header: i18next.t('figure.label.tags'),\n record: {\n editable: false,\n renderer: (value: string[] | null) => (value || []).join(' · ')\n },\n filter: 'search',\n sortable: false,\n width: 180\n },\n {\n type: 'string',\n name: 'state',\n header: i18next.t('figure.label.state'),\n record: {\n editable: false,\n /*\n **발행된 것에는 아무 말도 하지 않는다.** 카탈로그에 있는 것은 대개 발행된 것이라,\n 카드마다 「발행됨」이 붙으면 그 말이 아무것도 가르지 않고 자리만 먹는다. 판 번호도\n 고를 때 쓰는 값이 아니다 — 이력 탭에 있다.\n\n 표 모드에서는 칸에 값이 있어야 하므로 그때는 발행됨도 적는다(`wide` 가 아니라 label\n 이 붙는 자리인지로 가른다 — 카드는 label 을 달지 않았다).\n */\n renderer: (value: string, _column: any, record: Figure, _rowIndex: number, field: Element) => {\n if (value !== 'released') {\n return html`<span style=${DRAFT}>${i18next.t('figure.label.draft')}</span>`\n }\n\n /* 표에서는 칸이 비면 안 된다. 카드에서만 입을 다문다 — 그리는 자리가 알려 준다. */\n return field?.tagName === 'OX-CARD-FIELD'\n ? ''\n : html`<span style=${RELEASED}\n >${i18next.t('figure.label.figure-released')}\n ${record.version ? i18next.t('figure.text.version-number', { version: record.version }) : ''}</span\n >`\n }\n },\n /*\n 전체·초안·발행됨을 단추로 고른다 — 값이 둘뿐인 조건을 펼침 목록에 넣지 않는다.\n\n 이름표는 비워 둔다. 단추에 「초안」·「발행됨」이라고 적혀 있으니 그 앞의 「상태」는\n 같은 말을 한 번 더 하는 것이고, 걸러 보기 줄에서 자리만 먹는다.\n */\n filter: {\n type: 'select-buttons',\n operator: 'in',\n label: '',\n options: [\n { display: i18next.t('figure.label.draft'), value: 'draft' },\n { display: i18next.t('figure.label.figure-released'), value: 'released' }\n ]\n },\n width: 120\n },\n {\n /*\n **몇 개까지 놓을 수 있나.** 카드에는 싣지 않고 표 모드의 칸으로 둔다 — 고른 뒤에\n 따지는 값이다. 서버에 없는 값이라 `groups` 와 `triangles` 에서 계산하는데, 저작면\n 머리와 **같은 함수**를 쓴다. 두 화면이 같은 형상을 다른 잣대로 말하면 무엇을 고칠지\n 모른다.\n */\n type: 'string',\n name: 'capacity',\n header: i18next.t('figure.label.placement-limit'),\n sortable: false,\n record: {\n editable: false,\n renderer: (_value: unknown, _column: any, record: Figure) => {\n const capacity = capacityFrom(record.groups ?? 0, record.triangles ?? 0)\n\n return capacity\n ? html`<span style=${CAPACITY} title=${i18next.t('figure.text.reuse-score-explained')}>\n ${i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })}\n </span>`\n : html`<span style=${UNMEASURED}>${i18next.t('figure.text.not-measured-yet')}</span>`\n }\n },\n width: 120\n },\n {\n type: 'float',\n name: 'score',\n header: i18next.t('figure.label.maturity'),\n record: {\n editable: false,\n /*\n 카드에 남긴 하나뿐인 숫자. **이름표를 달지 않으므로** 무엇인지는 손이 갔을 때 뜨는\n 말이 알려 준다.\n\n 색칠한 배지로 두지 않는다. 옛 draw call 한도 3 으로 재던 때는 카탈로그 거의 모두가 0 점이라\n 붉은 덩어리가 카드마다 섰고 아무것도 가르지 못했다. 이 모델은 「한 화면 N 대」 목표에 견준\n 여유라(`figureScoreOf`, 2026-09-24) 퍼지지만, 숫자는 조용히 적고 낮은 것에만 **점 하나**를 붙인다.\n\n 아직 재지 않은 것을 0 으로 보여 주지 않는다 — 0 은 「가볍다」는 뜻이 되어 버린다.\n 등급 문자(A~D)도 여기서 만들지 않는다: 경계는 `figure-model` 의 `scoreOf` · `figureScoreOf` 가 정하고,\n 화면이 그것을 베끼면 두 벌이 되어 목록과 저작면이 다른 등급을 말한다.\n */\n renderer: (value: number | null) =>\n value == null\n ? html`<span style=${UNMEASURED}>${i18next.t('figure.text.not-measured-yet')}</span>`\n : html`<span style=${SCORE} title=${i18next.t('figure.label.maturity')}>\n ${value < ATTENTION_BELOW ? html`<i style=${LOW_DOT}></i>` : ''}${Math.round(value)}\n </span>`\n },\n sortable: true,\n width: 90\n },\n {\n type: 'integer',\n name: 'triangles',\n header: i18next.t('figure.label.triangles'),\n record: {\n editable: false,\n /*\n 카드에서는 이름표 없이 서므로 숫자만 두면 옆의 성숙도와 구별되지 않는다. 무엇의\n 수인지 값이 스스로 말하게 한다 — 표 모드에서는 머리글이 그 일을 하므로 겹치지만,\n 그 겹침이 카드에서 뜻을 잃는 것보다 낫다.\n */\n renderer: (value: number | null) =>\n value == null ? '' : i18next.t('figure.text.triangle-count', { count: value })\n },\n sortable: true,\n width: 110\n },\n {\n type: 'integer',\n name: 'groups',\n header: i18next.t('figure.label.budget-groups'),\n record: { editable: false },\n hidden: true,\n width: 0\n },\n {\n /*\n **카드의 그림 칸 전체를 이 renderer 가 그린다.**\n\n 기본 image renderer 는 그림만 그리고, 그 위에 얹혀야 하는 것(초안 표시·삭제)은 카드\n 아래 gutter 줄로 밀린다. 격자로 옮기기 전 손 카드가 그것을 그림 위에 두었고 그 편이\n 맞다 — 그래서 그림 칸을 `figure-thumb` 에게 넘긴다. hover 가 필요해 컴포넌트여야\n 하는 사연은 그 파일에 적혀 있다.\n\n `renderEmpty` — 그림이 아직 없는 것도 자리를 지킨다. 그 카드에도 초안 표시와 삭제가\n 있어야 하고, 「그림 없음」이라고 말하는 것도 `figure-thumb` 이 한다.\n */\n type: 'image',\n name: 'thumbnail',\n header: i18next.t('field.thumbnail'),\n record: {\n editable: false,\n align: 'center',\n wide: true,\n renderEmpty: true,\n renderer: (_value: unknown, _column: any, record: Figure) => html`\n <figure-thumb .figure=${record} @remove=${() => this.removeFigure(record)}></figure-thumb>\n `\n },\n hidden: true,\n width: 160\n },\n {\n type: 'datetime',\n name: 'updatedAt',\n header: i18next.t('field.updated_at'),\n label: true,\n record: { editable: false },\n sortable: true,\n width: 180\n },\n {\n /*\n `resource-object` 를 쓰지 않는다 — 그 renderer 는 다른 패키지(resource-ui)가 등록하고,\n 등록되지 않은 앱에서는 조용히 글자 renderer 로 떨어져 `[object Object]` 가 된다.\n 이름 하나 꺼내는 일에 그 위험을 질 이유가 없다.\n */\n type: 'string',\n name: 'updater',\n header: i18next.t('field.updater'),\n record: {\n editable: false,\n renderer: (value: { name?: string } | null) => value?.name || ''\n },\n hidden: true,\n width: 120\n }\n ],\n\n rows: {\n selectable: false,\n appendable: false,\n handlers: {\n click: (_columns: any, _data: any, _column: any, record: Figure) => {\n if (record?.id) {\n openFigureDetail(record)\n }\n }\n }\n },\n\n sorters: [{ name: 'updatedAt', desc: true }]\n }\n }\n\n connectedCallback() {\n super.connectedCallback()\n\n /*\n **로그인 정보가 도착하면 다시 불러온다.**\n\n 화면이 서는 것과 프로필이 오는 것은 순서가 정해져 있지 않고, 목록 질의는 로그인된 사람으로\n 나간다. 첫 질의가 프로필보다 앞서면 그 한 번이 실패하고, 그 뒤로 다시 묻는 사람이 없으면\n 화면은 비어 있는 채로 남는다. lite-menu 도 같은 신호로 같은 순서 문제를 푼다\n (`auth.on('profile')` — 그쪽 주석에 그 사연이 적혀 있다).\n */\n this.listen = () => {\n if (this.active) {\n this.grist?.fetch()\n }\n }\n auth.on('profile', this.listen)\n }\n\n disconnectedCallback() {\n if (this.listen) {\n auth.off?.('profile', this.listen)\n this.listen = undefined\n }\n super.disconnectedCallback()\n }\n\n async pageUpdated() {\n if (this.active) {\n this.grist.fetch()\n }\n }\n\n /**\n * 걸러 보기·정렬·쪽을 **격자가 만든 그대로** 서버에 넘긴다.\n *\n * 여기서 이름을 바꿔 담으면 격자가 아는 조건과 서버에 가는 조건이 어긋난다. 실패는 격자가\n * 받아 화면에 낸다 — 목록이 비어 있는 것과 못 불러온 것은 다른 사실이다.\n */\n async fetchHandler({ page = 1, limit = 24, sortings = [], filters = [] }: FetchOption) {\n const { items, total } = await fetchFigureList({ page, limit, filters, sortings })\n\n return { total: total || 0, records: items || [] }\n }\n\n /** The modeller that can edit this figure, by its format; a new figure starts in (`modeller-route.ts`). */\n private openModeller(figure?: Figure) {\n navigate(modellerPathOf(figure))\n }\n\n /** `remove` 는 HTMLElement 의 메서드다 — 가리면 조용한 결함이 된다. */\n private async removeFigure(figure: Figure) {\n // 되돌릴 수 없는 조작이므로 무엇이 사라지는지 이름으로 확인한다\n const message = i18next.t('figure.text.delete-figure-confirm', { name: figure.name })\n if (!confirm(message)) {\n return\n }\n\n await deleteFigure(figure.id)\n this.grist.fetch()\n }\n}\n"]}
1
+ {"version":3,"file":"figure-list-page.js","sourceRoot":"","sources":["../../client/pages/figure-list-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/data-grist'\nimport '@material/web/icon/icon.js'\nimport '../viewparts/figure-thumb.js'\n\nimport { css, html } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\n\nimport type { DataGrist, FetchOption } from '@operato/data-grist'\nimport { i18next, localize } from '@operato/i18n'\nimport { p13n } from '@operato/p13n'\nimport { auth } from '@things-factory/auth-base/dist-client'\nimport { navigate, PageView } from '@operato/shell'\nimport { CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\n\nimport { capacityFrom } from '../modeller/scene-budget.js'\nimport { deleteFigure, fetchFigureList } from '../graphql/index.js'\nimport type { Figure } from '../types.js'\nimport { modellerPathOf } from '../modeller/modeller-route.js'\nimport { openFigureDetail } from '../viewparts/figure-detail-dock.js'\n\n/**\n * 이 값 아래면 점수를 색으로 눈에 띄게 한다.\n *\n * **등급 경계가 아니다.** 등급(A~D)은 `figure-model` 의 `scoreOf` 가 정하고 화면은 그것을\n * 베끼지 않는다. 이것은 「눈에 걸리게 할 선」이고, 화면의 판단이다.\n */\nconst ATTENTION_BELOW = 50\n\n/*\n * ── renderer 가 그리는 것은 이 파일의 CSS 가 닿지 않는다 ──────────────────────\n *\n * 컬럼 renderer 의 결과는 `ox-card-field` 의 shadow root 안에 그려진다. 이 페이지의 `static\n * styles` 는 그 경계를 넘지 못하므로 `code[figure-type]` 같은 선택자는 **아무 일도 하지 않는다.**\n * 넘어가는 것은 custom property 뿐이라, 색과 글꼴은 여기서 인라인으로 준다 — 값 자체는 테마\n * 토큰을 가리키므로 밝음·어둠은 따라온다. data-grist 의 image renderer 도 같은 방식이다.\n */\nconst CAPACITY = 'font-weight: 700; color: var(--md-sys-color-primary);'\nconst UNMEASURED = 'font-style: italic; color: var(--md-sys-color-outline);'\nconst RELEASED = 'font-weight: 700; color: var(--md-sys-color-primary);'\nconst DRAFT = 'color: var(--md-sys-color-tertiary);'\n/**\n * 조용히 적는 숫자. 낮은 것만 점으로 눈에 걸리게 한다.\n *\n * **`inline-flex` 를 쓰지 않는다.** 썼더니 점이 뜨는 카드에서만 이 값이 옆의 두 값과 밑줄이\n * 어긋났다 — inline-flex 상자의 밑줄은 **첫 항목**에서 오는데, 첫 항목이 글자 없는 점이라\n * 밑줄이 없어 브라우저가 상자 아래를 밑줄로 삼는다. 점이 없는 카드(50 이상)는 멀쩡했으므로\n * 한 줄에 세 값이 나란히 설 때만 눈에 띄었다.\n *\n * 그냥 인라인으로 두면 이 상자의 밑줄이 곧 숫자의 밑줄이라 옆의 둘과 맞는다. 점은 제 몫으로\n * 글자 가운데에 맞춘다.\n */\nconst SCORE = 'font-variant-numeric: tabular-nums;'\nconst LOW_DOT =\n 'display: inline-block; width: 5px; height: 5px; margin-right: 4px; vertical-align: middle; ' +\n 'border-radius: 50%; background-color: var(--md-sys-color-error);'\n\n/*\n * `as typeof PageView` 로 조이지 않는다 — 그렇게 하면 p13n 이 얹어 주는\n * `getPagePreferenceProvider` 가 타입에서 사라져 개인 설정을 붙일 수 없다.\n */\nconst FigureListPageBase = p13n(localize(i18next)(PageView))\n\n/**\n * Figure 카탈로그.\n *\n * 카드를 고르면 상세 패널에서 저장본과 발행판을 살펴본다. 크기와 움직임을 시험한 뒤\n * 「편집 화면 열기」로 저작면에 들어간다. 목록 질의는 자산 원본을 싣지 않는다.\n *\n * ## 왜 손으로 만든 카드가 아니라 격자인가\n *\n * 전에는 카드 격자와 걸러 보기 줄과 빈 화면을 이 파일이 직접 그렸다(200줄). 그 자리에서 빠져\n * 있던 것들은 하나같이 `ox-grist` 가 이미 가진 것이다 — 쪽 나누기, 정렬, 컬럼 걸러 보기,\n * 개인 설정(모드·정렬·쪽 크기를 사람마다 기억한다), 표 모드로 바꿔 보기, 내보내기.\n * 손으로 만든 카드는 그 중 아무것도 없었고, 목록이 늘어나면 전부 한 번에 그렸다.\n *\n * 카드에 실을 것은 `list` 가 정하고, 무엇을 어떻게 그릴지는 컬럼의 renderer 가 정한다. 같은\n * 설정이 표 모드에서도 그대로 쓰이므로 **두 모드가 다른 말을 하지 않는다.**\n */\n@customElement('figure-list-page')\nexport class FigureListPage extends FigureListPageBase {\n static styles = [\n /* The frame owns the page shell; the grist is the content it sizes (ADR-0060 decision 8). */\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n css`\n :host {\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n /* 떠 있는 단추의 기준 상자 */\n position: relative;\n }\n\n /*\n ── 만들기 단추 ────────────────────────────────────────────────────────\n 이 화면의 주된 동작 하나. 목록 위에 떠 있고 자리를 먹지 않는다.\n\n 글자를 함께 둔다(extended FAB). 아이콘만 두면 처음 온 사람은 + 표가 무엇을 만드는지\n 모른다 — 이 화면에서 만들 수 있는 것이 무엇인지가 그 글자다.\n */\n button[fab] {\n position: absolute;\n right: var(--spacing-large, 12px);\n bottom: var(--spacing-large, 12px);\n z-index: 10;\n\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 10px 18px 10px 14px;\n font: var(--label-font, inherit);\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border: none;\n /* 떠 있는 것의 모서리는 알약이다 — 담는 면(6px)을 따르지 않는다 */\n border-radius: var(--md-sys-shape-corner-full, 999px);\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);\n cursor: pointer;\n }\n button[fab]:hover {\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n }\n button[fab] md-icon {\n --md-icon-size: 20px;\n }\n\n /* 격자가 바깥에 요구하는 것은 확정된 높이 하나뿐이다 — 스크롤은 격자가 안에서 한다 */\n /*\n ── 그림이 주인공인 카탈로그 ────────────────────────────────────────\n 카드 기본값은 글이 많은 목록(직원·결재함)에 맞춰져 있다. 여기서는 사람이 먼저 보는 것이\n 형상 그림이므로, 그림 칸을 비율로 잡아 카드 폭이 달라져도 그림이 나란히 읽히게 한다.\n\n 그림은 카드 끝까지 닿는다 — 위쪽 모서리만 카드와 같은 6px 로 깎아 한 장처럼 보이게 하고,\n 그림 안에 얹히는 초안 표시·삭제는 figure-thumb 이 그린다.\n */\n ox-grist {\n --data-card-min-width: 240px;\n --data-card-record-card-border-radius: 6px;\n --data-card-record-card-padding: 0;\n --data-card-thumbnail-height: auto;\n --data-card-thumbnail-aspect: 4 / 3;\n --data-card-thumbnail-padding: 0;\n --data-card-thumbnail-radius: 6px 6px 0 0;\n --data-card-thumbnail-background: var(--md-sys-color-surface-container);\n\n /*\n 카드 기본 글꼴은 굵다(제목 bold 1.1em). 글이 정보를 나르는 목록에서는 맞지만, 여기서\n 정보를 나르는 것은 그림이다. 굵은 제목이 카드마다 서면 그림보다 글이 먼저 읽힌다 —\n 이름은 알아볼 정도로만, 타입은 곁글로 둔다.\n */\n --data-card-item-name-font: 500 0.9rem/1.35 var(--theme-font, 'Roboto');\n --data-card-item-name-color: var(--md-sys-color-on-surface);\n --data-card-item-disc-font: normal 0.72rem/1.2 var(--theme-font, 'Roboto');\n --data-card-item-disc-color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n --data-card-item-etc-font: normal 0.72rem/1.2 var(--theme-font, 'Roboto');\n\n /* 상태와 성숙도는 짧은 표식이다 — 두 줄로 세우지 않고 한 줄에 뉜다 */\n --data-card-detail-direction: row;\n\n /*\n 맨 위로 가는 화살표를 만들기 단추 **위로** 올린다. 둘 다 오른쪽 아래에 떠 있으므로\n 그냥 두면 겹친다 — 단추(36px + 아래 여백 12px) 위에 12px 을 띄운 자리다.\n */\n --data-card-fab-position-vertical: 64px;\n --data-list-fab-position-vertical: 64px;\n }\n `\n ]\n\n @property({ type: Object }) gristConfig: any\n\n @query('ox-grist') private grist!: DataGrist\n\n private listen?: () => void\n\n get context() {\n return {\n title: i18next.t('figure.title.figures'),\n search: {\n handler: (search: string) => {\n this.grist.searchText = search\n },\n value: this.grist?.searchText || '',\n placeholder: i18next.t('figure.text.search-figure')\n },\n filter: {\n handler: () => {\n this.grist.toggleHeadroom()\n }\n }\n /*\n **만들기는 `actions` 가 아니라 FAB 이다.**\n\n `context.actions` 로 두면 페이지 바닥 줄에 글자 단추로 서는데, 그 줄은 「지금 화면에 대해\n 할 일」을 늘어놓는 자리다. 만들기는 이 화면의 유일한 주된 동작이므로 줄에 섞이지 않고\n 떠 있어야 한다. 카드를 가리지 않는 오른쪽 아래, 손이 가는 자리다(아래 render 의 [fab]).\n */\n }\n }\n\n render() {\n return html`\n <ox-grist\n class=\"page-fill\"\n mode=\"CARD\"\n @search-tag=${(e: CustomEvent) => this.findByTag(e.detail.tag)}\n .config=${this.gristConfig}\n .fetchHandler=${this.fetchHandler.bind(this)}\n .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}\n >\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">\n <ox-filters-form autofocus without-search></ox-filters-form>\n </div>\n </div>\n\n <ox-grist-personalizer slot=\"setting\"></ox-grist-personalizer>\n </ox-grist>\n\n <button fab title=${i18next.t('figure.button.new-figure')} @click=${() => this.openModeller()}>\n <md-icon>add</md-icon>${i18next.t('figure.button.new-figure')}\n </button>\n `\n }\n\n /**\n * 카드의 태그 알약을 눌렀다 — **그 태그로 목록을 거른다.**\n *\n * 검색창에 그 말을 채워 넣는다. 격자에 조건만 걸고 창을 비워 두면 저작자는 **왜 목록이\n * 줄었는지 모른 채** 보게 되고, 되돌리려면 무엇을 지워야 하는지도 알 수 없다.\n *\n * 서버가 `tags` 뿐 아니라 이름·타입·설명·분류를 함께 훑으므로, 그 말이 다른 칸에 든 도형도\n * 같이 온다. 태그만 정확히 거르는 것이 아니라 **찾기**다.\n */\n private findByTag(tag: string) {\n this.grist.searchText = tag\n /* 창의 값은 `context` 에서 나온다 — 다시 알려야 헤더가 새 값을 그린다. */\n this.updateContext()\n }\n\n async pageInitialized() {\n this.gristConfig = {\n /*\n **쪽 나누기는 선택이 아니다.** 격자에 가상 스크롤이 없어 받은 것을 전부 DOM 에 그린다.\n 카드 모드는 끝까지 내려가면 다음 쪽을 이어 붙이므로, 한 쪽이 작아도 목록은 계속 읽힌다.\n */\n pagination: { pages: [24, 48, 96] },\n\n /*\n 아직 아무것도 없을 때 하는 말. **이 자리가 이 화면에서 가장 중요하다** — 처음 온 사람은\n 여기서 이 도구가 무엇을 만드는 것인지 알게 된다. 조건이 걸려 비었을 때는 격자가 스스로\n 다른 말을 한다(「찾는 것이 없다」). 그 둘을 섞지 않는 것이 격자 쪽에 이미 들어 있다.\n */\n emptyNote: {\n icon: 'deployed_code',\n title: i18next.t('figure.text.no-figure-yet'),\n description: i18next.t('figure.text.what-a-figure-is')\n },\n\n list: {\n /* 그림 칸은 그 컬럼의 renderer 가 그린다 — 그림 없을 때의 안내까지 `figure-thumb` 안이다 */\n thumbnail: 'thumbnail',\n fields: ['name', 'type'],\n /*\n ── 무게를 모른 채 고르게 두지 않는다 ────────────────────────────────\n 이름과 그림만 실으면 저작자는 **이 형상이 무거운지 모른 채** 고른다. 그것을 저작면에\n 들어가서야 아는 것은 늦다. 그래서 격자로 옮기기 전 손 카드가 실었던 셋을 그대로 싣는다 —\n 몇 개까지 놓을 수 있나(배치 한도) · 성숙도 · 삼각형 수.\n\n 한 줄에 뉜다(`--data-card-detail-direction: row`). 이름표는 달지 않는다 — 값 자체가\n 「625개까지」·「삼각형 440개」로 읽히고, 성숙도만 숫자 하나여서 손이 갔을 때 이름이 뜬다.\n\n 상태는 이 줄에 없다. 그림 위 초안 표시가 그 일을 한다(`figure-thumb`) — 발행된 것에는\n 아무 말도 하지 않으므로 빈 칸이 생기지 않는다.\n */\n details: ['capacity', 'score', 'triangles']\n },\n\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n /*\n 표 모드에서만 쓰는 삭제 칸. 카드에서는 `figure-thumb` 이 그림 위에 그리므로\n `forList: false` 로 내린다 — 카드 아래 단추 줄은 감춰도 자리가 남는다.\n */\n type: 'gutter',\n gutterName: 'button',\n icon: 'delete',\n title: i18next.t('figure.button.delete-figure'),\n danger: true,\n forList: false,\n width: 40,\n handlers: {\n click: (_columns: any, _data: any, _column: any, record: Figure) => this.removeFigure(record)\n }\n },\n {\n type: 'string',\n name: 'name',\n header: i18next.t('figure.label.name'),\n record: { editable: false },\n filter: 'search',\n sortable: true,\n width: 200\n },\n {\n /*\n 타입 이름은 **저장되는 식별자**다. 표시 이름과 다르게 보이게 두어야 도면이 무엇으로\n 이 형상을 찾는지 헷갈리지 않는다.\n */\n type: 'string',\n name: 'type',\n header: i18next.t('figure.label.figure-type'),\n record: {\n editable: false,\n /* The full name a board stores: {namespace}/{type} (ADR-0114). */\n renderer: (value: string, column: unknown, record: { namespace?: string }) =>\n html`<code>${record?.namespace ? `${record.namespace}/${value}` : value}</code>`\n },\n filter: 'search',\n sortable: true,\n width: 160\n },\n {\n /*\n 태그 — **카드에서 곁줄로 보이고 검색에 걸린다.**\n\n 서버가 이미 `type`·`name`·`description`·`category`·`tags` 를 함께 훑는다. 그런데\n 화면에는 태그가 어디에도 안 보여서, 저작자가 적은 태그가 **자기 눈에 돌아오지 않았다** —\n 적어 둔 것이 쓰이는지 알 수 없으면 아무도 적지 않는다.\n\n 정렬은 걸지 않는다. 배열이라 무엇을 기준으로 줄을 세우는지 답할 수 없다.\n\n **카드에서는 이 컬럼이 그리지 않는다.** 본문에 한 줄로 두었더니 태그가 있는 카드만\n 높아져 격자가 어긋났다 — 개수가 도형마다 다르므로 본문에 두는 한 남는 문제다.\n 카드에서는 `figure-thumb` 이 그림 위에 얹는다. 여기 남은 것은 표 모드의 칸이다.\n */\n type: 'string',\n name: 'tags',\n header: i18next.t('figure.label.tags'),\n record: {\n editable: false,\n renderer: (value: string[] | null) => (value || []).join(' · ')\n },\n filter: 'search',\n sortable: false,\n width: 180\n },\n {\n type: 'string',\n name: 'state',\n header: i18next.t('figure.label.state'),\n record: {\n editable: false,\n /*\n **발행된 것에는 아무 말도 하지 않는다.** 카탈로그에 있는 것은 대개 발행된 것이라,\n 카드마다 「발행됨」이 붙으면 그 말이 아무것도 가르지 않고 자리만 먹는다. 판 번호도\n 고를 때 쓰는 값이 아니다 — 이력 탭에 있다.\n\n 표 모드에서는 칸에 값이 있어야 하므로 그때는 발행됨도 적는다(`wide` 가 아니라 label\n 이 붙는 자리인지로 가른다 — 카드는 label 을 달지 않았다).\n */\n renderer: (value: string, _column: any, record: Figure, _rowIndex: number, field: Element) => {\n if (value !== 'released') {\n return html`<span style=${DRAFT}>${i18next.t('figure.label.draft')}</span>`\n }\n\n /* 표에서는 칸이 비면 안 된다. 카드에서만 입을 다문다 — 그리는 자리가 알려 준다. */\n return field?.tagName === 'OX-CARD-FIELD'\n ? ''\n : html`<span style=${RELEASED}\n >${i18next.t('figure.label.figure-released')}\n ${record.version ? i18next.t('figure.text.version-number', { version: record.version }) : ''}</span\n >`\n }\n },\n /*\n 전체·초안·발행됨을 단추로 고른다 — 값이 둘뿐인 조건을 펼침 목록에 넣지 않는다.\n\n 이름표는 비워 둔다. 단추에 「초안」·「발행됨」이라고 적혀 있으니 그 앞의 「상태」는\n 같은 말을 한 번 더 하는 것이고, 걸러 보기 줄에서 자리만 먹는다.\n */\n filter: {\n type: 'select-buttons',\n operator: 'in',\n label: '',\n options: [\n { display: i18next.t('figure.label.draft'), value: 'draft' },\n { display: i18next.t('figure.label.figure-released'), value: 'released' }\n ]\n },\n width: 120\n },\n {\n /*\n **몇 개까지 놓을 수 있나.** 카드에는 싣지 않고 표 모드의 칸으로 둔다 — 고른 뒤에\n 따지는 값이다. 서버에 없는 값이라 `groups` 와 `triangles` 에서 계산하는데, 저작면\n 머리와 **같은 함수**를 쓴다. 두 화면이 같은 형상을 다른 잣대로 말하면 무엇을 고칠지\n 모른다.\n */\n type: 'string',\n name: 'capacity',\n header: i18next.t('figure.label.placement-limit'),\n sortable: false,\n record: {\n editable: false,\n renderer: (_value: unknown, _column: any, record: Figure) => {\n const capacity = capacityFrom(record.groups ?? 0, record.triangles ?? 0)\n\n return capacity\n ? html`<span style=${CAPACITY} title=${i18next.t('figure.text.reuse-score-explained')}>\n ${i18next.t('figure.text.fits-n-instances', { n: capacity.instances.toLocaleString() })}\n </span>`\n : html`<span style=${UNMEASURED}>${i18next.t('figure.text.not-measured-yet')}</span>`\n }\n },\n width: 120\n },\n {\n type: 'float',\n name: 'score',\n header: i18next.t('figure.label.maturity'),\n record: {\n editable: false,\n /*\n 카드에 남긴 하나뿐인 숫자. **이름표를 달지 않으므로** 무엇인지는 손이 갔을 때 뜨는\n 말이 알려 준다.\n\n 색칠한 배지로 두지 않는다. 옛 draw call 한도 3 으로 재던 때는 카탈로그 거의 모두가 0 점이라\n 붉은 덩어리가 카드마다 섰고 아무것도 가르지 못했다. 이 모델은 「한 화면 N 대」 목표에 견준\n 여유라(`figureScoreOf`, 2026-09-24) 퍼지지만, 숫자는 조용히 적고 낮은 것에만 **점 하나**를 붙인다.\n\n 아직 재지 않은 것을 0 으로 보여 주지 않는다 — 0 은 「가볍다」는 뜻이 되어 버린다.\n 등급 문자(A~D)도 여기서 만들지 않는다: 경계는 `figure-model` 의 `scoreOf` · `figureScoreOf` 가 정하고,\n 화면이 그것을 베끼면 두 벌이 되어 목록과 저작면이 다른 등급을 말한다.\n */\n renderer: (value: number | null) =>\n value == null\n ? html`<span style=${UNMEASURED}>${i18next.t('figure.text.not-measured-yet')}</span>`\n : html`<span style=${SCORE} title=${i18next.t('figure.label.maturity')}>\n ${value < ATTENTION_BELOW ? html`<i style=${LOW_DOT}></i>` : ''}${Math.round(value)}\n </span>`\n },\n sortable: true,\n width: 90\n },\n {\n type: 'integer',\n name: 'triangles',\n header: i18next.t('figure.label.triangles'),\n record: {\n editable: false,\n /*\n 카드에서는 이름표 없이 서므로 숫자만 두면 옆의 성숙도와 구별되지 않는다. 무엇의\n 수인지 값이 스스로 말하게 한다 — 표 모드에서는 머리글이 그 일을 하므로 겹치지만,\n 그 겹침이 카드에서 뜻을 잃는 것보다 낫다.\n */\n renderer: (value: number | null) =>\n value == null ? '' : i18next.t('figure.text.triangle-count', { count: value })\n },\n sortable: true,\n width: 110\n },\n {\n type: 'integer',\n name: 'groups',\n header: i18next.t('figure.label.budget-groups'),\n record: { editable: false },\n hidden: true,\n width: 0\n },\n {\n /*\n **카드의 그림 칸 전체를 이 renderer 가 그린다.**\n\n 기본 image renderer 는 그림만 그리고, 그 위에 얹혀야 하는 것(초안 표시·삭제)은 카드\n 아래 gutter 줄로 밀린다. 격자로 옮기기 전 손 카드가 그것을 그림 위에 두었고 그 편이\n 맞다 — 그래서 그림 칸을 `figure-thumb` 에게 넘긴다. hover 가 필요해 컴포넌트여야\n 하는 사연은 그 파일에 적혀 있다.\n\n `renderEmpty` — 그림이 아직 없는 것도 자리를 지킨다. 그 카드에도 초안 표시와 삭제가\n 있어야 하고, 「그림 없음」이라고 말하는 것도 `figure-thumb` 이 한다.\n */\n type: 'image',\n name: 'thumbnail',\n header: i18next.t('field.thumbnail'),\n record: {\n editable: false,\n align: 'center',\n wide: true,\n renderEmpty: true,\n renderer: (_value: unknown, _column: any, record: Figure) => html`\n <figure-thumb .figure=${record} @remove=${() => this.removeFigure(record)}></figure-thumb>\n `\n },\n hidden: true,\n width: 160\n },\n {\n type: 'datetime',\n name: 'updatedAt',\n header: i18next.t('field.updated_at'),\n label: true,\n record: { editable: false },\n sortable: true,\n width: 180\n },\n {\n /*\n `resource-object` 를 쓰지 않는다 — 그 renderer 는 다른 패키지(resource-ui)가 등록하고,\n 등록되지 않은 앱에서는 조용히 글자 renderer 로 떨어져 `[object Object]` 가 된다.\n 이름 하나 꺼내는 일에 그 위험을 질 이유가 없다.\n */\n type: 'string',\n name: 'updater',\n header: i18next.t('field.updater'),\n record: {\n editable: false,\n renderer: (value: { name?: string } | null) => value?.name || ''\n },\n hidden: true,\n width: 120\n }\n ],\n\n rows: {\n selectable: false,\n appendable: false,\n handlers: {\n click: (_columns: any, _data: any, _column: any, record: Figure) => {\n if (record?.id) {\n openFigureDetail(record)\n }\n }\n }\n },\n\n sorters: [{ name: 'updatedAt', desc: true }]\n }\n }\n\n connectedCallback() {\n super.connectedCallback()\n\n /*\n **로그인 정보가 도착하면 다시 불러온다.**\n\n 화면이 서는 것과 프로필이 오는 것은 순서가 정해져 있지 않고, 목록 질의는 로그인된 사람으로\n 나간다. 첫 질의가 프로필보다 앞서면 그 한 번이 실패하고, 그 뒤로 다시 묻는 사람이 없으면\n 화면은 비어 있는 채로 남는다. lite-menu 도 같은 신호로 같은 순서 문제를 푼다\n (`auth.on('profile')` — 그쪽 주석에 그 사연이 적혀 있다).\n */\n this.listen = () => {\n if (this.active) {\n this.grist?.fetch()\n }\n }\n auth.on('profile', this.listen)\n }\n\n disconnectedCallback() {\n if (this.listen) {\n auth.off?.('profile', this.listen)\n this.listen = undefined\n }\n super.disconnectedCallback()\n }\n\n async pageUpdated() {\n if (this.active) {\n this.grist.fetch()\n }\n }\n\n /**\n * 걸러 보기·정렬·쪽을 **격자가 만든 그대로** 서버에 넘긴다.\n *\n * 여기서 이름을 바꿔 담으면 격자가 아는 조건과 서버에 가는 조건이 어긋난다. 실패는 격자가\n * 받아 화면에 낸다 — 목록이 비어 있는 것과 못 불러온 것은 다른 사실이다.\n */\n async fetchHandler({ page = 1, limit = 24, sortings = [], filters = [] }: FetchOption) {\n const { items, total } = await fetchFigureList({ page, limit, filters, sortings })\n\n return { total: total || 0, records: items || [] }\n }\n\n /** The modeller that can edit this figure, by its format; a new figure starts in (`modeller-route.ts`). */\n private openModeller(figure?: Figure) {\n navigate(modellerPathOf(figure))\n }\n\n /** `remove` 는 HTMLElement 의 메서드다 — 가리면 조용한 결함이 된다. */\n private async removeFigure(figure: Figure) {\n // 되돌릴 수 없는 조작이므로 무엇이 사라지는지 이름으로 확인한다\n const message = i18next.t('figure.text.delete-figure-confirm', { name: figure.name })\n if (!confirm(message)) {\n return\n }\n\n await deleteFigure(figure.id)\n this.grist.fetch()\n }\n}\n"]}
@@ -20,6 +20,7 @@ import { notify, openPopup } from '@operato/layout';
20
20
  import '../modeller/figure-first-save.js';
21
21
  import { activePalette } from '@hatiolab/things-scene';
22
22
  import { askAssistant, clearAssistantSurface, notifyAssistantSurface, provideAssistantSurface } from '@things-factory/ai-assistant/dist-client/index.js';
23
+ import { typeOf } from '../modeller/first-save.js';
23
24
  import { placementRefusalOf } from '../modeller/refusals.js';
24
25
  import { proposalOutcomeFrom, takeFigureOffer, takePendingNewFigure } from '../modeller/ai-proposal.js';
25
26
  /**
@@ -132,7 +133,7 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
132
133
  this.renameType = async (event) => {
133
134
  const input = event.target;
134
135
  const id = this.session.read().id;
135
- const wanted = input.value.trim().replace(/[^A-Za-z0-9_]/g, '_').toUpperCase();
136
+ const wanted = typeOf(input.value);
136
137
  input.value = wanted;
137
138
  /*
138
139
  A figure not saved yet has no row to rename: the type is held here and the first save's question opens with it.
@@ -356,6 +357,7 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
356
357
  this.row = {
357
358
  state: figure.state,
358
359
  version: figure.version ?? 0,
360
+ namespace: figure.namespace ?? undefined,
359
361
  type: figure.type,
360
362
  category: figure.category ?? undefined,
361
363
  tags: figure.tags ?? undefined
@@ -543,6 +545,7 @@ let FigureModellerPage = class FigureModellerPage extends PageView {
543
545
  .figureState=${this.row.state}
544
546
  .figureVersion=${this.row.version}
545
547
  .figureType=${this.row.type}
548
+ .figureNamespace=${this.row.namespace}
546
549
  .dirty=${!!view.dirty}
547
550
  .saveFirst=${this.saveBeforeRevert}
548
551
  .serverSource=${() => this.session.confirmedSource()}