@things-factory/figure-ui 10.1.94 → 10.1.96

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 (38) hide show
  1. package/client/modeller/editor.ts +30 -11
  2. package/client/modeller/figure-fasten-drawing.ts +0 -2
  3. package/client/modeller/figure-links.ts +22 -25
  4. package/client/modeller/figure-material-panel.ts +18 -11
  5. package/client/modeller/figure-properties.ts +2 -2
  6. package/client/modeller/figure-section-panel.ts +12 -11
  7. package/client/modeller/links.ts +49 -23
  8. package/client/modeller/side-panel-styles.ts +67 -32
  9. package/dist-client/modeller/editor.d.ts +7 -2
  10. package/dist-client/modeller/editor.js +31 -10
  11. package/dist-client/modeller/editor.js.map +1 -1
  12. package/dist-client/modeller/figure-fasten-drawing.js +0 -2
  13. package/dist-client/modeller/figure-fasten-drawing.js.map +1 -1
  14. package/dist-client/modeller/figure-links.js +24 -25
  15. package/dist-client/modeller/figure-links.js.map +1 -1
  16. package/dist-client/modeller/figure-material-panel.js +18 -11
  17. package/dist-client/modeller/figure-material-panel.js.map +1 -1
  18. package/dist-client/modeller/figure-properties.js +2 -2
  19. package/dist-client/modeller/figure-properties.js.map +1 -1
  20. package/dist-client/modeller/figure-section-panel.d.ts +1 -0
  21. package/dist-client/modeller/figure-section-panel.js +12 -11
  22. package/dist-client/modeller/figure-section-panel.js.map +1 -1
  23. package/dist-client/modeller/links.d.ts +14 -4
  24. package/dist-client/modeller/links.js +51 -23
  25. package/dist-client/modeller/links.js.map +1 -1
  26. package/dist-client/modeller/side-panel-styles.d.ts +5 -1
  27. package/dist-client/modeller/side-panel-styles.js +67 -32
  28. package/dist-client/modeller/side-panel-styles.js.map +1 -1
  29. package/dist-client/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +2 -2
  31. package/test/command-preview.test.ts +3 -3
  32. package/test/first-minute.test.ts +16 -7
  33. package/test/section-panel.test.ts +2 -2
  34. package/translations/en.json +18 -10
  35. package/translations/ja.json +18 -10
  36. package/translations/ko.json +18 -10
  37. package/translations/ms.json +18 -10
  38. package/translations/zh.json +18 -10
@@ -1 +1 @@
1
- {"version":3,"file":"figure-material-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-material-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { MATERIAL_PRESETS, SEGMENT_PRESETS, figureTrianglesOf } from '@hatiolab/figure-model'\nimport { activePalette } from '@hatiolab/things-scene'\nimport type { FigureAssetGeometry, FigureMaterial, FigurePartEdit } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { isRoundBody } from './starting-shape.js'\n\n/** The purpose each palette role is for, by its token. */\nconst PURPOSE: Record<string, string> = {\n 'palette.primary': 'primary',\n 'palette.accent': 'accent',\n 'palette.neutral': 'neutral',\n 'palette.dark': 'dark',\n 'palette.light': 'light',\n 'palette.surface': 'surface',\n 'palette.status-danger': 'danger',\n 'palette.status-warning': 'warning',\n 'palette.status-info': 'info',\n 'palette.status-success': 'success'\n}\nexport const purposeOf = (token?: string) => i18next.t(`figure.label.palette-purpose-${PURPOSE[token ?? ''] ?? 'registered'}`)\n\n/** Bodies with a rim — the ones `set-segments` accepts. */\nconst DEFAULT_SEGMENTS = 12\n\n/**\n * 재질 — a part's material.\n *\n * An earlier editor showed the colour role as a raw token (`palette.dark`) in a select and nothing else. The material\n * holds the colour role, a preset, transparency and a glow; a round body also has how finely its rim is drawn. This\n * shows those: painted colours and lit colours as two groups of swatches, each named for what it\n * is for.\n *\n * Not here, because a material has no place for them: flat shading and casting\n * shadows. The glow's colour comes from the lit colours.\n *\n * Every change leaves as one `part-edit`; the editor runs it through the session, one step of the undo history.\n */\n@customElement('figure-material-panel')\nexport class FigureMaterialPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n div[field] {\n display: grid;\n gap: 4px;\n }\n div[field] > label {\n font-weight: 600;\n }\n div[swatches] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n button[swatch] {\n width: 22px;\n height: 22px;\n padding: 0;\n border-radius: 50%;\n border: 1px solid var(--md-sys-color-outline-variant);\n background: var(--swatch);\n cursor: pointer;\n }\n button[swatch][on] {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n select,\n input[type='range'] {\n font: inherit;\n min-width: 0;\n }\n /* The segment picker: the four common counts as their silhouettes with their triangle cost, the rest folded. */\n div[segments] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 4px;\n }\n button[seg] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n padding: 6px 2px 4px;\n font: inherit;\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 button[seg]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[seg][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n /* Small, and 12 and 16 both read as circles: large enough for the corners to show. */\n button[seg] svg {\n display: block;\n width: 32px;\n height: 32px;\n }\n button[seg] svg polygon {\n fill: var(--md-sys-color-surface-container-highest);\n stroke: currentColor;\n stroke-width: 1.1;\n stroke-linejoin: miter;\n }\n button[seg][on] svg polygon {\n fill: var(--md-sys-color-surface-container-lowest);\n }\n button[seg] b {\n font-size: 0.8rem;\n }\n button[seg] small {\n margin: 0;\n font-size: 0.6rem;\n }\n details[free] summary {\n margin-top: 4px;\n cursor: pointer;\n font-size: 0.68rem;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[presets] {\n display: flex;\n gap: 4px;\n margin-top: 4px;\n }\n div[presets] button {\n flex: 1;\n padding: 3px 0;\n font: inherit;\n font-size: 0.72rem;\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 div[presets] button[on] {\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 /** The selected body as evaluated — its material, its segments and its shape. */\n @property({ attribute: false }) body?: FigureAssetGeometry & { id: string }\n /** The asset's palette, token → colour. */\n @property({ attribute: false }) palette: Record<string, string> = {}\n @property({ type: Boolean }) locked = false\n /** The glow while its slider is dragged: shown beside it and drawn on the canvas, recorded on release. */\n @state() private glowing?: number\n\n render() {\n const part = this.body\n if (!part) return nothing\n const material = part.material\n /*\n The roles offered are the app's palette, with any the asset carries beyond it. A new asset carries\n only its primary colour; a role it lacks is sent with its colour and added in the same edit (`put`).\n */\n const entries = Object.entries(this.offered())\n const painted = entries.filter(([token]) => !token.startsWith('palette.status-'))\n const lit = entries.filter(([token]) => token.startsWith('palette.status-'))\n const glow = material.emissive\n return html`<section>\n <h3>${i18next.t('figure.label.part-material')}</h3>\n <div field>\n <label>${i18next.t('figure.label.palette-role')}</label>\n <div swatches>${this.swatches(painted, material.token, token => this.put({ token }))}</div>\n ${lit.length\n ? html`<small>${i18next.t('figure.label.status-colors')}</small>\n <div swatches>${this.swatches(lit, material.token, token => this.put({ token }))}</div>`\n : nothing}\n <small><b>${purposeOf(material.token)}</b> — ${i18next.t('figure.text.palette-purpose-explained')}</small>\n </div>\n <div field>\n <label>${i18next.t('figure.label.preset')}</label>\n <select\n ?disabled=${this.locked}\n @change=${(e: Event) => this.put({ preset: ((e.target as HTMLSelectElement).value || undefined) as FigureMaterial['preset'] })}\n >\n <option value=\"\" ?selected=${!material.preset}>${i18next.t('figure.label.none')}</option>\n ${MATERIAL_PRESETS.map(\n preset => html`<option value=${preset} ?selected=${preset === material.preset}>${i18next.t('figure.label.preset-' + preset)}</option>`\n )}\n </select>\n </div>\n <label toggle>\n <input\n type=\"checkbox\"\n ?disabled=${this.locked}\n .checked=${!!material.transparent}\n @change=${(e: Event) => this.put({ transparent: (e.target as HTMLInputElement).checked || undefined })}\n />\n ${i18next.t('figure.label.transparent')}\n </label>\n <div field>\n <label>${i18next.t('figure.label.emissive')}</label>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n ?disabled=${this.locked}\n .value=${String(glow?.intensity ?? 0)}\n @input=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value), true)}\n @change=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value))}\n />\n <small>${(this.glowing ?? glow?.intensity ?? 0).toFixed(2)}</small>\n </div>\n ${glow\n ? html`<div field>\n <label>${i18next.t('figure.label.emissive-color')}</label>\n <div swatches>${this.swatches(lit.length ? lit : painted, glow.token, token => this.put({ emissive: { ...glow, token } }))}</div>\n </div>`\n : nothing}\n ${this.renderSegments(part)}\n </section>`\n }\n\n private renderSegments(part: FigureAssetGeometry & { id: string }) {\n if (!isRoundBody((part as { shape?: never }).shape)) return nothing\n const current = part.segments ?? DEFAULT_SEGMENTS\n return html`\n <div field>\n <label>${i18next.t('figure.label.segments')}</label>\n <div segments>\n ${SEGMENT_PRESETS.slice(0, 4).map(\n preset => html`<button\n seg\n ?on=${preset === current}\n ?disabled=${this.locked}\n @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}\n >\n ${silhouette(preset)}\n <b>${preset}</b>\n <small>${i18next.t('figure.text.n-triangles', { count: figureTrianglesOf({ ...part, segments: preset }) })}</small>\n </button>`\n )}\n </div>\n <details free ?open=${SEGMENT_PRESETS.slice(4).includes(current as never)}>\n <summary>${i18next.t('figure.text.more-segment-options')}</summary>\n <div presets>\n ${SEGMENT_PRESETS.slice(4).map(\n preset => html`<button ?on=${preset === current} ?disabled=${this.locked} @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}>\n ${preset}\n </button>`\n )}\n </div>\n </details>\n </div>\n `\n }\n\n private swatches(entries: [string, string][], chosen: string | undefined, pick: (token: string) => void) {\n return entries.map(\n ([token, colour]) => html`<button\n swatch\n ?on=${token === chosen}\n ?disabled=${this.locked}\n title=${purposeOf(token)}\n aria-label=${purposeOf(token)}\n style=\"--swatch:${colour}\"\n @click=${() => pick(token)}\n ></button>`\n )\n }\n\n private offered(): Record<string, string> {\n return { ...(activePalette() as Record<string, string>), ...this.palette }\n }\n\n /** Intensity 0 is no glow at all; a new glow takes the part's own colour until another is picked. */\n private glow(intensity: number, live = false) {\n const material = this.body!.material\n this.glowing = live ? intensity : undefined\n this.put({ emissive: intensity > 0 ? { token: material.emissive?.token ?? material.token, intensity, on: true } : undefined }, live)\n }\n\n /** A material change: recorded, or -- while a slider is dragged -- only drawn (`part-live`). */\n private put(patch: Partial<FigureMaterial>, live = false) {\n const material: FigureMaterial = { ...this.body!.material, ...patch }\n for (const key of Object.keys(material) as (keyof FigureMaterial)[]) if (material[key] === undefined) delete material[key]\n /* The colours this material names that the asset does not carry yet. */\n const offered = this.offered()\n const palette = Object.fromEntries(\n [material.token, material.emissive?.token]\n .filter((token): token is string => !!token && this.palette[token] === undefined && offered[token] !== undefined)\n .map(token => [token, offered[token]!])\n )\n const edit: FigurePartEdit = { kind: 'set-material', target: this.body!.id, material, ...(Object.keys(palette).length ? { palette } : {}) }\n if (live) this.dispatchEvent(new CustomEvent('part-live', { detail: { edit }, bubbles: true, composed: true }))\n else this.edit(edit)\n }\n\n private edit(edit: FigurePartEdit) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }))\n }\n}\n\n/** A regular polygon of this many sides -- how round a body with this rim count looks from its end. */\nfunction silhouette(segments: number) {\n const points: string[] = []\n for (let i = 0; i < segments; i++) {\n const angle = -Math.PI / 2 + (i * 2 * Math.PI) / segments\n points.push(`${(16 + 13 * Math.cos(angle)).toFixed(2)},${(16 + 13 * Math.sin(angle)).toFixed(2)}`)\n }\n return svg`<svg viewBox=\"0 0 32 32\" aria-hidden=\"true\"><polygon points=${points.join(' ')}></polygon></svg>`\n}\n"]}
1
+ {"version":3,"file":"figure-material-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-material-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, svg } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { MATERIAL_PRESETS, SEGMENT_PRESETS, figureTrianglesOf } from '@hatiolab/figure-model'\nimport { activePalette } from '@hatiolab/things-scene'\nimport type { FigureAssetGeometry, FigureMaterial, FigurePartEdit } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { isRoundBody } from './starting-shape.js'\n\n/** The purpose each palette role is for, by its token. */\nconst PURPOSE: Record<string, string> = {\n 'palette.primary': 'primary',\n 'palette.accent': 'accent',\n 'palette.neutral': 'neutral',\n 'palette.dark': 'dark',\n 'palette.light': 'light',\n 'palette.surface': 'surface',\n 'palette.status-danger': 'danger',\n 'palette.status-warning': 'warning',\n 'palette.status-info': 'info',\n 'palette.status-success': 'success'\n}\nexport const purposeOf = (token?: string) => i18next.t(`figure.label.palette-purpose-${PURPOSE[token ?? ''] ?? 'registered'}`)\n\n/** Bodies with a rim — the ones `set-segments` accepts. */\nconst DEFAULT_SEGMENTS = 12\n\n/**\n * 재질 — a part's material.\n *\n * An earlier editor showed the colour role as a raw token (`palette.dark`) in a select and nothing else. The material\n * holds the colour role, a preset, transparency and a glow; a round body also has how finely its rim is drawn. This\n * shows those: painted colours and lit colours as two groups of swatches, each named for what it\n * is for.\n *\n * Not here, because a material has no place for them: flat shading and casting\n * shadows. The glow's colour comes from the lit colours.\n *\n * Every change leaves as one `part-edit`; the editor runs it through the session, one step of the undo history.\n */\n@customElement('figure-material-panel')\nexport class FigureMaterialPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n div[field] {\n display: grid;\n gap: 4px;\n }\n /* A slider and its value on one line, the value at the end where the eye finishes. */\n div[slider] {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 3em;\n align-items: center;\n gap: 8px;\n }\n div[slider] output {\n text-align: right;\n font-variant-numeric: tabular-nums;\n }\n div[swatches] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n }\n button[swatch] {\n width: 22px;\n height: 22px;\n padding: 0;\n border-radius: 50%;\n border: 1px solid var(--md-sys-color-outline-variant);\n background: var(--swatch);\n cursor: pointer;\n }\n button[swatch][on] {\n outline: 2px solid var(--md-sys-color-primary);\n outline-offset: 1px;\n }\n select,\n input[type='range'] {\n font: inherit;\n min-width: 0;\n }\n /* The segment picker: the four common counts as their silhouettes with their triangle cost, the rest folded. */\n div[segments] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 4px;\n }\n button[seg] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1px;\n padding: 6px 2px 4px;\n font: inherit;\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 button[seg]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n button[seg][on] {\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n border-color: transparent;\n }\n /* Small, and 12 and 16 both read as circles: large enough for the corners to show. */\n button[seg] svg {\n display: block;\n width: 32px;\n height: 32px;\n }\n button[seg] svg polygon {\n fill: var(--md-sys-color-surface-container-highest);\n stroke: currentColor;\n stroke-width: 1.1;\n stroke-linejoin: miter;\n }\n button[seg][on] svg polygon {\n fill: var(--md-sys-color-surface-container-lowest);\n }\n button[seg] b {\n font-weight: 600;\n }\n button[seg] small {\n margin: 0;\n }\n details[free] summary {\n margin-top: 4px;\n cursor: pointer;\n color: var(--md-sys-color-on-surface-variant);\n }\n div[presets] {\n display: flex;\n gap: 4px;\n margin-top: 4px;\n }\n div[presets] button {\n flex: 1;\n padding: 3px 0;\n font: inherit;\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 div[presets] button[on] {\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 /** The selected body as evaluated — its material, its segments and its shape. */\n @property({ attribute: false }) body?: FigureAssetGeometry & { id: string }\n /** The asset's palette, token → colour. */\n @property({ attribute: false }) palette: Record<string, string> = {}\n @property({ type: Boolean }) locked = false\n /** The glow while its slider is dragged: shown beside it and drawn on the canvas, recorded on release. */\n @state() private glowing?: number\n\n render() {\n const part = this.body\n if (!part) return nothing\n const material = part.material\n /*\n The roles offered are the app's palette, with any the asset carries beyond it. A new asset carries\n only its primary colour; a role it lacks is sent with its colour and added in the same edit (`put`).\n */\n const entries = Object.entries(this.offered())\n const painted = entries.filter(([token]) => !token.startsWith('palette.status-'))\n const lit = entries.filter(([token]) => token.startsWith('palette.status-'))\n const glow = material.emissive\n return html`<section>\n <h3>${i18next.t('figure.label.part-material')}</h3>\n <div field>\n <label>${i18next.t('figure.label.palette-role')}</label>\n <div swatches>${this.swatches(painted, material.token, token => this.put({ token }))}</div>\n ${lit.length\n ? html`<small>${i18next.t('figure.label.status-colors')}</small>\n <div swatches>${this.swatches(lit, material.token, token => this.put({ token }))}</div>`\n : nothing}\n <small><b>${purposeOf(material.token)}</b> — ${i18next.t('figure.text.palette-purpose-explained')}</small>\n </div>\n <div form>\n <span>${i18next.t('figure.label.preset')}</span>\n <select\n ?disabled=${this.locked}\n @change=${(e: Event) => this.put({ preset: ((e.target as HTMLSelectElement).value || undefined) as FigureMaterial['preset'] })}\n >\n <option value=\"\" ?selected=${!material.preset}>${i18next.t('figure.label.none')}</option>\n ${MATERIAL_PRESETS.map(\n preset => html`<option value=${preset} ?selected=${preset === material.preset}>${i18next.t('figure.label.preset-' + preset)}</option>`\n )}\n </select>\n </div>\n <label toggle>\n <input\n type=\"checkbox\"\n ?disabled=${this.locked}\n .checked=${!!material.transparent}\n @change=${(e: Event) => this.put({ transparent: (e.target as HTMLInputElement).checked || undefined })}\n />\n ${i18next.t('figure.label.transparent')}\n </label>\n <div form>\n <span>${i18next.t('figure.label.emissive')}</span>\n <div slider>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n ?disabled=${this.locked}\n .value=${String(glow?.intensity ?? 0)}\n @input=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value), true)}\n @change=${(e: Event) => this.glow(Number((e.target as HTMLInputElement).value))}\n />\n <output>${(this.glowing ?? glow?.intensity ?? 0).toFixed(2)}</output>\n </div>\n </div>\n ${glow\n ? html`<div field>\n <label>${i18next.t('figure.label.emissive-color')}</label>\n <div swatches>${this.swatches(lit.length ? lit : painted, glow.token, token => this.put({ emissive: { ...glow, token } }))}</div>\n </div>`\n : nothing}\n ${this.renderSegments(part)}\n </section>`\n }\n\n private renderSegments(part: FigureAssetGeometry & { id: string }) {\n if (!isRoundBody((part as { shape?: never }).shape)) return nothing\n const current = part.segments ?? DEFAULT_SEGMENTS\n return html`\n <div field>\n <label>${i18next.t('figure.label.segments')}</label>\n <div segments>\n ${SEGMENT_PRESETS.slice(0, 4).map(\n preset => html`<button\n seg\n ?on=${preset === current}\n ?disabled=${this.locked}\n @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}\n >\n ${silhouette(preset)}\n <b>${preset}</b>\n <small>${i18next.t('figure.text.n-triangles', { count: figureTrianglesOf({ ...part, segments: preset }) })}</small>\n </button>`\n )}\n </div>\n <details free ?open=${SEGMENT_PRESETS.slice(4).includes(current as never)}>\n <summary>${i18next.t('figure.text.more-segment-options')}</summary>\n <div presets>\n ${SEGMENT_PRESETS.slice(4).map(\n preset => html`<button ?on=${preset === current} ?disabled=${this.locked} @click=${() => this.edit({ kind: 'set-segments', target: part.id, segments: preset })}>\n ${preset}\n </button>`\n )}\n </div>\n </details>\n </div>\n `\n }\n\n private swatches(entries: [string, string][], chosen: string | undefined, pick: (token: string) => void) {\n return entries.map(\n ([token, colour]) => html`<button\n swatch\n ?on=${token === chosen}\n ?disabled=${this.locked}\n title=${purposeOf(token)}\n aria-label=${purposeOf(token)}\n style=\"--swatch:${colour}\"\n @click=${() => pick(token)}\n ></button>`\n )\n }\n\n private offered(): Record<string, string> {\n return { ...(activePalette() as Record<string, string>), ...this.palette }\n }\n\n /** Intensity 0 is no glow at all; a new glow takes the part's own colour until another is picked. */\n private glow(intensity: number, live = false) {\n const material = this.body!.material\n this.glowing = live ? intensity : undefined\n this.put({ emissive: intensity > 0 ? { token: material.emissive?.token ?? material.token, intensity, on: true } : undefined }, live)\n }\n\n /** A material change: recorded, or -- while a slider is dragged -- only drawn (`part-live`). */\n private put(patch: Partial<FigureMaterial>, live = false) {\n const material: FigureMaterial = { ...this.body!.material, ...patch }\n for (const key of Object.keys(material) as (keyof FigureMaterial)[]) if (material[key] === undefined) delete material[key]\n /* The colours this material names that the asset does not carry yet. */\n const offered = this.offered()\n const palette = Object.fromEntries(\n [material.token, material.emissive?.token]\n .filter((token): token is string => !!token && this.palette[token] === undefined && offered[token] !== undefined)\n .map(token => [token, offered[token]!])\n )\n const edit: FigurePartEdit = { kind: 'set-material', target: this.body!.id, material, ...(Object.keys(palette).length ? { palette } : {}) }\n if (live) this.dispatchEvent(new CustomEvent('part-live', { detail: { edit }, bubbles: true, composed: true }))\n else this.edit(edit)\n }\n\n private edit(edit: FigurePartEdit) {\n this.dispatchEvent(new CustomEvent('part-edit', { detail: { edit }, bubbles: true, composed: true }))\n }\n}\n\n/** A regular polygon of this many sides -- how round a body with this rim count looks from its end. */\nfunction silhouette(segments: number) {\n const points: string[] = []\n for (let i = 0; i < segments; i++) {\n const angle = -Math.PI / 2 + (i * 2 * Math.PI) / segments\n points.push(`${(16 + 13 * Math.cos(angle)).toFixed(2)},${(16 + 13 * Math.sin(angle)).toFixed(2)}`)\n }\n return svg`<svg viewBox=\"0 0 32 32\" aria-hidden=\"true\"><polygon points=${points.join(' ')}></polygon></svg>`\n}\n"]}
@@ -156,10 +156,10 @@ let FigureProperties = class FigureProperties extends localize(i18next)(LitEleme
156
156
  </div>
157
157
  <div row>
158
158
  <button act ?disabled=${!facts.declared || !part} @click=${() => this.command({ kind: 'set-mounting-face', part: part, face })}>
159
- ${i18next.t('figure.button.set-mounting-face')}
159
+ <md-icon>check</md-icon>${i18next.t('figure.button.set-mounting-face')}
160
160
  </button>
161
161
  <button act ?disabled=${!mounting} @click=${() => this.command({ kind: 'clear-mounting-face' })}>
162
- ${i18next.t('figure.button.clear-mounting-face')}
162
+ <md-icon>close</md-icon>${i18next.t('figure.button.clear-mounting-face')}
163
163
  </button>
164
164
  </div>
165
165
  <small>
@@ -1 +1 @@
1
- {"version":3,"file":"figure-properties.js","sourceRoot":"","sources":["../../client/modeller/figure-properties.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { FIGURE_FACES } from '@hatiolab/figure-model'\nimport type { FigureAuthoringAction, FigureFace } from '@hatiolab/figure-model'\n\nimport { occupancyFromParts } from './occupancy.js'\nimport { tagsOf, figureFigureFacts, type Placement, type FigureFigureFacts } from './figure-facts.js'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { ScrollbarStyles } from '@operato/styles'\n\nconst PLACEMENTS: Placement[] = ['floor', 'ceiling', 'center', 'wall']\nconst FACES = Object.keys(FIGURE_FACES) as FigureFace[]\n\n/**\n * 속성 — the modeller's figure properties.\n *\n * The tab opens with what the figure is: its name, the face it is placed by, the room it takes up, where the\n * catalogue files it.\n *\n * Two kinds of change leave this tab, and it does neither itself:\n * - `command` carries an authoring command (rename, declare the room, the mounting face). The page runs it\n * through the session, so it is one step of the same undo history as every other edit, and is saved with it.\n * - `catalog` carries a change to the row (category, tags). Those are not in the asset; the page writes them\n * to the saved row at once, so they need a saved figure.\n *\n * Not here yet, because has no place for them: editing the declared room by hand (declares it from the\n * parts, as graph expressions), and the detail level. Both wait on a ruling.\n */\n@customElement('figure-properties')\nexport class FigureProperties extends localize(i18next)(LitElement) {\n static styles = [ScrollbarStyles, SidePanelStyles, css`\n :host {\n display: block;\n /* It stands inside the properties tab's panel, which gives the margin; a second one indented it past the type. */\n padding: 0;\n overflow: auto;\n }\n div[row] {\n display: flex;\n gap: 6px;\n align-items: center;\n }\n div[row] > select {\n flex: 1;\n }\n div[row] > button[act] {\n flex: 1;\n width: auto;\n margin-top: 0;\n }\n div[row] > span {\n flex: 1;\n }\n `]\n\n /** The source being edited. */\n @property({ type: String }) source?: string\n /** The saved row, if any. Category and tags live on it. */\n @property({ type: String }) figureId?: string\n @property({ type: String }) figureCategory?: string\n @property({ type: Array }) figureTags?: string[]\n /** The part and face picked for the mounting face, before 「이 면으로」 is pressed. */\n @state() private pickedPart?: string\n @state() private pickedFace?: FigureFace\n\n private facts(): FigureFigureFacts | undefined {\n try {\n return this.source ? figureFigureFacts(this.source) : undefined\n } catch {\n /* A source the format refuses has no facts to show; the verdict tab says why. */\n return undefined\n }\n }\n\n render() {\n const facts = this.facts()\n if (!facts) return nothing\n /* The figure's type is the page's (it lives on the row); it is slotted into the first section. */\n return html`<section>\n <h3>${i18next.t('figure.label.group-figure')}</h3>\n <slot name=\"type\"></slot>\n ${this.renderName(facts)} ${this.renderPlacement(facts)}\n </section>\n <section>\n <h3>${i18next.t('figure.label.group-standing')}</h3>\n ${this.renderSpace(facts)} ${this.renderMounting(facts)}\n </section>\n <section>\n <h3>${i18next.t('figure.label.group-catalog')}</h3>\n ${this.renderCatalog()}\n </section>`\n }\n\n private renderName(facts: FigureFigureFacts) {\n return html`\n <div field>\n <label>${i18next.t('figure.label.name')}</label>\n <input\n .value=${facts.name}\n placeholder=${i18next.t('figure.text.figure-display-name')}\n @change=${(event: Event) => {\n const input = event.target as HTMLInputElement\n if (!input.value.trim()) {\n /* The format refuses a blank name; put the old one back rather than send a command that fails. */\n input.value = facts.name\n return\n }\n if (input.value.trim() !== facts.name) this.command({ kind: 'rename', name: input.value })\n }}\n />\n </div>\n `\n }\n\n /**\n * The face the figure is placed by. In it is part of the room declaration, so choosing one declares the room\n * again from the parts — the only way declares it — with the new face. The hint says so.\n */\n private renderPlacement(facts: FigureFigureFacts) {\n const current = facts.placement ?? 'floor'\n return html`\n <div field>\n <label>${i18next.t('figure.label.figure-placement')}</label>\n <select\n .value=${live(current)}\n @change=${(event: Event) => {\n const select = event.target as HTMLSelectElement\n /* The figure's own setting (ADR-0097): chosen with no part too, and the room is left as it is declared. */\n this.command({ kind: 'set-placement', placement: select.value as Placement })\n /* Refused, the figure is placed as it was, and the list says so; taken, the figure drawn again says the new one. */\n select.value = current\n }}\n >\n ${PLACEMENTS.map(\n one => html`<option value=${one} ?selected=${one === current}>${i18next.t('figure.label.placement-' + one)}</option>`\n )}\n </select>\n <small>${i18next.t('figure.text.placement-is-intent')}</small>\n </div>\n `\n }\n\n private renderSpace(facts: FigureFigureFacts) {\n return html`\n <div field>\n <label>${i18next.t('figure.label.declared-space')}</label>\n <div row>\n <span>${i18next.t(facts.declared ? 'figure.text.declared-space-declared' : 'figure.text.declared-space-none')}</span>\n <button act ?disabled=${!facts.parts.length} @click=${() => this.declare(facts.placement ?? 'floor')}>\n <md-icon>fit_screen</md-icon>${i18next.t('figure.button.fit-base-to-parts')}\n </button>\n </div>\n </div>\n `\n }\n\n private renderMounting(facts: FigureFigureFacts) {\n const mounting = facts.mounting\n /* Chosen, or what is set, or where the figure meets its plane -- not the first part listed (walk 1). */\n const part = [this.pickedPart, mounting?.part, facts.suggested?.part, facts.parts[0]].find(one => one && facts.parts.includes(one))\n const face: FigureFace = this.pickedFace ?? mounting?.face ?? facts.suggested?.face ?? 'bottom'\n return html`\n <div field>\n <label>${i18next.t('figure.label.mounting-face')}</label>\n <div row>\n <select ?disabled=${!facts.declared} @change=${(e: Event) => (this.pickedPart = (e.target as HTMLSelectElement).value)}>\n ${facts.parts.map(one => html`<option value=${one} ?selected=${one === part}>${one}</option>`)}\n </select>\n <select ?disabled=${!facts.declared} @change=${(e: Event) => (this.pickedFace = (e.target as HTMLSelectElement).value as FigureFace)}>\n ${FACES.map(one => html`<option value=${one} ?selected=${one === face}>${i18next.t('figure.label.face-' + one)}</option>`)}\n </select>\n </div>\n <div row>\n <button act ?disabled=${!facts.declared || !part} @click=${() => this.command({ kind: 'set-mounting-face', part: part!, face })}>\n ${i18next.t('figure.button.set-mounting-face')}\n </button>\n <button act ?disabled=${!mounting} @click=${() => this.command({ kind: 'clear-mounting-face' })}>\n ${i18next.t('figure.button.clear-mounting-face')}\n </button>\n </div>\n <small>\n ${facts.declared\n ? mounting\n ? `${mounting.part} · ${i18next.t('figure.label.face-' + mounting.face)}`\n : i18next.t('figure.label.mounting-face-none')\n : i18next.t('figure.text.mounting-face-needs-space')}\n </small>\n </div>\n `\n }\n\n private renderCatalog() {\n const saved = !!this.figureId\n return html`\n <div field>\n <label>${i18next.t('figure.label.category')}</label>\n <input\n .value=${this.figureCategory ?? ''}\n ?disabled=${!saved}\n placeholder=${i18next.t('figure.text.category-placeholder')}\n @change=${(e: Event) => this.catalog({ category: (e.target as HTMLInputElement).value.trim() })}\n />\n </div>\n <div field>\n <label>${i18next.t('figure.label.tags')}</label>\n <input\n .value=${(this.figureTags ?? []).join(', ')}\n ?disabled=${!saved}\n placeholder=${i18next.t('figure.text.tags-placeholder')}\n @change=${(e: Event) => this.catalog({ tags: tagsOf((e.target as HTMLInputElement).value) })}\n />\n ${saved ? nothing : html`<small>${i18next.t('figure.text.catalog-needs-a-save')}</small>`}\n </div>\n `\n }\n\n private declare(placement: Placement) {\n if (!this.source) return\n this.command(occupancyFromParts(this.source, placement))\n }\n\n private command(action: FigureAuthoringAction) {\n this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))\n }\n\n private catalog(patch: { category?: string; tags?: string[] }) {\n this.dispatchEvent(new CustomEvent('catalog', { detail: patch, bubbles: true, composed: true }))\n }\n}\n"]}
1
+ {"version":3,"file":"figure-properties.js","sourceRoot":"","sources":["../../client/modeller/figure-properties.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { css, html, LitElement, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { FIGURE_FACES } from '@hatiolab/figure-model'\nimport type { FigureAuthoringAction, FigureFace } from '@hatiolab/figure-model'\n\nimport { occupancyFromParts } from './occupancy.js'\nimport { tagsOf, figureFigureFacts, type Placement, type FigureFigureFacts } from './figure-facts.js'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { ScrollbarStyles } from '@operato/styles'\n\nconst PLACEMENTS: Placement[] = ['floor', 'ceiling', 'center', 'wall']\nconst FACES = Object.keys(FIGURE_FACES) as FigureFace[]\n\n/**\n * 속성 — the modeller's figure properties.\n *\n * The tab opens with what the figure is: its name, the face it is placed by, the room it takes up, where the\n * catalogue files it.\n *\n * Two kinds of change leave this tab, and it does neither itself:\n * - `command` carries an authoring command (rename, declare the room, the mounting face). The page runs it\n * through the session, so it is one step of the same undo history as every other edit, and is saved with it.\n * - `catalog` carries a change to the row (category, tags). Those are not in the asset; the page writes them\n * to the saved row at once, so they need a saved figure.\n *\n * Not here yet, because has no place for them: editing the declared room by hand (declares it from the\n * parts, as graph expressions), and the detail level. Both wait on a ruling.\n */\n@customElement('figure-properties')\nexport class FigureProperties extends localize(i18next)(LitElement) {\n static styles = [ScrollbarStyles, SidePanelStyles, css`\n :host {\n display: block;\n /* It stands inside the properties tab's panel, which gives the margin; a second one indented it past the type. */\n padding: 0;\n overflow: auto;\n }\n div[row] {\n display: flex;\n gap: 6px;\n align-items: center;\n }\n div[row] > select {\n flex: 1;\n }\n div[row] > button[act] {\n flex: 1;\n width: auto;\n margin-top: 0;\n }\n div[row] > span {\n flex: 1;\n }\n `]\n\n /** The source being edited. */\n @property({ type: String }) source?: string\n /** The saved row, if any. Category and tags live on it. */\n @property({ type: String }) figureId?: string\n @property({ type: String }) figureCategory?: string\n @property({ type: Array }) figureTags?: string[]\n /** The part and face picked for the mounting face, before 「이 면으로」 is pressed. */\n @state() private pickedPart?: string\n @state() private pickedFace?: FigureFace\n\n private facts(): FigureFigureFacts | undefined {\n try {\n return this.source ? figureFigureFacts(this.source) : undefined\n } catch {\n /* A source the format refuses has no facts to show; the verdict tab says why. */\n return undefined\n }\n }\n\n render() {\n const facts = this.facts()\n if (!facts) return nothing\n /* The figure's type is the page's (it lives on the row); it is slotted into the first section. */\n return html`<section>\n <h3>${i18next.t('figure.label.group-figure')}</h3>\n <slot name=\"type\"></slot>\n ${this.renderName(facts)} ${this.renderPlacement(facts)}\n </section>\n <section>\n <h3>${i18next.t('figure.label.group-standing')}</h3>\n ${this.renderSpace(facts)} ${this.renderMounting(facts)}\n </section>\n <section>\n <h3>${i18next.t('figure.label.group-catalog')}</h3>\n ${this.renderCatalog()}\n </section>`\n }\n\n private renderName(facts: FigureFigureFacts) {\n return html`\n <div field>\n <label>${i18next.t('figure.label.name')}</label>\n <input\n .value=${facts.name}\n placeholder=${i18next.t('figure.text.figure-display-name')}\n @change=${(event: Event) => {\n const input = event.target as HTMLInputElement\n if (!input.value.trim()) {\n /* The format refuses a blank name; put the old one back rather than send a command that fails. */\n input.value = facts.name\n return\n }\n if (input.value.trim() !== facts.name) this.command({ kind: 'rename', name: input.value })\n }}\n />\n </div>\n `\n }\n\n /**\n * The face the figure is placed by. In it is part of the room declaration, so choosing one declares the room\n * again from the parts — the only way declares it — with the new face. The hint says so.\n */\n private renderPlacement(facts: FigureFigureFacts) {\n const current = facts.placement ?? 'floor'\n return html`\n <div field>\n <label>${i18next.t('figure.label.figure-placement')}</label>\n <select\n .value=${live(current)}\n @change=${(event: Event) => {\n const select = event.target as HTMLSelectElement\n /* The figure's own setting (ADR-0097): chosen with no part too, and the room is left as it is declared. */\n this.command({ kind: 'set-placement', placement: select.value as Placement })\n /* Refused, the figure is placed as it was, and the list says so; taken, the figure drawn again says the new one. */\n select.value = current\n }}\n >\n ${PLACEMENTS.map(\n one => html`<option value=${one} ?selected=${one === current}>${i18next.t('figure.label.placement-' + one)}</option>`\n )}\n </select>\n <small>${i18next.t('figure.text.placement-is-intent')}</small>\n </div>\n `\n }\n\n private renderSpace(facts: FigureFigureFacts) {\n return html`\n <div field>\n <label>${i18next.t('figure.label.declared-space')}</label>\n <div row>\n <span>${i18next.t(facts.declared ? 'figure.text.declared-space-declared' : 'figure.text.declared-space-none')}</span>\n <button act ?disabled=${!facts.parts.length} @click=${() => this.declare(facts.placement ?? 'floor')}>\n <md-icon>fit_screen</md-icon>${i18next.t('figure.button.fit-base-to-parts')}\n </button>\n </div>\n </div>\n `\n }\n\n private renderMounting(facts: FigureFigureFacts) {\n const mounting = facts.mounting\n /* Chosen, or what is set, or where the figure meets its plane -- not the first part listed (walk 1). */\n const part = [this.pickedPart, mounting?.part, facts.suggested?.part, facts.parts[0]].find(one => one && facts.parts.includes(one))\n const face: FigureFace = this.pickedFace ?? mounting?.face ?? facts.suggested?.face ?? 'bottom'\n return html`\n <div field>\n <label>${i18next.t('figure.label.mounting-face')}</label>\n <div row>\n <select ?disabled=${!facts.declared} @change=${(e: Event) => (this.pickedPart = (e.target as HTMLSelectElement).value)}>\n ${facts.parts.map(one => html`<option value=${one} ?selected=${one === part}>${one}</option>`)}\n </select>\n <select ?disabled=${!facts.declared} @change=${(e: Event) => (this.pickedFace = (e.target as HTMLSelectElement).value as FigureFace)}>\n ${FACES.map(one => html`<option value=${one} ?selected=${one === face}>${i18next.t('figure.label.face-' + one)}</option>`)}\n </select>\n </div>\n <div row>\n <button act ?disabled=${!facts.declared || !part} @click=${() => this.command({ kind: 'set-mounting-face', part: part!, face })}>\n <md-icon>check</md-icon>${i18next.t('figure.button.set-mounting-face')}\n </button>\n <button act ?disabled=${!mounting} @click=${() => this.command({ kind: 'clear-mounting-face' })}>\n <md-icon>close</md-icon>${i18next.t('figure.button.clear-mounting-face')}\n </button>\n </div>\n <small>\n ${facts.declared\n ? mounting\n ? `${mounting.part} · ${i18next.t('figure.label.face-' + mounting.face)}`\n : i18next.t('figure.label.mounting-face-none')\n : i18next.t('figure.text.mounting-face-needs-space')}\n </small>\n </div>\n `\n }\n\n private renderCatalog() {\n const saved = !!this.figureId\n return html`\n <div field>\n <label>${i18next.t('figure.label.category')}</label>\n <input\n .value=${this.figureCategory ?? ''}\n ?disabled=${!saved}\n placeholder=${i18next.t('figure.text.category-placeholder')}\n @change=${(e: Event) => this.catalog({ category: (e.target as HTMLInputElement).value.trim() })}\n />\n </div>\n <div field>\n <label>${i18next.t('figure.label.tags')}</label>\n <input\n .value=${(this.figureTags ?? []).join(', ')}\n ?disabled=${!saved}\n placeholder=${i18next.t('figure.text.tags-placeholder')}\n @change=${(e: Event) => this.catalog({ tags: tagsOf((e.target as HTMLInputElement).value) })}\n />\n ${saved ? nothing : html`<small>${i18next.t('figure.text.catalog-needs-a-save')}</small>`}\n </div>\n `\n }\n\n private declare(placement: Placement) {\n if (!this.source) return\n this.command(occupancyFromParts(this.source, placement))\n }\n\n private command(action: FigureAuthoringAction) {\n this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))\n }\n\n private catalog(patch: { category?: string; tags?: string[] }) {\n this.dispatchEvent(new CustomEvent('catalog', { detail: patch, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import '@material/web/icon/icon.js';
1
2
  import { LitElement, nothing, type PropertyValues } from 'lit';
2
3
  import type { FigureEvaluatedShape } from '@hatiolab/figure-model';
3
4
  import './figure-cavity-drawing.js';
@@ -1,4 +1,5 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
2
3
  import { css, html, LitElement, nothing } from 'lit';
3
4
  import { customElement, property, state } from 'lit/decorators.js';
4
5
  import { live } from 'lit/directives/live.js';
@@ -82,7 +83,7 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
82
83
  ${body.shape.dimensions.jamb === undefined
83
84
  ? html `<div field>
84
85
  <button act ?disabled=${this.locked} @click=${() => this.dispatchEvent(new CustomEvent('section-editor-open', { bubbles: true, composed: true }))}>
85
- ${i18next.t('figure.button.section-editor-open')}
86
+ <md-icon>edit</md-icon>${i18next.t('figure.button.section-editor-open')}
86
87
  </button>
87
88
  ${e.section.kind === 'polygon'
88
89
  ? html `<small>${i18next.t(e.apex ? 'figure.text.section-points-apex' : 'figure.text.section-points', { points: String(e.section.points.length) })}</small>`
@@ -90,11 +91,7 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
90
91
  </div>`
91
92
  : nothing}
92
93
  ${kind === 'rectangle' ? this.renderCorner(body) : nothing}
93
- ${kind === 'rectangle' || kind === 'ellipse'
94
- ? this.renderCavity(body, e.axis)
95
- : html `<div field>
96
- <small>${i18next.t('figure.text.section-cavity-unavailable')}</small>
97
- </div>`}
94
+ ${ /* No inside for a drawn section: the control is not there, rather than a line saying so. */kind === 'rectangle' || kind === 'ellipse' ? this.renderCavity(body, e.axis) : nothing}
98
95
  </section>`;
99
96
  }
100
97
  renderCorner(body) {
@@ -104,8 +101,9 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
104
101
  .map(k => d[k]);
105
102
  const now = reaches.length ? sameOf(reaches) : 0;
106
103
  const max = cornerLimitOf(body.shape);
107
- return html `<div field>
104
+ return html `<div form>
108
105
  <label for="corner">${i18next.t('figure.label.section-corner')}</label>
106
+ <div>
109
107
  ${this.number('corner', now, {
110
108
  min: 0,
111
109
  ask: value => {
@@ -116,6 +114,7 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
116
114
  placeholder: now === undefined ? i18next.t('figure.text.section-corner-differs') : ''
117
115
  })}
118
116
  ${this.refusals.corner ? nothing : max !== undefined ? html `<small>${i18next.t('figure.text.section-at-most', { max: String(mm(max)) })}</small>` : nothing}
117
+ </div>
119
118
  </div>`;
120
119
  }
121
120
  renderCavity(body, axis) {
@@ -141,7 +140,7 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
141
140
  action: value => ({ kind: 'set-cavity', part: body.id, cavity: request(patch(value)) })
142
141
  });
143
142
  return html `<div field>
144
- <label>
143
+ <label toggle>
145
144
  <input
146
145
  type="checkbox"
147
146
  .checked=${live(!!cavity)}
@@ -165,8 +164,9 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
165
164
  </div>
166
165
  ${cavity ? html `<figure-cavity-drawing .body=${body} .perSide=${this.perSide} ?locked=${this.locked}></figure-cavity-drawing>` : nothing}
167
166
  ${cavity
168
- ? html `<div field>
167
+ ? html `<div form>
169
168
  <label>${i18next.t('figure.label.section-wall')}</label>
169
+ <div>
170
170
  ${this.perSide
171
171
  ? html `<div sides>
172
172
  ${[...sidesOf(axis)].sort((a, b) => a[0].localeCompare(b[0]) || (a[1] === '-' ? -1 : 1)).map(side => html `<label>
@@ -178,7 +178,7 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
178
178
  ${this.perSide || this.refusals.wall
179
179
  ? nothing
180
180
  : html `<small>${i18next.t('figure.text.section-wall-at-most', { max: String(mm(wallLimitOf(body.shape))) })}</small>`}
181
- <label>
181
+ <label toggle>
182
182
  <input
183
183
  type="checkbox"
184
184
  .checked=${live(this.perSide)}
@@ -187,13 +187,14 @@ let FigureSectionPanel = class FigureSectionPanel extends localize(i18next)(LitE
187
187
  />
188
188
  ${i18next.t('figure.label.section-wall-per-side')}
189
189
  </label>
190
+ </div>
190
191
  </div>
191
192
  ${['start', 'end'].map(end => this.renderEnd(body, axis, end, cavity[end], request, cavityField))}`
192
193
  : nothing}`;
193
194
  }
194
195
  renderEnd(body, axis, end, now, request, cavityField) {
195
196
  const closed = now !== 'open';
196
- return html `<div field>
197
+ return html `<div form>
197
198
  <label>${i18next.t('figure.label.face-' + END_FACE(axis, end))}</label>
198
199
  <div row>
199
200
  <select
@@ -1 +1 @@
1
- {"version":3,"file":"figure-section-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-section-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, nothing, type PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport type { FigureAuthoringAction, FigureCavityRequest, FigureEvaluatedShape } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { drawnOf } from './section-drawing.js'\nimport './figure-cavity-drawing.js'\nimport {\n END_FACE,\n SIDE_FACE,\n cavityDepthOf,\n cavityRefusalOf,\n cavityRequestOf,\n cavityResultOf,\n cornerLimitOf,\n cornerRefusalOf,\n defaultWallOf,\n mm,\n sameOf,\n sidesOf,\n sideWallLimitOf,\n wallLimitOf,\n type FigureCavityAsk\n} from './section-controls.js'\n\ntype Axis = 'x' | 'y' | 'z'\ntype End = 'start' | 'end'\ntype Body = { id: string; shape: FigureEvaluatedShape }\n\n/**\n * 단면 — a pushed part's rounded corners and its inside (ADR-0100 §3.3).\n *\n * Corners for a rectangle; an inside for a rectangle or an ellipse, with its walls (one thickness, or one per side when\n * asked for), its two ends open or closed, and what that makes of the part and how deep. A drawn section says beside\n * the inside why it has none. A turned body shows nothing here.\n *\n * A value being typed is drawn at once and recorded nowhere (`command-live`); a value the kernel would refuse is said\n * under its field with the most it can be, and is not sent. Enter or leaving the field sends the one `command`, one step\n * of the undo history; Esc puts the value back.\n */\n@customElement('figure-section-panel')\nexport class FigureSectionPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n div[row] {\n display: flex;\n gap: 6px;\n align-items: center;\n }\n div[row] > * {\n flex: 1;\n min-width: 0;\n }\n div[sides] {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px;\n /* A side's refusal under its field lengthens its cell only: the field beside it stays level. */\n align-items: start;\n }\n div[sides] > label {\n display: grid;\n gap: 2px;\n }\n small[refusal] {\n color: var(--md-sys-color-error);\n }\n input[aria-invalid='true'] {\n outline: 2px solid var(--md-sys-color-error);\n }\n `]\n\n @property({ attribute: false }) body?: Body\n @property({ type: Boolean }) locked = false\n /** What each field would not take, by field, said under it. */\n @state() private refusals: Record<string, string> = {}\n /** What is typed in a field and not yet sent, by field: the figure drawn with it must not type over it. */\n @state() private typed: Record<string, string> = {}\n /** Walls one per side, asked for or because they already differ. */\n @state() private perSide = false\n\n protected willUpdate(changed: PropertyValues<this>) {\n const before = changed.get('body') as Body | undefined\n if (changed.has('body') && before?.id !== this.body?.id) {\n this.refusals = {}\n this.typed = {}\n /* A part whose walls already differ opens with them one per side. */\n const walls = this.body?.shape.cavity?.wall\n this.perSide = !!walls && sameOf(Object.values(walls)) === undefined\n }\n }\n\n render() {\n const body = this.body\n /* A push, or a pyramid: a drawn section drawn to one point, which has no corners and no inside. */\n const e = body?.shape ? drawnOf(body.shape) : undefined\n if (!body || !e) return nothing\n const kind = e.section.kind\n return html`<section>\n <h3>${i18next.t('figure.label.group-section')}</h3>\n ${body.shape.dimensions.jamb === undefined\n ? html`<div field>\n <button act ?disabled=${this.locked} @click=${() => this.dispatchEvent(new CustomEvent('section-editor-open', { bubbles: true, composed: true }))}>\n ${i18next.t('figure.button.section-editor-open')}\n </button>\n ${e.section.kind === 'polygon'\n ? html`<small>${i18next.t(e.apex ? 'figure.text.section-points-apex' : 'figure.text.section-points', { points: String(e.section.points.length) })}</small>`\n : nothing}\n </div>`\n : nothing}\n ${kind === 'rectangle' ? this.renderCorner(body) : nothing}\n ${kind === 'rectangle' || kind === 'ellipse'\n ? this.renderCavity(body, e.axis)\n : html`<div field>\n <small>${i18next.t('figure.text.section-cavity-unavailable')}</small>\n </div>`}\n </section>`\n }\n\n private renderCorner(body: Body) {\n const d = body.shape.dimensions\n const reaches = Object.keys(d)\n .filter(k => k.startsWith('corner.'))\n .map(k => d[k])\n const now = reaches.length ? sameOf(reaches) : 0\n const max = cornerLimitOf(body.shape)\n return html`<div field>\n <label for=\"corner\">${i18next.t('figure.label.section-corner')}</label>\n ${this.number('corner', now, {\n min: 0,\n ask: value => {\n const r = cornerRefusalOf(body.shape, value)\n return r ? i18next.t('figure.text.section-corner-over', { max: String(mm(r.max)) }) : ''\n },\n action: value => ({ kind: 'set-section-corner', part: body.id, corner: value > 0 ? value : null }),\n placeholder: now === undefined ? i18next.t('figure.text.section-corner-differs') : ''\n })}\n ${this.refusals.corner ? nothing : max !== undefined ? html`<small>${i18next.t('figure.text.section-at-most', { max: String(mm(max)) })}</small>` : nothing}\n </div>`\n }\n\n private renderCavity(body: Body, axis: Axis) {\n const cavity = body.shape.cavity\n const request = (patch: Partial<FigureCavityRequest>) => cavityRequestOf(body.shape, patch)\n const said = (ask: FigureCavityAsk | null) =>\n !ask\n ? ''\n : ask.code === 'not-positive'\n ? i18next.t('figure.text.section-not-positive')\n : i18next.t(`figure.text.section-${ask.code === 'wall-too-thick' ? 'wall' : 'ends'}-over`, { max: String(ask.max) })\n const cavityField = (key: string, now: number | undefined, patch: (value: number) => Partial<FigureCavityRequest>) =>\n this.number(key, now, {\n min: 0,\n ask: value => {\n if (!(value > 0)) return i18next.t('figure.text.section-not-positive')\n const ask = cavityRefusalOf(body.shape, request(patch(value)))\n /* One side too thick: the most it can be is the width across less the wall opposite, not the even limit. */\n const side = key.startsWith('wall.') ? key.slice(5) : undefined\n if (ask?.code === 'wall-too-thick' && side && cavity)\n return said({ code: 'wall-too-thick', max: mm(sideWallLimitOf(body.shape, cavity.wall, side)) })\n return said(ask)\n },\n action: value => ({ kind: 'set-cavity', part: body.id, cavity: request(patch(value)) })\n })\n return html`<div field>\n <label>\n <input\n type=\"checkbox\"\n .checked=${live(!!cavity)}\n ?disabled=${this.locked}\n @change=${(ev: Event) => {\n const on = (ev.target as HTMLInputElement).checked\n ;(ev.target as HTMLInputElement).checked = !!cavity\n this.command({ kind: 'set-cavity', part: body.id, cavity: on ? request({}) : null })\n }}\n />\n ${i18next.t('figure.label.section-cavity')}\n </label>\n ${cavity\n ? html`<small result>\n ${i18next.t('figure.text.section-depth', {\n result: i18next.t(`figure.label.section-result-${cavityResultOf(cavity)}`),\n depth: String(cavityDepthOf(body.shape))\n })}\n </small>`\n : nothing}\n </div>\n ${cavity ? html`<figure-cavity-drawing .body=${body} .perSide=${this.perSide} ?locked=${this.locked}></figure-cavity-drawing>` : nothing}\n ${cavity\n ? html`<div field>\n <label>${i18next.t('figure.label.section-wall')}</label>\n ${this.perSide\n ? html`<div sides>\n ${[...sidesOf(axis)].sort((a, b) => a[0]!.localeCompare(b[0]!) || (a[1] === '-' ? -1 : 1)).map(\n side => html`<label>\n <span>${i18next.t('figure.label.face-' + SIDE_FACE[side])}</span>\n ${cavityField(`wall.${side}`, cavity.wall[side], value => ({ wall: { ...cavity.wall, [side]: value } }))}\n </label>`\n )}\n </div>`\n : cavityField('wall', sameOf(Object.values(cavity.wall)), value => ({ wall: value }))}\n ${this.perSide || this.refusals.wall\n ? nothing\n : html`<small>${i18next.t('figure.text.section-wall-at-most', { max: String(mm(wallLimitOf(body.shape))) })}</small>`}\n <label>\n <input\n type=\"checkbox\"\n .checked=${live(this.perSide)}\n ?disabled=${this.locked}\n @change=${(ev: Event) => (this.perSide = (ev.target as HTMLInputElement).checked)}\n />\n ${i18next.t('figure.label.section-wall-per-side')}\n </label>\n </div>\n ${(['start', 'end'] as const).map(end => this.renderEnd(body, axis, end, cavity[end], request, cavityField))}`\n : nothing}`\n }\n\n private renderEnd(\n body: Body,\n axis: Axis,\n end: End,\n now: 'open' | { closed: number },\n request: (patch: Partial<FigureCavityRequest>) => FigureCavityRequest,\n cavityField: (key: string, now: number | undefined, patch: (value: number) => Partial<FigureCavityRequest>) => unknown\n ) {\n const closed = now !== 'open'\n return html`<div field>\n <label>${i18next.t('figure.label.face-' + END_FACE(axis, end))}</label>\n <div row>\n <select\n .value=${live(closed ? 'closed' : 'open')}\n ?disabled=${this.locked}\n @change=${(ev: Event) => {\n const select = ev.target as HTMLSelectElement\n const want = select.value\n select.value = closed ? 'closed' : 'open'\n this.command({ kind: 'set-cavity', part: body.id, cavity: request({ [end]: want === 'open' ? 'open' : { closed: defaultWallOf(body.shape) } }) })\n }}\n >\n <option value=\"closed\">${i18next.t('figure.label.section-end-closed')}</option>\n <option value=\"open\">${i18next.t('figure.label.section-end-open')}</option>\n </select>\n ${closed ? cavityField(`closed.${end}`, now.closed, value => ({ [end]: { closed: value } })) : html`<span></span>`}\n </div>\n </div>`\n }\n\n /**\n * A millimetre field. Typing draws the value (or, if the kernel would refuse it, says why under the field and draws the\n * figure as it stands); Enter or leaving sends it once; Esc puts the value back.\n */\n private number(\n key: string,\n now: number | undefined,\n o: { min: number; ask: (value: number) => string; action: (value: number) => FigureAuthoringAction; placeholder?: string }\n ) {\n const shown = now === undefined ? '' : String(mm(now))\n const done = () => {\n const { [key]: _, ...rest } = this.typed\n this.typed = rest\n }\n const read = (input: HTMLInputElement) => (input.value === '' ? NaN : Number(input.value))\n const judge = (input: HTMLInputElement) => {\n const value = read(input)\n const refusal = !Number.isFinite(value) || value < o.min ? '' : o.ask(value)\n this.refusals = { ...this.refusals, [key]: refusal }\n return Number.isFinite(value) && value >= o.min && !refusal ? value : undefined\n }\n return html`<input\n id=${key}\n type=\"number\"\n min=${o.min}\n step=\"any\"\n inputmode=\"decimal\"\n .value=${live(this.typed[key] ?? shown)}\n placeholder=${o.placeholder ?? ''}\n aria-invalid=${this.refusals[key] ? 'true' : 'false'}\n aria-describedby=${`${key}-refusal`}\n ?disabled=${this.locked}\n @input=${(ev: Event) => {\n this.typed = { ...this.typed, [key]: (ev.target as HTMLInputElement).value }\n const value = judge(ev.target as HTMLInputElement)\n this.preview(value === undefined ? null : o.action(value))\n }}\n @change=${(ev: Event) => {\n const input = ev.target as HTMLInputElement\n if (input.value === shown) return done(), this.preview(null)\n const value = judge(input)\n /* A refused value stays typed with its reason under it, to be corrected; nothing is sent. */\n if (value === undefined) return this.preview(null)\n done()\n this.command(o.action(value))\n }}\n @keydown=${(ev: KeyboardEvent) => {\n if (ev.key !== 'Escape') return\n const input = ev.target as HTMLInputElement\n input.value = shown\n done()\n this.refusals = { ...this.refusals, [key]: '' }\n this.preview(null)\n }}\n />\n ${this.refusals[key] ? html`<small refusal id=${`${key}-refusal`} role=\"alert\">${this.refusals[key]}</small>` : nothing}`\n }\n\n private preview(action: FigureAuthoringAction | null) {\n this.dispatchEvent(new CustomEvent('command-live', { detail: { action }, bubbles: true, composed: true }))\n }\n\n private command(action: FigureAuthoringAction) {\n this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))\n }\n}\n"]}
1
+ {"version":3,"file":"figure-section-panel.js","sourceRoot":"","sources":["../../client/modeller/figure-section-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport { css, html, LitElement, nothing, type PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { live } from 'lit/directives/live.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport type { FigureAuthoringAction, FigureCavityRequest, FigureEvaluatedShape } from '@hatiolab/figure-model'\nimport { SidePanelStyles } from './side-panel-styles.js'\nimport { drawnOf } from './section-drawing.js'\nimport './figure-cavity-drawing.js'\nimport {\n END_FACE,\n SIDE_FACE,\n cavityDepthOf,\n cavityRefusalOf,\n cavityRequestOf,\n cavityResultOf,\n cornerLimitOf,\n cornerRefusalOf,\n defaultWallOf,\n mm,\n sameOf,\n sidesOf,\n sideWallLimitOf,\n wallLimitOf,\n type FigureCavityAsk\n} from './section-controls.js'\n\ntype Axis = 'x' | 'y' | 'z'\ntype End = 'start' | 'end'\ntype Body = { id: string; shape: FigureEvaluatedShape }\n\n/**\n * 단면 — a pushed part's rounded corners and its inside (ADR-0100 §3.3).\n *\n * Corners for a rectangle; an inside for a rectangle or an ellipse, with its walls (one thickness, or one per side when\n * asked for), its two ends open or closed, and what that makes of the part and how deep. A drawn section says beside\n * the inside why it has none. A turned body shows nothing here.\n *\n * A value being typed is drawn at once and recorded nowhere (`command-live`); a value the kernel would refuse is said\n * under its field with the most it can be, and is not sent. Enter or leaving the field sends the one `command`, one step\n * of the undo history; Esc puts the value back.\n */\n@customElement('figure-section-panel')\nexport class FigureSectionPanel extends localize(i18next)(LitElement) {\n static styles = [SidePanelStyles, css`\n :host {\n display: block;\n }\n div[row] {\n display: flex;\n gap: 6px;\n align-items: center;\n }\n div[row] > * {\n flex: 1;\n min-width: 0;\n }\n div[sides] {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px;\n /* A side's refusal under its field lengthens its cell only: the field beside it stays level. */\n align-items: start;\n }\n div[sides] > label {\n display: grid;\n gap: 2px;\n }\n small[refusal] {\n color: var(--md-sys-color-error);\n }\n input[aria-invalid='true'] {\n outline: 2px solid var(--md-sys-color-error);\n }\n `]\n\n @property({ attribute: false }) body?: Body\n @property({ type: Boolean }) locked = false\n /** What each field would not take, by field, said under it. */\n @state() private refusals: Record<string, string> = {}\n /** What is typed in a field and not yet sent, by field: the figure drawn with it must not type over it. */\n @state() private typed: Record<string, string> = {}\n /** Walls one per side, asked for or because they already differ. */\n @state() private perSide = false\n\n protected willUpdate(changed: PropertyValues<this>) {\n const before = changed.get('body') as Body | undefined\n if (changed.has('body') && before?.id !== this.body?.id) {\n this.refusals = {}\n this.typed = {}\n /* A part whose walls already differ opens with them one per side. */\n const walls = this.body?.shape.cavity?.wall\n this.perSide = !!walls && sameOf(Object.values(walls)) === undefined\n }\n }\n\n render() {\n const body = this.body\n /* A push, or a pyramid: a drawn section drawn to one point, which has no corners and no inside. */\n const e = body?.shape ? drawnOf(body.shape) : undefined\n if (!body || !e) return nothing\n const kind = e.section.kind\n return html`<section>\n <h3>${i18next.t('figure.label.group-section')}</h3>\n ${body.shape.dimensions.jamb === undefined\n ? html`<div field>\n <button act ?disabled=${this.locked} @click=${() => this.dispatchEvent(new CustomEvent('section-editor-open', { bubbles: true, composed: true }))}>\n <md-icon>edit</md-icon>${i18next.t('figure.button.section-editor-open')}\n </button>\n ${e.section.kind === 'polygon'\n ? html`<small>${i18next.t(e.apex ? 'figure.text.section-points-apex' : 'figure.text.section-points', { points: String(e.section.points.length) })}</small>`\n : nothing}\n </div>`\n : nothing}\n ${kind === 'rectangle' ? this.renderCorner(body) : nothing}\n ${/* No inside for a drawn section: the control is not there, rather than a line saying so. */ kind === 'rectangle' || kind === 'ellipse' ? this.renderCavity(body, e.axis) : nothing}\n </section>`\n }\n\n private renderCorner(body: Body) {\n const d = body.shape.dimensions\n const reaches = Object.keys(d)\n .filter(k => k.startsWith('corner.'))\n .map(k => d[k])\n const now = reaches.length ? sameOf(reaches) : 0\n const max = cornerLimitOf(body.shape)\n return html`<div form>\n <label for=\"corner\">${i18next.t('figure.label.section-corner')}</label>\n <div>\n ${this.number('corner', now, {\n min: 0,\n ask: value => {\n const r = cornerRefusalOf(body.shape, value)\n return r ? i18next.t('figure.text.section-corner-over', { max: String(mm(r.max)) }) : ''\n },\n action: value => ({ kind: 'set-section-corner', part: body.id, corner: value > 0 ? value : null }),\n placeholder: now === undefined ? i18next.t('figure.text.section-corner-differs') : ''\n })}\n ${this.refusals.corner ? nothing : max !== undefined ? html`<small>${i18next.t('figure.text.section-at-most', { max: String(mm(max)) })}</small>` : nothing}\n </div>\n </div>`\n }\n\n private renderCavity(body: Body, axis: Axis) {\n const cavity = body.shape.cavity\n const request = (patch: Partial<FigureCavityRequest>) => cavityRequestOf(body.shape, patch)\n const said = (ask: FigureCavityAsk | null) =>\n !ask\n ? ''\n : ask.code === 'not-positive'\n ? i18next.t('figure.text.section-not-positive')\n : i18next.t(`figure.text.section-${ask.code === 'wall-too-thick' ? 'wall' : 'ends'}-over`, { max: String(ask.max) })\n const cavityField = (key: string, now: number | undefined, patch: (value: number) => Partial<FigureCavityRequest>) =>\n this.number(key, now, {\n min: 0,\n ask: value => {\n if (!(value > 0)) return i18next.t('figure.text.section-not-positive')\n const ask = cavityRefusalOf(body.shape, request(patch(value)))\n /* One side too thick: the most it can be is the width across less the wall opposite, not the even limit. */\n const side = key.startsWith('wall.') ? key.slice(5) : undefined\n if (ask?.code === 'wall-too-thick' && side && cavity)\n return said({ code: 'wall-too-thick', max: mm(sideWallLimitOf(body.shape, cavity.wall, side)) })\n return said(ask)\n },\n action: value => ({ kind: 'set-cavity', part: body.id, cavity: request(patch(value)) })\n })\n return html`<div field>\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${live(!!cavity)}\n ?disabled=${this.locked}\n @change=${(ev: Event) => {\n const on = (ev.target as HTMLInputElement).checked\n ;(ev.target as HTMLInputElement).checked = !!cavity\n this.command({ kind: 'set-cavity', part: body.id, cavity: on ? request({}) : null })\n }}\n />\n ${i18next.t('figure.label.section-cavity')}\n </label>\n ${cavity\n ? html`<small result>\n ${i18next.t('figure.text.section-depth', {\n result: i18next.t(`figure.label.section-result-${cavityResultOf(cavity)}`),\n depth: String(cavityDepthOf(body.shape))\n })}\n </small>`\n : nothing}\n </div>\n ${cavity ? html`<figure-cavity-drawing .body=${body} .perSide=${this.perSide} ?locked=${this.locked}></figure-cavity-drawing>` : nothing}\n ${cavity\n ? html`<div form>\n <label>${i18next.t('figure.label.section-wall')}</label>\n <div>\n ${this.perSide\n ? html`<div sides>\n ${[...sidesOf(axis)].sort((a, b) => a[0]!.localeCompare(b[0]!) || (a[1] === '-' ? -1 : 1)).map(\n side => html`<label>\n <span>${i18next.t('figure.label.face-' + SIDE_FACE[side])}</span>\n ${cavityField(`wall.${side}`, cavity.wall[side], value => ({ wall: { ...cavity.wall, [side]: value } }))}\n </label>`\n )}\n </div>`\n : cavityField('wall', sameOf(Object.values(cavity.wall)), value => ({ wall: value }))}\n ${this.perSide || this.refusals.wall\n ? nothing\n : html`<small>${i18next.t('figure.text.section-wall-at-most', { max: String(mm(wallLimitOf(body.shape))) })}</small>`}\n <label toggle>\n <input\n type=\"checkbox\"\n .checked=${live(this.perSide)}\n ?disabled=${this.locked}\n @change=${(ev: Event) => (this.perSide = (ev.target as HTMLInputElement).checked)}\n />\n ${i18next.t('figure.label.section-wall-per-side')}\n </label>\n </div>\n </div>\n ${(['start', 'end'] as const).map(end => this.renderEnd(body, axis, end, cavity[end], request, cavityField))}`\n : nothing}`\n }\n\n private renderEnd(\n body: Body,\n axis: Axis,\n end: End,\n now: 'open' | { closed: number },\n request: (patch: Partial<FigureCavityRequest>) => FigureCavityRequest,\n cavityField: (key: string, now: number | undefined, patch: (value: number) => Partial<FigureCavityRequest>) => unknown\n ) {\n const closed = now !== 'open'\n return html`<div form>\n <label>${i18next.t('figure.label.face-' + END_FACE(axis, end))}</label>\n <div row>\n <select\n .value=${live(closed ? 'closed' : 'open')}\n ?disabled=${this.locked}\n @change=${(ev: Event) => {\n const select = ev.target as HTMLSelectElement\n const want = select.value\n select.value = closed ? 'closed' : 'open'\n this.command({ kind: 'set-cavity', part: body.id, cavity: request({ [end]: want === 'open' ? 'open' : { closed: defaultWallOf(body.shape) } }) })\n }}\n >\n <option value=\"closed\">${i18next.t('figure.label.section-end-closed')}</option>\n <option value=\"open\">${i18next.t('figure.label.section-end-open')}</option>\n </select>\n ${closed ? cavityField(`closed.${end}`, now.closed, value => ({ [end]: { closed: value } })) : html`<span></span>`}\n </div>\n </div>`\n }\n\n /**\n * A millimetre field. Typing draws the value (or, if the kernel would refuse it, says why under the field and draws the\n * figure as it stands); Enter or leaving sends it once; Esc puts the value back.\n */\n private number(\n key: string,\n now: number | undefined,\n o: { min: number; ask: (value: number) => string; action: (value: number) => FigureAuthoringAction; placeholder?: string }\n ) {\n const shown = now === undefined ? '' : String(mm(now))\n const done = () => {\n const { [key]: _, ...rest } = this.typed\n this.typed = rest\n }\n const read = (input: HTMLInputElement) => (input.value === '' ? NaN : Number(input.value))\n const judge = (input: HTMLInputElement) => {\n const value = read(input)\n const refusal = !Number.isFinite(value) || value < o.min ? '' : o.ask(value)\n this.refusals = { ...this.refusals, [key]: refusal }\n return Number.isFinite(value) && value >= o.min && !refusal ? value : undefined\n }\n return html`<input\n id=${key}\n type=\"number\"\n min=${o.min}\n step=\"any\"\n inputmode=\"decimal\"\n .value=${live(this.typed[key] ?? shown)}\n placeholder=${o.placeholder ?? ''}\n aria-invalid=${this.refusals[key] ? 'true' : 'false'}\n aria-describedby=${`${key}-refusal`}\n ?disabled=${this.locked}\n @input=${(ev: Event) => {\n this.typed = { ...this.typed, [key]: (ev.target as HTMLInputElement).value }\n const value = judge(ev.target as HTMLInputElement)\n this.preview(value === undefined ? null : o.action(value))\n }}\n @change=${(ev: Event) => {\n const input = ev.target as HTMLInputElement\n if (input.value === shown) return done(), this.preview(null)\n const value = judge(input)\n /* A refused value stays typed with its reason under it, to be corrected; nothing is sent. */\n if (value === undefined) return this.preview(null)\n done()\n this.command(o.action(value))\n }}\n @keydown=${(ev: KeyboardEvent) => {\n if (ev.key !== 'Escape') return\n const input = ev.target as HTMLInputElement\n input.value = shown\n done()\n this.refusals = { ...this.refusals, [key]: '' }\n this.preview(null)\n }}\n />\n ${this.refusals[key] ? html`<small refusal id=${`${key}-refusal`} role=\"alert\">${this.refusals[key]}</small>` : nothing}`\n }\n\n private preview(action: FigureAuthoringAction | null) {\n this.dispatchEvent(new CustomEvent('command-live', { detail: { action }, bubbles: true, composed: true }))\n }\n\n private command(action: FigureAuthoringAction) {\n this.dispatchEvent(new CustomEvent('command', { detail: { action }, bubbles: true, composed: true }))\n }\n}\n"]}
@@ -45,13 +45,23 @@ export declare function resizeAxesOf(asset: FigureAsset, part: string, pending?:
45
45
  basis: 'chosen' | 'suggested' | 'none';
46
46
  };
47
47
  /**
48
- * The selected part in three short lines, for the head of its panel (designer 2026-09-27, B): what it is, how its
49
- * size answers the figure's, and what holds it -- 「box · 상자 / 너비는 도형을 따라감 / 아랫면을 바닥에 유지 중」.
48
+ * Korean particles for a word the sentence is built around: the topic (은/는) and "and/with" (과/와), by whether the
49
+ * word's last syllable ends in a consonant. Other languages need none.
50
50
  */
51
- export declare function partSummaryOf(asset: FigureAsset, part: string, say: (key: string, params?: Record<string, string>) => string): {
51
+ export declare function particlesOf(word: string, lang: string): {
52
+ topic: string;
53
+ with: string;
54
+ };
55
+ /**
56
+ * The selected part at the head of its panel: what it is, and -- only when a measure changes by itself -- what it
57
+ * follows, as a sentence a person reads without knowing the model's words: the model's size, a share of it, another
58
+ * part's measure, a field, a calculation (`figureFollowOf`). A size of its own and a part fastened or not say nothing
59
+ * here; the fastening is drawn in its own section (user 2026-10-01: 「규칙을 따름: 높이 (Y)」 -- "who would know what that
60
+ * means?", and "why say anything when all is as usual?").
61
+ */
62
+ export declare function partSummaryOf(asset: FigureAsset, part: string, say: (key: string, params?: Record<string, string>) => string, lang?: string): {
52
63
  kind: string;
53
64
  size: string;
54
- relation: string;
55
65
  };
56
66
  /**
57
67
  * A part's own field in the words its row uses -- 「위치 Y」, 「크기 X」, and the turn as one 「회전」 -- so a field the plan
@@ -64,36 +64,64 @@ export function resizeAxesOf(asset, part, pending) {
64
64
  return { axes: editable, basis: editable.length ? 'suggested' : 'none' };
65
65
  }
66
66
  /**
67
- * The selected part in three short lines, for the head of its panel (designer 2026-09-27, B): what it is, how its
68
- * size answers the figure's, and what holds it -- 「box · 상자 / 너비는 도형을 따라감 / 아랫면을 바닥에 유지 중」.
67
+ * Korean particles for a word the sentence is built around: the topic (은/는) and "and/with" (과/와), by whether the
68
+ * word's last syllable ends in a consonant. Other languages need none.
69
69
  */
70
- export function partSummaryOf(asset, part, say) {
70
+ export function particlesOf(word, lang) {
71
+ if (!lang.startsWith('ko'))
72
+ return { topic: word, with: word };
73
+ const last = word.trim().slice(-1).charCodeAt(0);
74
+ const closed = last >= 0xac00 && last <= 0xd7a3 && (last - 0xac00) % 28 !== 0;
75
+ return { topic: word + (closed ? '은' : '는'), with: word + (closed ? '과' : '와') };
76
+ }
77
+ /**
78
+ * The selected part at the head of its panel: what it is, and -- only when a measure changes by itself -- what it
79
+ * follows, as a sentence a person reads without knowing the model's words: the model's size, a share of it, another
80
+ * part's measure, a field, a calculation (`figureFollowOf`). A size of its own and a part fastened or not say nothing
81
+ * here; the fastening is drawn in its own section (user 2026-10-01: 「규칙을 따름: 높이 (Y)」 -- "who would know what that
82
+ * means?", and "why say anything when all is as usual?").
83
+ */
84
+ export function partSummaryOf(asset, part, say, lang = 'ko') {
71
85
  const shape = modelOf(asset).nodes.find(n => n.id === `${part}.shape`);
72
86
  const named = startingShapeKindOf(shape);
73
87
  const kind = named ? say(`figure.label.primitive-${named}`) : '';
74
88
  const editable = followableAxes(asset, part);
75
89
  const round = editable.length === 2;
76
- const word = (axis) => say(round && axis === 'x' ? 'figure.label.resize-diameter' : `figure.label.resize-${axis}`);
77
- const following = editable.filter(axis => {
78
- const now = followOf(asset, part, axis);
79
- return now.of === 'instance' && now.axis === axis;
80
- });
81
- const ruled = editable.filter(axis => followOf(asset, part, axis).of !== 'own' && !following.includes(axis));
82
- const size = [
83
- following.length ? say('figure.text.summary-follows', { axes: following.map(word).join(' · ') }) : '',
84
- ruled.length ? say('figure.text.summary-ruled', { axes: ruled.map(word).join(' · ') }) : ''
85
- ].filter(Boolean).join(' / ') || say('figure.text.summary-size-fixed');
86
- const joint = asset.joints?.find(j => j.body1.part === part);
87
- let relation = say('figure.text.summary-free');
88
- if (joint) {
89
- const face = say(`figure.label.face-${joint.body1.face}`);
90
- const gap = typeof joint.origin?.gap === 'number' ? joint.origin.gap : 0;
91
- relation =
92
- 'plane' in joint.body0
93
- ? say('figure.text.held-on-plane', { face, place: say(`figure.label.resize-place-${asset.placement ?? 'floor'}`), gap: String(gap) })
94
- : say('figure.text.held-on-part', { face, other: joint.body0.part, otherFace: say(`figure.label.face-${joint.body0.face}`) });
90
+ const word = (axis) => say(round && axis === 'x' ? 'figure.label.measure-diameter' : `figure.label.measure-${axis}`);
91
+ const pct = (times) => String(Math.round(times * 1000) / 10);
92
+ /* One sentence per way of following; the measures that follow the same way are said together. */
93
+ const lines = new Map();
94
+ for (const axis of editable) {
95
+ const f = followOf(asset, part, axis);
96
+ let key;
97
+ let params = {};
98
+ if (f.of === 'own')
99
+ continue;
100
+ if (f.of === 'instance') {
101
+ key = f.times === 1 ? 'figure.text.summary-size-follows' : 'figure.text.summary-size-share';
102
+ params = { pct: pct(f.times), whole: say(`figure.label.measure-${f.axis}`) };
103
+ if (f.plus)
104
+ (key = key + '-plus'), (params.plus = String(f.plus));
105
+ }
106
+ else if (f.of === 'part') {
107
+ const other = say(`figure.label.measure-${f.axis}`);
108
+ key = (f.times ?? 1) === 1 ? 'figure.text.summary-size-part' : 'figure.text.summary-size-part-share';
109
+ params = { other: f.part, measure: particlesOf(other, lang).with, measureWord: other, pct: pct(f.times ?? 1) };
110
+ if (f.plus)
111
+ (key = key + '-plus'), (params.plus = String(f.plus));
112
+ }
113
+ else if (f.of === 'input') {
114
+ key = 'figure.text.summary-size-input';
115
+ params = { field: fieldWordsOf(f.input, k => say(k)) };
116
+ }
117
+ else
118
+ key = 'figure.text.summary-size-worked-out';
119
+ const id = key + JSON.stringify(params);
120
+ lines.set(id, { key, params, axes: [...(lines.get(id)?.axes ?? []), axis] });
95
121
  }
96
- return { kind, size, relation };
122
+ /* Nothing to say when the size is its own: only a measure that changes by itself is news (user 2026-10-01). */
123
+ const size = [...lines.values()].map(l => say(l.key, { ...l.params, axes: particlesOf(l.axes.map(word).join(' · '), lang).topic })).join(' ');
124
+ return { kind, size };
97
125
  }
98
126
  /**
99
127
  * A part's own field in the words its row uses -- 「위치 Y」, 「크기 X」, and the turn as one 「회전」 -- so a field the plan