@things-factory/figure-ui 10.1.28 โ†’ 10.1.29

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 (55) hide show
  1. package/client/modeller/angle-dial.ts +70 -0
  2. package/client/modeller/figure-animations.ts +9 -9
  3. package/client/modeller/figure-canvas.ts +6 -3
  4. package/client/modeller/figure-inspector.ts +18 -3
  5. package/client/modeller/figure-preview.ts +55 -3
  6. package/client/modeller/figure-report.ts +11 -28
  7. package/client/modeller/figure-side.ts +1 -0
  8. package/client/modeller/joint-gizmo.ts +63 -1
  9. package/client/modeller/part-edits.ts +75 -9
  10. package/client/pages/figure-modeller-page.ts +20 -2
  11. package/client/types.ts +2 -0
  12. package/dist-client/modeller/angle-dial.d.ts +32 -0
  13. package/dist-client/modeller/angle-dial.js +41 -0
  14. package/dist-client/modeller/angle-dial.js.map +1 -0
  15. package/dist-client/modeller/figure-animations.d.ts +4 -4
  16. package/dist-client/modeller/figure-animations.js +9 -9
  17. package/dist-client/modeller/figure-animations.js.map +1 -1
  18. package/dist-client/modeller/figure-canvas.js +6 -3
  19. package/dist-client/modeller/figure-canvas.js.map +1 -1
  20. package/dist-client/modeller/figure-inspector.d.ts +11 -0
  21. package/dist-client/modeller/figure-inspector.js +20 -3
  22. package/dist-client/modeller/figure-inspector.js.map +1 -1
  23. package/dist-client/modeller/figure-preview.d.ts +11 -0
  24. package/dist-client/modeller/figure-preview.js +53 -3
  25. package/dist-client/modeller/figure-preview.js.map +1 -1
  26. package/dist-client/modeller/figure-report.d.ts +8 -4
  27. package/dist-client/modeller/figure-report.js +11 -31
  28. package/dist-client/modeller/figure-report.js.map +1 -1
  29. package/dist-client/modeller/figure-side.js +1 -0
  30. package/dist-client/modeller/figure-side.js.map +1 -1
  31. package/dist-client/modeller/joint-gizmo.d.ts +4 -1
  32. package/dist-client/modeller/joint-gizmo.js +54 -0
  33. package/dist-client/modeller/joint-gizmo.js.map +1 -1
  34. package/dist-client/modeller/part-edits.d.ts +29 -4
  35. package/dist-client/modeller/part-edits.js +61 -9
  36. package/dist-client/modeller/part-edits.js.map +1 -1
  37. package/dist-client/pages/figure-modeller-page.d.ts +7 -0
  38. package/dist-client/pages/figure-modeller-page.js +20 -2
  39. package/dist-client/pages/figure-modeller-page.js.map +1 -1
  40. package/dist-client/tsconfig.tsbuildinfo +1 -1
  41. package/dist-client/types.d.ts +2 -0
  42. package/dist-client/types.js.map +1 -1
  43. package/package.json +5 -5
  44. package/test/angle-dial.test.ts +78 -0
  45. package/test/base-box.test.ts +11 -0
  46. package/test/expect.ts +2 -0
  47. package/test/i18n-prefix-guard.test.ts +17 -0
  48. package/test/joint-needle.test.ts +162 -0
  49. package/test/part-edits.test.ts +100 -0
  50. package/test/scene-budget.test.ts +29 -0
  51. package/translations/en.json +3 -7
  52. package/translations/ja.json +3 -7
  53. package/translations/ko.json +3 -7
  54. package/translations/ms.json +3 -7
  55. package/translations/zh.json +3 -7
@@ -1 +1 @@
1
- {"version":3,"file":"figure-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '../modeller/figure-side.js'\nimport '../modeller/figure-canvas.js'\nimport { captureThumbnail } from '../modeller/figure-thumbnail.js'\nimport '../modeller/figure-preview.js'\nimport { shouldStartNewFigure } from '../modeller/new-figure-entry.js'\nimport '../modeller/figure-parts.js'\nimport '../modeller/figure-inspector.js'\n\nimport { css, html, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { consumePendingFigureProposal } from '../modeller/figure-ai-target.js'\nimport { contractChanges, isPlaceholderType, placeholderType, TYPE_LENGTH, TYPE_PATTERN, validate } from '@hatiolab/figure-model'\nimport type { ContractChange, DetailLevel, FigureSource, PrimitiveKind } from '@hatiolab/figure-model'\n\nimport * as edits from '../modeller/part-edits.js'\nimport * as proposals from '../modeller/proposal.js'\nimport * as proposalSessions from '../modeller/proposal-session.js'\nimport type { ProposalSession } from '../modeller/proposal-session.js'\nimport { toFigureSource, fromFigureSource } from '../modeller/figure-source.js'\nimport type { FigureDraft, PartModel, PlaneAxis } from '../modeller/figure-source.js'\nimport { jointOf } from '../modeller/joint-edits.js'\nimport { poseAsDefaults } from '../modeller/pose-defaults.js'\nimport { gizmoPart } from '../modeller/joint-gizmo.js'\nimport { DEFAULT_VIEW } from '../modeller/figure-view.js'\nimport type { ViewSettings } from '../modeller/figure-view.js'\nimport { createFigure, fetchFigure, fetchFigureTypeNames, fetchReleasedSource, updateFigure } from '../graphql/index.js'\nimport type { Figure, FigureFinding, FigureGateFix, FigureProposal, ProposalFeedback } from '../types.js'\n\nconst FigureModellerPageBase = localize(i18next)(PageView) as typeof PageView\n\n/**\n * ์–ด๋А ์นธ์ด ์ ‘ํ˜€ ์žˆ๋‚˜. ์™ผ์ชฝ์€ ๋ถ€ํ’ˆ ๋ชฉ๋ก, ์˜ค๋ฅธ์ชฝ์€ ์†์„ฑยทํŒ์ •ยท์„ค์ •์ด๋‹ค.\n *\n * ์ด๋ฆ„์— fold ๋ฅผ ์•ˆ ์“ด๋‹ค. ์ด ํŒŒ์ผ์—์„œ ๊ทธ ๋‚ฑ๋ง์€ ์ด๋ฏธ fold.ts ์˜ ๊ฒƒ์ด๋‹ค โ€” ํŽธ์ง‘ ์ƒํƒœ๋ฅผ\n * ์ €์žฅ ํ˜•์‹์œผ๋กœ ์ ‘๋Š”๋‹ค๋Š” ๋œป์ด๋ผ ์นธ์„ ์ ‘๋Š” ๊ฒƒ๊ณผ ์•„๋ฌด ์ƒ๊ด€์ด ์—†๋‹ค. ํ•œ ํŒŒ์ผ์—์„œ ํ•œ ๋‚ฑ๋ง์ด\n * ๋‘ ๊ฐ€์ง€๋ฅผ ๋œปํ•˜๋ฉด ์ฝ๋Š” ์‚ฌ๋žŒ์ด ๋งค๋ฒˆ ์–ด๋А ์ชฝ์ธ์ง€ ๊ฐ€๋ ค์•ผ ํ•œ๋‹ค.\n */\ninterface Panes {\n left: boolean\n right: boolean\n}\n\n/**\n * ์ ‘์–ด ๋‘” ๊ฒƒ์€ **์ด ๋ธŒ๋ผ์šฐ์ €์—๋งŒ** ๋‚จ๋Š”๋‹ค.\n *\n * ์ €์ž‘ํ•œ Figure ์— ์‹ค๋ฆฌ๋Š” ๊ฐ’์ด ์•„๋‹ˆ๋‹ค. ํ™”๋ฉด์„ ์–ด๋–ป๊ฒŒ ๋ฒŒ๋ ค ๋†“๊ณ  ์ผํ•˜๋А๋ƒ๋Š” ์‚ฌ๋žŒ๋งˆ๋‹ค\n * ๋‹ค๋ฅด๊ณ , ๊ฐ™์€ Figure ๋ฅผ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์—ด์—ˆ์„ ๋•Œ ๋‚ด ํ™”๋ฉด ๋ฐฐ์น˜๊ฐ€ ๋”ฐ๋ผ๊ฐ€๋ฉด ๊ณค๋ž€ํ•˜๋‹ค.\n * ํ™”๋ฉด ์„ค์ •(`figure-view`)์„ ์ €์žฅํ•˜์ง€ ์•Š๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ด์œ ๋‹ค.\n *\n * ์‹œํฌ๋ฆฟ ์ฐฝ์ด๋‚˜ ์‚ฌ์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ง‰์€ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” **์ฝ๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ๋˜์ง„๋‹ค.** ๊ทธ๋•Œ๋Š”\n * ๋‘˜ ๋‹ค ํŽด์ง„ ์ฑ„๋กœ ์‹œ์ž‘ํ•œ๋‹ค โ€” ์ €์ž‘๋ฉด์ด ์•ˆ ๋œจ๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ซ๋‹ค.\n */\nconst PANES_KEY = 'figure-modeller.collapsed'\n\nfunction recallPanes(): Panes {\n try {\n const said = JSON.parse(localStorage.getItem(PANES_KEY) ?? 'null')\n return { left: said?.left === true, right: said?.right === true }\n } catch {\n return { left: false, right: false }\n }\n}\n\nfunction rememberPanes(panes: Panes): void {\n try {\n localStorage.setItem(PANES_KEY, JSON.stringify(panes))\n } catch {\n // ๋ชป ์ ์–ด๋„ ์ด๋ฒˆ ํ™”๋ฉด์€ ๊ทธ๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค. ๋‹ค์Œ์— ์—ด ๋•Œ ํŽด์ ธ ์žˆ์„ ๋ฟ์ด๋‹ค.\n }\n}\n\n/**\n * ์ƒˆ๋กœ ๋งŒ๋“  Figure ์˜ ์‹œ์ž‘ ์ƒํƒœ โ€” **๋นˆ ์ƒ์ž ํ•˜๋‚˜๋ฟ์ด๋‹ค.**\n *\n * ์ „์—๋Š” ๋ถ€ํ’ˆ(300 ์ƒ์ž)์„ ํ•˜๋‚˜ ๋†“์•„ ์ฃผ์—ˆ๋‹ค. ํ˜•์‹์ด ๋ถ€ํ’ˆ 0๊ฐœ๋ฅผ ์˜ค๋ฅ˜๋กœ ๋ณด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค\n * (`missing-parts` โ€” parts ๊ฐ€ ์—†๋‹ค). ๊ทธ๋ž˜์„œ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ–ˆ๋Š”๋ฐ ๋ถ‰์€ ํŒ์ •์ด ๋œจ๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋ ค๊ณ \n * ์ƒ์ž๋ฅผ ๊น”์•˜๋‹ค.\n *\n * **๊ทธ๋Ÿฐ๋ฐ ใ€Œ๋ฌด์กฐ๊ฑด ์ƒ์žใ€๋Š” ๋Œ€๊ฐœ ์ฒซ ๋™์ž‘์ด ๊ทธ๊ฒƒ์„ ์ง€์šฐ๋Š” ์ผ์ด ๋œ๋‹ค** โ€” ์ปจ๋ฒ ์ด์–ด๋ฅผ ๋งŒ๋“ค ์‚ฌ๋žŒ๋„,\n * ์‹œ๊ทธ๋„ํƒ€์›Œ๋ฅผ ๋งŒ๋“ค ์‚ฌ๋žŒ๋„ ์ƒ์ž๋ถ€ํ„ฐ ์น˜์šด๋‹ค. ๊ทธ๋ž˜์„œ ๋น„์šด๋‹ค. ํ˜•์‹์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ (๋ถ€ํ’ˆ ์—†๋Š” ๊ฒƒ์€\n * ๋ฐœํ–‰ํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด ๋งž๋‹ค) **ํ™”๋ฉด์ด ใ€Œ์•„์ง ์ €์ž‘ ์•ˆ ํ•จใ€๊ณผ ใ€Œํ˜•์‹์„ ์–ด๊น€ใ€์„ ๊ฐ€๋ฅธ๋‹ค.**\n *\n * ๋นˆ ํ™”๋ฉด์ด ๊ฐ€๋ฅด์น˜๋Š” ์ž๋ฆฌ๋‹ค: ๋ถ€ํ’ˆ ๋ชฉ๋ก์ด ใ€Œ์œ„์—์„œ ๋„ํ˜•์„ ๊ณจ๋ผ ์‹œ์ž‘ํ•˜์„ธ์š”ใ€๋ฅผ ์ด๋ฏธ ๋งํ•˜๊ณ  ํŒ”๋ ˆํŠธ๊ฐ€\n * ๋ฐ”๋กœ ๊ทธ ์œ„์— ์žˆ๋‹ค(`figure-parts`).\n */\nfunction startingModel(type: string): { draft: FigureDraft; parts: PartModel[] } {\n return {\n draft: { width: 300, height: 300, depth: 300, figureType: type, detailLevel: 'M' },\n parts: []\n }\n}\n\n/**\n * Figure ์ €์ž‘๋ฉด.\n *\n * ## ์„ธ ์นธ์ด๋‹ค\n *\n * ์™ผ์ชฝ ๋„ํ˜• ํŒ”๋ ˆํŠธ ยท ๋ถ€ํ’ˆ ๋ชฉ๋ก ยท ์†์„ฑ\n * ๊ฐ€์šด๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ โ€” `FigureInstance` ๋ฅผ ์‹ค์ œ๋กœ ์„ธ์šด๋‹ค\n * ์˜ค๋ฅธ์ชฝ ํŒ๋‹จ โ€” ๋Š˜ ๋–  ์žˆ๋‹ค\n *\n * ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ์”ฌ ์œ„์—์„œ ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ด ์ด ๋„๊ตฌ์˜ ์ „์ œ๋‹ค. **ํŽธ์ง‘ํ•œ ๊ฒƒ์ด ๊ณง ์‹ค์ œ ๊ฒฐ๊ณผ๋ฌผ**์ด๋ผ์•ผ\n * \"๋ชจ๋ธ๋Ÿฌ์—์„œ๋Š” ๋˜๋Š”๋ฐ ๋„๋ฉด์—์„œ๋Š” ๋‹ค๋ฅด๋‹ค\"๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š๋Š”๋‹ค.\n *\n * ## ํŽธ์ง‘ ๋ฐ์ดํ„ฐ๋Š” ์”ฌ ๋ชจ๋ธ ํ•˜๋‚˜๋ฟ์ด๋‹ค\n *\n * ์ด ํŽ˜์ด์ง€๊ฐ€ ๊ฐ–๋Š” ๊ฒƒ์€ ์ดˆ์•ˆ ํ•˜๋‚˜์™€ ๋ถ€ํ’ˆ ๋ชฉ๋ก โ€” ๋ฏธ๋ฆฌ๋ณด๊ธฐ์— ๊ทธ๋Œ€๋กœ ์„ธ์›Œ์ง€๋Š” ์”ฌ ๋ชจ๋ธ์ด๋‹ค.\n * ์ €์žฅ ํ˜•์‹(`FigureSource`)์œผ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ **๊ฒฝ๊ณ„์—์„œ๋งŒ** ํ•œ๋‹ค: ์ €์žฅํ•  ๋•Œ, ํŒ๋‹จํ•  ๋•Œ,\n * AI ํ›„๋ณด์™€ ๋น„๊ตํ•  ๋•Œ. ๊ทธ ๋ณ€ํ™˜์€ `figure-source.ts` ํ•œ ๊ณณ์—์„œ๋งŒ ์ผ์–ด๋‚œ๋‹ค.\n *\n * ํŒ”๋ ˆํŠธ๋„ ์†์„ฑ ํŽธ์ง‘๊ธฐ๋„ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฌด์—‡์„ ํ•˜๊ฒ ๋‹ค๋Š” ๊ฒƒ๋งŒ ์•Œ๋ฆฌ๊ณ ,\n * ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ ์ด ํŽ˜์ด์ง€๋‹ค. ๋‘ ๊ณณ์—์„œ ๊ณ ์น˜๋ฉด ์‹คํ–‰ ์ทจ์†Œ๋„ ์ €์žฅ๋„ ์–ด๋А ์ชฝ์ด ๋งž๋Š”์ง€ ์•Œ ์ˆ˜\n * ์—†๊ฒŒ ๋œ๋‹ค.\n */\n/**\n * Whether this name could be released, by the format's own rules (`type-not-identifier`,\n * `type-is-placeholder`). Saving is not blocked, but the type cannot change after creation, so the\n * author has to hear it now to avoid a draft that can never be released.\n */\nfunction releasableType(type: string): boolean {\n return (\n !isPlaceholderType(type) &&\n TYPE_PATTERN.test(type) &&\n type.length >= TYPE_LENGTH.min &&\n type.length <= TYPE_LENGTH.max\n )\n}\n\n@customElement('figure-modeller-page')\nexport class FigureModellerPage extends FigureModellerPageBase {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n /*\n ์ด ํ™”๋ฉด์˜ ๊ธ€์ž ํฌ๊ธฐ๋ฅผ ์—ฌ๊ธฐ์„œ ์ •ํ•œ๋‹ค.\n\n ์•ˆ ์ ์–ด ๋‘๋ฉด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ํฌ๊ธฐ(16px)๋ฅผ ๋ฌผ๋ ค๋ฐ›๋Š”๋‹ค. ์•„๋ž˜ ํŒจ๋„๋“ค์€ ์ „๋ถ€ 12px\n ์–ธ์ €๋ฆฌ๋ผ ์œ„ ๋‘ ์ค„๋งŒ ์ปค ๋ณด์ด๊ณ , ๋ฒ„ํŠผ์ด ํ™”๋ฉด ์ „์ฒด์™€ ๋”ฐ๋กœ ๋†€์•˜๋‹ค.\n\n form ์š”์†Œ(input ยท button)๋Š” ๊ธ€๊ผด์„ ๋ฌผ๋ ค๋ฐ›์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฐ์ž ์ ์–ด์•ผ ํ•œ๋‹ค.\n */\n font-size: 0.76rem;\n }\n\n /* ์„ธ ์นธ. 3D ๋ฅผ ๊ฐ€์žฅ ํฌ๊ฒŒ ๋‘”๋‹ค โ€” ์ด ๋„๊ตฌ์˜ ์ฃผ์–ด๋Š” ํ˜•์ƒ์ด์ง€ ์†์„ฑํ‘œ๊ฐ€ ์•„๋‹ˆ๋‹ค */\n div[edits] {\n flex: 1;\n display: grid;\n grid-template-columns: 220px 1fr 320px;\n min-height: 0;\n overflow: hidden;\n }\n\n /*\n **์„ธ ์นธ์˜ ์ž๋ฆฌ๋ฅผ ๋ชป ๋ฐ•๋Š”๋‹ค.**\n\n ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ์™ผ์ชฝ์„ ์ ‘์—ˆ์„ ๋•Œ ์˜ค๋ฅธ์ชฝ ์นธ์ด ๊ฐ€์šด๋ฐ ์ž๋ฆฌ๋กœ ๋ฐ€๋ ค ๋“ค์–ด์™€ ์บ”๋ฒ„์Šค๊ฐ€\n ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ง„๋‹ค. display none ์ธ ํ˜•์ œ๋Š” ๊ทธ๋ฆฌ๋“œ ์ž๋ฆฌ๋ฅผ ์ฐจ์ง€ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์ฒซ์งธ๊ฐ€\n ๋น ์ง€๋ฉด ๋‚˜๋จธ์ง€๊ฐ€ ํ•œ ์นธ์”ฉ ๋‹น๊ฒจ์ง„๋‹ค. ์˜ค๋ฅธ์ชฝ์€ ๋งˆ์ง€๋ง‰์ด๋ผ ๋น ์ ธ๋„ ๋ฐ€ ๊ฒƒ์ด ์—†์–ด์„œ\n ๋ฉ€์ฉกํ–ˆ๋‹ค โ€” ํ•œ์ชฝ๋งŒ ๊ณ ์žฅ ๋‚˜๋Š” ์ข…๋ฅ˜๋‹ค.\n\n ์ด ํŒŒ์ผ์ด ์ด๋ฏธ ํ•œ ๋ฒˆ ์ ์–ด ๋‘” ํ•จ์ •์ด๋‹ค. ์•„๋ž˜ div[lanes] ์ฃผ์„์— ๊ฐ™์€ ๋ง์ด ์žˆ๋‹ค.\n */\n div[edits] > figure-parts {\n grid-column: 1;\n }\n div[edits] > div[middle] {\n grid-column: 2;\n }\n div[edits] > figure-side {\n grid-column: 3;\n }\n\n /*\n ์ ‘ํžŒ ์นธ์€ **์ž๋ฆฌ๋ฅผ ์•„์˜ˆ ์•ˆ ์“ด๋‹ค.**\n\n ํญ๋งŒ 0 ์œผ๋กœ ์ค„์ด๋ฉด ์•ˆ์ชฝ์ด ๋„˜์ณ ๋‚˜์™€ ๊ฐ€์šด๋ฐ๋ฅผ ๋ฎ๋Š”๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ๋–ผ์–ด ๋‚ด๋ฉด ์˜ค๋ฅธ์ชฝ\n ์นธ์ด ํŽผ์ณ ๋‘” ์ถ”์ด ๊ทธ๋ฆผ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ์žƒ๋Š”๋‹ค โ€” figure-side ๊ฐ€ ํƒญ์„ ๊ฐ์ถœ ๋•Œ๋„ DOM ์—๋Š”\n ๋‚จ๊ธฐ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ด์œ ๋‹ค. display none ์ด ๋‘˜์„ ๋‹ค ์ง€ํ‚จ๋‹ค.\n\n ๊ฐ€์šด๋ฐ๋Š” ์ ‘์„ ๋•Œ๋งˆ๋‹ค ๋„“์ด๊ฐ€ ๋ฐ”๋€Œ๋Š”๋ฐ, ํŽธ์ง‘ ์บ”๋ฒ„์Šค์™€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ๊ฐ์ž ResizeObserver\n ๋ฅผ ๊ฐ–๊ณ  ์žˆ์–ด 3D ๊ฐ€ ์•Œ์•„์„œ ๋”ฐ๋ผ์˜จ๋‹ค.\n */\n div[edits][collapse-left] {\n grid-template-columns: 0 1fr 320px;\n }\n div[edits][collapse-right] {\n grid-template-columns: 220px 1fr 0;\n }\n div[edits][collapse-left][collapse-right] {\n grid-template-columns: 0 1fr 0;\n }\n div[edits][collapse-left] > figure-parts,\n div[edits][collapse-right] > figure-side {\n display: none;\n }\n\n div[spacer] {\n flex: 1;\n }\n\n /*\n ์นธ์„ ๋ฒกํ„ฐ ์ €์ž‘๋„๊ตฌ์˜ ๊ด€๋ก€๋Œ€๋กœ ๋†“๋Š”๋‹ค โ€” ์™ผ์ชฝ์— ๋ฌด์—‡์ด ์žˆ๋‚˜(๋ ˆ์ด์–ด), ์˜ค๋ฅธ์ชฝ์—\n ๊ณ ๋ฅธ ๊ฒƒ์˜ ์†์„ฑ. ์“ฐ๋Š” ์‚ฌ๋žŒ์ด ์ด๋ฏธ ์•„๋Š” ์ž๋ฆฌ๋ผ ๋ฐฐ์šธ ๊ฒƒ์ด ํ•˜๋‚˜ ์ค„์–ด๋“ ๋‹ค.\n ์ฐจ๋ณ„์„ฑ์€ ๋ฐฐ์น˜๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ€์šด๋ฐ์˜ ๋กœ์šฐํด๋ฆฌ 3D ์— ์žˆ์œผ๋ฏ€๋กœ, ๋ฐฐ์น˜๋กœ ๋‚ฏ์„ค๊ฒŒ ํ•˜์ง€\n ์•Š๋Š”๋‹ค.\n\n ์˜ค๋ฅธ์ชฝ์€ figure-side ๊ฐ€ ์†์„ฑ๊ณผ ํŒ๋‹จ์„ ํƒญ์œผ๋กœ ๊ฒน์ณ ๋‘”๋‹ค. ์œ„์•„๋ž˜๋กœ ๋‚˜๋ˆ„๋ฉด ๋‘˜ ๋‹ค\n ์ž˜๋ ค์„œ ์†์„ฑ์€ ์žฌ์งˆ๊นŒ์ง€, ํŒ๋‹จ์€ ๋ฌด๊ฒŒ๊นŒ์ง€ ๋ชป ๋‚ด๋ ค๊ฐ„๋‹ค. ๊ฐœ์ˆ˜๋Š” ํƒญ ์ด๋ฆ„ํ‘œ์— ๋‚จ์•„\n ํŒ๋‹จ์„ ๋ฎ์–ด ๋‘์–ด๋„ ๋ณด์ธ๋‹ค.\n */\n figure-side {\n min-height: 0;\n }\n\n figure-canvas,\n figure-preview {\n min-width: 0;\n min-height: 0;\n }\n\n /*\n ํŽธ์ง‘ ยท ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ „ํ™˜ ๋ฐ ์กฐ์ž‘ ๋ .\n\n ๊ฐ€์šด๋ฐ ์นธ ์œ„์— ์–‡๊ฒŒ ๋‘”๋‹ค. ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€๊ฐ€ ๋Š˜ ๋ณด์—ฌ์•ผ ํ•˜๊ณ , ๋‘ ํ™”๋ฉด์ด ๊ฐ™์€\n ์ž๋ฆฌ๋ฅผ ์“ฐ๋ฏ€๋กœ ์–ด๋А ์ชฝ์ธ์ง€ ๋ชจ๋ฅด๋ฉด ํŽธ์ง‘์ด ์•ˆ ๋จน๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ฝํžŒ๋‹ค.\n */\n div[modes] {\n display: flex;\n align-items: center;\n flex: none;\n gap: 2px;\n padding: 4px var(--spacing-medium, 8px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n div[modes] button[mode] {\n padding: 3px 12px;\n font: var(--label-font, inherit);\n font-size: 0.74rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[mode]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n div[modes] button[mode][on] {\n font-weight: 700;\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n }\n div[modes] div[separator] {\n width: 1px;\n height: 16px;\n margin: 0 4px;\n background-color: var(--md-sys-color-outline-variant);\n }\n div[modes] button[history] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[history]:hover:not([disabled]) {\n background-color: var(--md-sys-color-surface-container);\n color: var(--md-sys-color-on-surface);\n }\n div[modes] button[history][disabled] {\n opacity: 0.3;\n cursor: default;\n }\n div[modes] button[history] md-icon {\n --md-icon-size: 18px;\n }\n div[modes] button[collapse] {\n display: flex;\n align-items: center;\n flex: none;\n padding: 2px 3px;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[collapse]:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n div[modes] button[collapse] md-icon {\n --md-icon-size: 18px;\n }\n div[modes] span[dirty] {\n align-self: center;\n margin-right: 6px;\n font: var(--label-font, inherit);\n font-size: 0.72rem;\n color: var(--md-sys-color-tertiary);\n }\n div[modes] span[save-failed] {\n align-self: center;\n max-width: 240px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 6px;\n padding: 3px 7px;\n font: var(--label-font, inherit);\n font-size: 0.7rem;\n color: var(--md-sys-color-on-error-container, var(--md-sys-color-on-surface));\n background-color: var(--md-sys-color-error-container);\n border-radius: 4px;\n }\n div[modes] button[save] {\n padding: 4px 14px;\n margin-right: 4px;\n font: var(--label-font, inherit);\n font-size: 0.74rem;\n font-weight: 600;\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[save]:hover:not([disabled]) {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n }\n div[modes] button[save][disabled] {\n opacity: 0.35;\n cursor: default;\n }\n /*\n ํ›„๋ณด๊ฐ€ ์˜ค๋ฉด ๊ฐ€์šด๋ฐ๋ฅผ ๋‘˜๋กœ ๋‚˜๋ˆˆ๋‹ค.\n\n ์ˆซ์ž์™€ JSON ์œผ๋กœ ์Šน๋‚™์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค โ€” ์ €์ž‘ํ•œ ๊ฒƒ์ด ๊ณง ์‹ค๋ฌผ์ด๋ฏ€๋กœ **๋‘˜ ๋‹ค ์„ธ์›Œ\n ๋†“๊ณ ** ๊ณ ๋ฅด๊ฒŒ ํ•œ๋‹ค. ๋‚˜๋ž€ํžˆ ๋‘์ง€ ์•Š์œผ๋ฉด ๋ฌด์—‡์ด ๋‹ฌ๋ผ์กŒ๋Š”์ง€ ๊ธฐ์–ต์œผ๋กœ ๊ฒฌ์ค˜์•ผ ํ•œ๋‹ค.\n */\n /*\n ์„ธ๋กœ๋กœ ์Œ“์•„ ๋‘”๋‹ค. ๊ทธ๋ฆฌ๋“œ๋กœ ๋‘์—ˆ๋”๋‹ˆ ํ›„๋ณด๊ฐ€ ์—†์„ ๋•Œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ์ฒซ ์ค„(auto)์—\n ๋–จ์–ด์ ธ **๋†’์ด๊ฐ€ 0** ์ด ๋๋‹ค โ€” 3D ๊ฐ€ ํ†ต์งธ๋กœ ์‚ฌ๋ผ์กŒ๋‹ค. display:none ์ธ ํ˜•์ œ๋Š”\n ๊ทธ๋ฆฌ๋“œ ์ž๋ฆฌ๋ฅผ ์ฐจ์ง€ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n */\n div[middle] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n }\n /*\n ๊ฐ€์šด๋ฐ๋ฅผ ์ฑ„์šด๋‹ค. ๋‘˜ ๋‹ค ์ ์–ด์•ผ ํ•œ๋‹ค โ€” ํ•˜๋‚˜๋งŒ ์ ์œผ๋ฉด ๊ฐ์ถฐ์กŒ๋‹ค ๋‚˜ํƒ€๋‚œ ์ชฝ์ด ๋†’์ด 0 ์ด\n ๋˜์–ด ํ™”๋ฉด์ด ํ•˜์–—๊ฒŒ ๋œฌ๋‹ค. ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ๋‚ฌ๋‹ค.\n */\n div[middle] > figure-canvas,\n div[middle] > figure-preview {\n flex: 1;\n }\n div[lanes] {\n flex: 1;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1px;\n min-height: 0;\n background-color: var(--md-sys-color-outline-variant);\n }\n div[lane] {\n display: grid;\n grid-template-rows: auto 1fr;\n min-width: 0;\n min-height: 0;\n background-color: var(--md-sys-color-surface);\n }\n div[lane] h4 {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0;\n padding: 5px var(--spacing-medium, 8px);\n font: var(--label-font, inherit);\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 div[lane][candidate] h4 {\n color: var(--md-sys-color-primary);\n }\n div[lane] h4 small {\n font-weight: 400;\n letter-spacing: 0;\n text-transform: none;\n }\n\n /* ๋ฐ›๊ฑฐ๋‚˜ ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ ์‚ฌ์ด์— ๋‹ค๋ฅธ ๊ฒƒ์€ ์—†๋‹ค */\n div[decide] {\n display: flex;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n background-color: var(--md-sys-color-primary-container);\n color: var(--md-sys-color-on-primary-container);\n font-size: 0.76rem;\n }\n div[decide] div[spacer] {\n flex: 1;\n }\n div[decide] button {\n padding: 5px 14px;\n font: var(--label-font, inherit);\n border-radius: 6px;\n cursor: pointer;\n }\n button[take] {\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n border: none;\n }\n button[drop] {\n color: inherit;\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n }\n button[take][disabled] {\n opacity: 0.4;\n cursor: default;\n }\n\n /* AI์˜ ํ‰๊ฐ€๋Š” ํŒ์ •๋ฌธ์ด ์•„๋‹ˆ๋ผ ์ˆ˜๋ฝ ์ „์— ์ฝ์„ ๊ทผ๊ฑฐ๋‹ค. ์ˆจ๊ธฐ์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ ์นฉ ์œ„์— ๋‘”๋‹ค. */\n div[quality-review] {\n padding: 7px var(--spacing-large, 12px);\n background-color: var(--md-sys-color-primary-container);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n color: var(--md-sys-color-on-primary-container);\n font-size: 0.72rem;\n line-height: 1.45;\n }\n div[quality-review][review] {\n background-color: var(--md-sys-color-tertiary-container);\n color: var(--md-sys-color-on-tertiary-container);\n }\n div[quality-review] strong { display: block; font-size: 0.74rem; }\n div[quality-review] ul { margin: 4px 0 0; padding-left: 18px; }\n div[proposal-note] {\n padding: 6px var(--spacing-large, 12px) 8px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n background-color: var(--md-sys-color-surface-container-low);\n }\n div[proposal-note] label { display: block; font-size: 0.71rem; color: var(--md-sys-color-on-surface-variant); }\n div[proposal-note] input {\n box-sizing: border-box;\n width: 100%;\n margin-top: 4px;\n padding: 5px 7px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 5px;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface);\n font: inherit;\n }\n\n /*\n ๋ฌด์—‡์„ ๋ฐ”๊พธ์ž๋Š” ๊ฒƒ์ธ๊ฐ€ โ€” ์นฉ ํ•˜๋‚˜๊ฐ€ ๋ณ€๊ฒฝ ํ•˜๋‚˜๋‹ค.\n\n ํ†ต์งธ๋กœ๋งŒ ๋ฐ›๊ฒŒ ๋‘๋ฉด ใ€Œ๋ชธํ†ต์€ ์ข‹์€๋ฐ ๋ชจํ„ฐ ์ž๋ฆฌ๊ฐ€ ํ‹€๋ ธ๋‹คใ€์— ๋‹ตํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๊ฒŒ\n ๊ฐ€์žฅ ํ”ํ•œ ๋ฐ˜์‘์ด๋‹ค. ์ฒ˜์Œ์—๋Š” ์ „๋ถ€ ๊ณจ๋ผ ๋‘”๋‹ค โ€” ํ†ต์งธ๋กœ ๋ฐ›๋Š” ๊ฒƒ์ด ๋ณดํ†ต์˜ ๊ธธ์ด๊ณ ,\n ๋นผ๊ณ  ์‹ถ์€ ๊ฒƒ๋งŒ ๋„๋ฉด ๋œ๋‹ค.\n */\n div[changes] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n padding: 0 var(--spacing-large, 12px) var(--spacing-medium, 8px) var(--spacing-large, 12px);\n background-color: var(--md-sys-color-primary-container);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n button[change] {\n display: flex;\n align-items: center;\n gap: 3px;\n padding: 2px 9px 2px 5px;\n font: var(--label-font, inherit);\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface-variant, 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: 999em;\n cursor: pointer;\n }\n button[change][on] {\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n border-color: transparent;\n }\n button[change] md-icon {\n --md-icon-size: 14px;\n }\n /* ์•ˆ ๊ณ ๋ฅธ ๊ฒƒ์€ ๊ธ€์ž์— ์ค„์„ ๊ทธ์–ด ใ€Œ์•ˆ ๋ฐ›๋Š”๋‹คใ€๋ฅผ ๋งํ•œ๋‹ค */\n button[change]:not([on]) span[key] {\n text-decoration: line-through;\n }\n\n /* ๊ณ ๋ฅธ ์กฐํ•ฉ์ด ํ˜•์‹์„ ์–ด๊ธฐ๋ฉด ๋ฏธ๋ฆฌ ์•Œ๋ฆฐ๋‹ค. ์„ ํƒํ•œ ๋’ค์— ๊ฑฐ์ ˆ๋‹นํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ซ๋‹ค */\n /* ์ •์ฑ…์„ ๋„˜์€ ๊ฒƒ์€ ๋ง‰์ง€ ์•Š๋Š”๋‹ค โ€” ์•Œ๋ฆฌ๊ธฐ๋งŒ ํ•œ๋‹ค. ์ƒ‰์œผ๋กœ ๊ฐˆ๋ผ ๋‘”๋‹ค */\n p[blocked][warn] {\n color: var(--md-sys-color-tertiary);\n }\n p[blocked] {\n margin: 0;\n padding: 0 var(--spacing-large, 12px) var(--spacing-medium, 8px) var(--spacing-large, 12px);\n background-color: var(--md-sys-color-primary-container);\n font-size: 0.72rem;\n line-height: 1.5;\n color: var(--md-sys-color-error);\n }\n .save-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n }\n .save-modal-card {\n background: var(--md-sys-color-surface-container-high, #fff);\n color: var(--md-sys-color-on-surface, #1f1f1f);\n border-radius: 12px;\n padding: 24px;\n width: 380px;\n max-width: 90vw;\n box-shadow: 0 12px 36px rgba(0, 0, 0, 0.28);\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n .save-modal-title {\n margin: 0;\n font-size: 1.15rem;\n font-weight: 700;\n }\n .save-modal-desc {\n margin: 0;\n font-size: 0.8rem;\n color: var(--md-sys-color-on-surface-variant, #666);\n }\n .save-modal-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .save-modal-field label {\n font-size: 0.76rem;\n font-weight: 600;\n color: var(--md-sys-color-on-surface-variant, #555);\n }\n .save-modal-field input {\n padding: 8px 10px;\n border: 1px solid var(--md-sys-color-outline, #ccc);\n border-radius: 6px;\n font-size: 0.88rem;\n background: var(--md-sys-color-surface, #fff);\n color: inherit;\n }\n .save-modal-error {\n color: var(--md-sys-color-error, #b00020);\n font-size: 0.78rem;\n }\n .save-modal-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-top: 8px;\n }\n .save-modal-actions button {\n padding: 6px 14px;\n border-radius: 6px;\n font-size: 0.82rem;\n cursor: pointer;\n border: none;\n }\n .save-modal-actions .btn-cancel {\n background: none;\n color: var(--md-sys-color-on-surface, inherit);\n }\n .save-modal-actions .btn-confirm {\n background: var(--md-sys-color-primary, #0066cc);\n color: var(--md-sys-color-on-primary, #fff);\n font-weight: 600;\n }\n .save-modal-actions .btn-confirm:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n `\n\n /** ํŽธ์ง‘ ์ค‘์ธ Figure. ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ์ค‘์ด๋ฉด `id` ๊ฐ€ ์—†๋‹ค. */\n @state() private figure?: Figure\n /** ํŽธ์ง‘ ๋ฐ์ดํ„ฐ โ€” ๋ฏธ๋ฆฌ๋ณด๊ธฐ์— ๊ทธ๋Œ€๋กœ ์„ธ์›Œ์ง€๋Š” ์”ฌ ๋ชจ๋ธ์ด๋‹ค. */\n @state() private draft?: FigureDraft\n @state() private parts: PartModel[] = []\n @state() private selected = -1\n /*\n ์ €์ž‘ ํ™”๋ฉด์˜ **๋ณด๊ธฐ ์„ค์ •**. ์กฐ๋ช… ยท ๋ฐ”๋‹ฅ ยท ์ถ• ๊ฐ€์ด๋“œ ๊ฐ™์€ ๊ฒƒ์ด๋‹ค.\n\n ์ž์‚ฐ์ด ์•„๋‹ˆ๋ผ ์ž‘์—…๋Œ€๋ผ ์ €์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค โ€” `toFigureSource` ๊ฐ€ ์ง‘์–ด ๊ฐ€์ง€ ์•Š๋Š”๋‹ค.\n ์ž์„ธํ•œ ๊ฒƒ์€ `modeller/figure-view.ts` ๋ฅผ ๋ณด๋ผ.\n */\n @state() private view: ViewSettings = DEFAULT_VIEW\n /**\n * ๊ฐ€์šด๋ฐ ์นธ์ด ๋ฌด์—‡์„ ๋ณด์ด๋‚˜.\n *\n * ํŽธ์ง‘ ์บ”๋ฒ„์Šค๋Š” ๋Š˜ ๊ธฐ์ค€ ํฌ๊ธฐ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ํ˜•์‹์˜ ๊ฐ’ ์ ˆ๋ฐ˜์€ ํฌ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์กŒ์„ ๋•Œ ์–ด๋–ป๊ฒŒ\n * ๋ฐ˜์‘ํ•˜๋Š”๊ฐ€์— ์žˆ๋‹ค. ๊ทธ๊ฒƒ์„ ๋ณผ ์ž๋ฆฌ๊ฐ€ ์—†์—ˆ๋‹ค.\n */\n @state() private mode: 'edit' | 'preview' = 'edit'\n /**\n * ์–ด๋А ์นธ์ด ์ ‘ํ˜€ ์žˆ๋‚˜.\n *\n * ํ†ต์งธ๋กœ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ์•ˆ์ชฝ ๊ฐ’๋งŒ ๋ฐ”๊พธ๋ฉด Lit ์ด ๊ฐ™์€ ๊ฐ์ฒด๋กœ ๋ณด๊ณ  ๋‹ค์‹œ ๊ทธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n */\n @state() private collapsed: Panes = recallPanes()\n @state() private dirty = false\n @state() private saving = false\n @state() private violations: FigureFinding[] = []\n /**\n * ์ €์žฅ์ด ์™œ ์•ˆ ๋๋‚˜.\n *\n * **`save()` ์— `catch` ๊ฐ€ ์—†์—ˆ๋‹ค.** `try โ€ฆ finally` ๋ฟ์ด๋ผ ์„œ๋ฒ„๊ฐ€ ๊ฑฐ์ ˆํ•˜๋ฉด(ํ˜•์‹ ์œ„๋ฐ˜ ยท ํƒ€์ž… ์ด๋ฆ„\n * ์ค‘๋ณต ยท ๊ถŒํ•œ ์—†์Œ ยท ๋„คํŠธ์›Œํฌ) ๋‹จ์ถ”๋งŒ ๋ฉˆ์ถ”๊ณ  ํ™”๋ฉด์€ **์•„๋ฌด ๋ง๋„ ํ•˜์ง€ ์•Š์•˜๋‹ค.** ์ €์ž‘์ž๋Š” ๋ˆŒ๋ €๋Š”๋ฐ\n * ์•„๋ฌด ์ผ๋„ ์•ˆ ๋‚œ ๊ฒƒ์œผ๋กœ ๋ณด๊ณ , ์ €์žฅ๋๋Š”์ง€ ์•„๋‹Œ์ง€๋„ ์•Œ ์ˆ˜ ์—†์—ˆ๋‹ค.\n *\n * ๋ฏธ์ €์žฅ ํ‘œ์‹œ(`dirty`)๋Š” ๊ทธ๋Œ€๋กœ ๋‚จ์œผ๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์žƒ์ง€๋Š” ์•Š์•˜๋‹ค. ์žƒ์€ ๊ฒƒ์€ **์‚ฌ์œ **๋‹ค.\n */\n @state() private saveFailure = ''\n @state() private showSaveModal = false\n @state() private saveModalType = ''\n @state() private saveModalName = ''\n @state() private saveModalError = ''\n /** ์ €์ž‘ ๋ณด์กฐ ๋˜๋Š” ๋ฐœํ–‰ ๊ฒŒ์ดํŠธ๊ฐ€ ์—ฐ ํ›„๋ณด ๊ฒ€ํ†  ์„ธ์…˜. ๋ฐ›๊ธฐ ์ „์—๋Š” ์ •๋ณธ์ด ์•„๋‹ˆ๋‹ค. */\n @state() private proposalSession?: ProposalSession\n /** ์ธ๋ผ์ธ ์š”์ฒญ์ฐฝ๊ณผ AI ๋„ํฌ๊ฐ€ ํ•จ๊ป˜ ์“ฐ๋Š” ํ˜„์žฌ Figure์˜ ์ œํ•œ๋œ ์„ธ์…˜ ํ”ผ๋“œ๋ฐฑ. */\n @state() private proposalFeedback: ProposalFeedback[] = []\n\n @state() private undoStack: { draft?: FigureDraft; parts: PartModel[]; selected: number }[] = []\n @state() private redoStack: { draft?: FigureDraft; parts: PartModel[]; selected: number }[] = []\n @state() private sceneRevision = 0\n private lastCoalesceKey?: string\n private lastHistoryTime = 0\n\n /**\n * ์ €์žฅ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๊ฒƒ. ์”ฌ ๋ชจ๋ธ์ด ๋ฐ”๋€” ๋•Œ๋งŒ ๋‹ค์‹œ ๋งŒ๋“ ๋‹ค.\n *\n * ์ €์žฅยทํŒ๋‹จยทAI ํ›„๋ณด ๋น„๊ต๊ฐ€ ์ด๊ฒƒ์„ ์“ด๋‹ค. ๋ Œ๋”๋งํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ ๋งŒ๋“ค๋ฉด ํŒ๋‹จ ํŒจ๋„์ด ๋งค๋ฒˆ\n * ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๋ฏ€๋กœ, ๋ณ€ํ™˜์€ `willUpdate` ์—์„œ ํ•œ ๋ฒˆ๋งŒ ํ•œ๋‹ค.\n */\n private folded?: FigureSource\n\n /**\n * ์ง์ „ ๋ฐœํ–‰ํŒ์˜ ์ •๋ณธ. ํ•œ ๋ฒˆ๋„ ๋ฐœํ–‰ ์•ˆ ํ•œ ๋„ํ˜•์—๋Š” ์—†๋‹ค.\n *\n * ๋ฐœํ–‰๋œ ๋„ํ˜•์„ ๊ณ ์น˜๋Š” ๋™์•ˆ ใ€Œ์ด ๊ณ ์นจ์ด ์ด๋ฏธ ๋†“์ธ ๊ฒƒ์„ ๊นจ๋‚˜ใ€๋ฅผ ๊ฒฌ์ฃผ๋ ค๋ฉด ์ด๊ฒƒ์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.\n * ์ €์žฅ ์ „ ์ดˆ์•ˆ์€ ์„œ๋ฒ„๊ฐ€ ๋ชจ๋ฅด๋ฏ€๋กœ ํŒ์ •๋„ ์—ฌ๊ธฐ์„œ ํ•œ๋‹ค โ€” ํ˜•์‹์˜ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ์“ด๋‹ค(ADR-0063 ๊ฒฐ์ • 3).\n */\n @state() private releasedSource?: FigureSource\n\n /** ์ง€๊ธˆ ๊ณ ์นœ ๊ฒƒ์ด ๋†“์ธ ๊ฒƒ์— ๋ฌด์—‡์„ ํ•˜๋‚˜. ๋ฐœํ–‰ ์ „์—๋Š” ๋น„์–ด ์žˆ๋‹ค. */\n @state() private contract: ContractChange[] = []\n\n /** ๊ฒฌ์ฃผ์ง€ ๋ชปํ•œ ๊นŒ๋‹ญ. **๋น„์–ด ์žˆ๋Š” ๋ชฉ๋ก๊ณผ ๋‹ค๋ฅด๋‹ค** โ€” ใ€Œ๊นจ์ง€ ์•Š๋Š”๋‹คใ€๊ฐ€ ์•„๋‹ˆ๋ผ ใ€Œ๋ชจ๋ฅธ๋‹คใ€๋‹ค. */\n @state() private contractFailure?: string\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('draft') || changed.has('parts') || changed.has('releasedSource')) {\n this.folded = this.draft ? toFigureSource(this.draft, this.parts) : undefined\n this.contract = this.compareWithReleased()\n }\n }\n\n /**\n * ๊ณ ์น˜๋Š” ์ค‘์˜ ์ •๋ณธ์€ **์ž ๊น ํ˜•์‹์„ ๋ชป ์ง€๋‚  ์ˆ˜ ์žˆ๋‹ค** โ€” ๋ถ€ํ’ˆ ์ด๋ฆ„์„ ์ง€์› ๋‹ค๊ฐ€ ๋‹ค์‹œ ์ ๋Š” ์‚ฌ์ด๊ฐ€\n * ๊ทธ๋ ‡๋‹ค. ๊ทธ๋•Œ ๊ฒฌ์ฃผ๊ธฐ๊ฐ€ ๋˜์ง€๋ฉด ํ™”๋ฉด์ด ํ†ต์งธ๋กœ ๋ฉˆ์ถ”๋ฏ€๋กœ ๋ชฉ๋ก์€ ๋น„์šด๋‹ค.\n *\n * **๋นˆ ๋ชฉ๋ก์„ ใ€Œ๊นจ์ง€ ์•Š๋Š”๋‹คใ€๋กœ ๋ณด์ด๊ฒŒ ๋‘์ง€ ์•Š๋Š”๋‹ค.** ๊นŒ๋‹ญ์„ ํ•จ๊ป˜ ๋“ค๊ณ  ์žˆ๋‹ค๊ฐ€ ํ™”๋ฉด์ด ใ€Œํ™•์ธํ•˜์ง€\n * ๋ชปํ–ˆ๋‹คใ€๋กœ ๋งํ•œ๋‹ค. ์กฐ์šฉํžˆ ๋น„์šฐ๋ฉด ์ €์ž‘์ž๋Š” ์•ˆ์ „ํ•˜๋‹ค๊ณ  ์ฝ๋Š”๋‹ค โ€” ์‹ค์ œ๋กœ ๊ทธ๋Ÿฐ ์ผ์ด ๋‚ฌ๋‹ค: ์•ฑ์—\n * ๋“  ํ˜•์‹์ด ์˜› ํŒ์ด๋ผ ๊ฒฌ์ฃผ๋Š” ํ•จ์ˆ˜ ์ž์ฒด๊ฐ€ ์—†์—ˆ๋Š”๋ฐ, ํ™”๋ฉด์€ ์•„๋ฌด ๋ง๋„ ์•ˆ ํ•˜๊ณ  ์กฐ์šฉํ–ˆ๋‹ค.\n */\n private compareWithReleased(): ContractChange[] {\n this.contractFailure = undefined\n if (!this.releasedSource || !this.folded) return []\n\n try {\n return contractChanges(this.releasedSource, this.folded)\n } catch (e) {\n this.contractFailure = (e as Error).message\n return []\n }\n }\n\n /**\n * ํŽ˜์ด์ง€ ๋ฐ– AI ๋„ํฌ์—๋„ **์ง€๊ธˆ ์†์— ๋“  ์ •๋ณธ**์„ ์•Œ๋ ค ์ค€๋‹ค.\n *\n * asidebar ๋„ํฌ๋Š” ์ฃผ์†Œ์˜ id๋งŒ\n * ์•Œ์•˜์œผ๋ฏ€๋กœ DB์— ์ €์žฅ๋œ ์˜› ํŒ์„ ์ฝ์—ˆ๋‹ค. ์ €์žฅ ์ „ ๋ณ€๊ฒฝ์„ ๋‘๊ณ  \"์™œ ๋ฐœํ–‰์ด ์•ˆ ๋˜๋‚˜\" ๋˜๋Š”\n * \"์ด ๋ถ€ํ’ˆ์„ ์ค„์—ฌ ๋‹ฌ๋ผ\"๊ณ  ๋ฌผ์œผ๋ฉด ๋Œ€ํ™”๊ฐ€ ์—‰๋šฑํ•œ ํŒ์„ ๋ณด๊ฒŒ ๋˜๋Š” ๊ฒฐํ•จ์ด์—ˆ๋‹ค.\n *\n * ์ด ์ด๋ฒคํŠธ๋Š” ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋„ํฌ๊ฐ€ ๋Œ€ํ™” ์ธ์ž๋กœ๋งŒ ์ž ์‹œ ๋“ค๊ณ  ์žˆ๊ณ , ์„œ๋ฒ„ ๋„๊ตฌ๋„ ๋„๋ฉ”์ธ ์†Œ์†\n * Figure๋ฅผ ๋จผ์ € ํ™•์ธํ•œ ๋’ค์—๋งŒ ์ด source๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ํŽ˜์ด์ง€๋ฅผ ๋– ๋‚  ๋•Œ๋Š” ์•„๋ž˜์—์„œ ์ง€์›Œ ์˜ค๋ž˜๋œ\n * ์ดˆ์•ˆ์ด ๋‹ค์Œ Figure์— ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\n updated(changed: Map<string, unknown>) {\n if (changed.has('figure') && (changed.get('figure') as Figure | undefined)?.id !== this.figure?.id) {\n this.proposalFeedback = []\n }\n if (changed.has('draft') || changed.has('parts') || changed.has('figure')) {\n window.dispatchEvent(\n new CustomEvent('figure-ai-context', {\n detail: { figureId: this.figure?.id, source: this.folded }\n })\n )\n }\n }\n\n private announceAiContext = () => {\n if (!this.active) return\n window.dispatchEvent(new CustomEvent('figure-ai-context', {\n detail: { figureId: this.figure?.id, source: this.folded }\n }))\n }\n\n /*\n `detail.staged` is the dock's answer to \"did anything actually happen to my candidate\". The\n listener is synchronous, so the dock reads it right after dispatching and only then tells the\n chat card to say so. It used to be called `accepted`, which claimed more than it knew โ€” the\n page had put the candidate somewhere, not the person had agreed to it.\n */\n private receiveDockProposal = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!detail) return\n\n const source =\n detail?.source ||\n (detail?.proposal?.source\n ? typeof detail.proposal.source === 'string'\n ? JSON.parse(detail.proposal.source)\n : detail.proposal.source\n : undefined)\n\n if (source) {\n detail.staged = this.openProposalForReview(source, detail.proposal)\n }\n }\n\n private onFigureAiStage = (event: Event) => {\n const detail = (event as CustomEvent).detail\n const source = detail?.source || (detail?.proposal?.source ? (typeof detail.proposal.source === 'string' ? JSON.parse(detail.proposal.source) : detail.proposal.source) : undefined)\n if (source) {\n this.openProposalForReview(source, detail.proposal)\n }\n }\n\n /**\n * A candidate from the dock opens the review lane. It does not become the draft.\n *\n * This used to write `draft` and `parts` straight onto the working draft and then set\n * `proposalSession = undefined` โ€” the dock's candidate overwrote what the author had open and\n * erased the lane built to review it. There was no moment where the person said yes, and a\n * draft has no version history to go back to, so the previous work was simply gone.\n *\n * `ai-client-base`'s analysis contract states this as a literal type: `approvalRequired: true`,\n * and of `proposal`, \"It is never an instruction to mutate canonical state.\" The in-page\n * The in-page entrance honoured this through `openAiProposal` before it was removed, and the dock\n * entrance is now the same door. Applying is what the take button does, from the lane, per change.\n *\n * ## Why the overwrite was there โ€” so it does not come back\n *\n * The dock opened the review lane originally. Before `c42a0b63df` this handler read:\n *\n * if (!this.active || (detail?.figureId ?? '') !== (this.figure?.id ?? '')) return\n * if (detail?.draftType && detail.draftType !== this.folded?.type) return\n * if (detail?.baseSource && detail.baseSource !== JSON.stringify(this.folded)) return\n * this.receive(detail.proposal)\n *\n * Three guards, each a silent `return`, and the last compared a serialized source byte for byte.\n * Any difference that changed nothing at all dropped the candidate with no message, so the dock\n * would report a candidate and the screen would show none.\n *\n * The `ox-assistant-chat` migration fixed that by replacing the path rather than the guards, and\n * the review lane went out with them โ€” `applySourceDirectly` wrote straight to the draft, and\n * cleared `proposalSession` on the way past. **Nobody decided that reviews were unwanted here.**\n * It was the cost of loosening a guard by deleting what stood around it.\n *\n * So the guards are gone and stay gone, and the lane is back. If a candidate ever seems not to\n * arrive, the thing to look for is a silent `return` on this path โ€” not the lane.\n *\n * Returns whether the lane opened, so the dock can tell the chat whether to say it staged.\n */\n private openProposalForReview(rawSource: any, proposal?: any): boolean {\n const source = typeof rawSource === 'string' ? JSON.parse(rawSource) : rawSource\n if (!source || !Array.isArray(source.parts)) return false\n\n try {\n /*\n The dock sends a FigureProposal whose metrics sit at the top level; the staged-on-navigate\n path carries the raw tool result, which nests them under score/cost. Read both rather than\n making one of the two entrances reshape its payload.\n */\n this.proposalSession = proposalSessions.openAiProposal(this.folded, {\n source: JSON.stringify(source),\n grade: proposal?.grade ?? proposal?.score?.grade,\n triangles: proposal?.triangles ?? proposal?.cost?.triangles,\n groups: proposal?.groups ?? proposal?.cost?.groups,\n attempts: proposal?.attempts,\n quality: proposal?.quality\n } as FigureProposal)\n\n this.mode = 'edit'\n this.requestUpdate()\n return true\n } catch (e) {\n console.error('[FigureModeller] Failed to open the AI candidate for review:', e)\n return false\n }\n }\n\n private proposalBaseSource?: string\n\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-ai-context-request', this.announceAiContext)\n window.addEventListener('figure-ai-review', this.receiveDockProposal)\n window.addEventListener('figure-ai-stage', this.onFigureAiStage)\n window.addEventListener('figure-save-request', this.onSaveRequest)\n window.addEventListener('keydown', this.handleKeyDown)\n }\n\n disconnectedCallback() {\n window.removeEventListener('keydown', this.handleKeyDown)\n window.removeEventListener('figure-save-request', this.onSaveRequest)\n window.removeEventListener('figure-ai-stage', this.onFigureAiStage)\n window.removeEventListener('figure-ai-context-request', this.announceAiContext)\n window.removeEventListener('figure-ai-review', this.receiveDockProposal)\n window.dispatchEvent(new CustomEvent('figure-ai-context', { detail: undefined }))\n super.disconnectedCallback()\n }\n\n private onSaveRequest = () => {\n if (this.active) this.save()\n }\n\n private handleKeyDown = (e: KeyboardEvent) => {\n if (!this.active || this.mode !== 'edit' || this.proposalSession) return\n const target = e.composedPath()[0] as HTMLElement | undefined\n const tag = target?.tagName?.toLowerCase()\n if (tag === 'input' || tag === 'textarea' || target?.isContentEditable) return\n\n if ((e.metaKey || e.ctrlKey) && !e.altKey) {\n if (e.key === 'z' && !e.shiftKey) {\n e.preventDefault()\n this.undo()\n } else if ((e.key === 'z' && e.shiftKey) || e.key === 'y') {\n e.preventDefault()\n this.redo()\n }\n }\n }\n\n get context() {\n return {\n title: this.figure?.name ?? i18next.t('figure.title.new-figure'),\n /*\n **์ œ๋ชฉ ์ค„์€ ๋ˆ๋‹ค.** ์ด๋ฆ„์€ ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค.\n\n ์ด ํ™”๋ฉด์˜ ์ฃผ์ธ๊ณต์€ ์บ”๋ฒ„์Šค๋‹ค. ์ด๋ฆ„์€ ์†์„ฑ ํŒจ๋„์—์„œ ๋ณด๊ณ  ๊ณ ์น˜๊ณ , ๋ธŒ๋ผ์šฐ์ € ํƒญ์—๋„\n ใ€Œoperato figure ยท ์ž‘์—…์žใ€๋กœ ๋‚˜์˜จ๋‹ค. ๊ทธ ์œ„์— ์ œ๋ชฉ ์ค„์„ ํ•œ ๊ฒน ๋” ์–น์œผ๋ฉด ๊ฐ™์€ ๋ง์ด ์„ธ ๋ฒˆ์ด๊ณ ,\n ๊ทธ๋งŒํผ ์บ”๋ฒ„์Šค๊ฐ€ ๋‚ฎ์•„์ง„๋‹ค.\n\n `title` ์„ ๋น„์šฐ๋Š” ๊ฒƒ์œผ๋กœ ๋„์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๊ทธ ๊ฐ’์„ MDI ํƒญ๋„ ์ฝ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ๋น„์šฐ๋ฉด ํƒญ ์ด๋ฆ„์ด\n ํ•จ๊ป˜ ์‚ฌ๋ผ์ง„๋‹ค. ์ด๋ฆ„์€ ํ•˜๋‚˜์ด๊ณ  ๊ทธ๋ฆฌ๋Š” ์ž๋ฆฌ๋งˆ๋‹ค ์ผœ๊ณ  ๋ˆ๋‹ค(`ox-page-title-bar`).\n */\n heading: false\n }\n }\n\n render() {\n const source = this.folded\n\n return html`\n <div\n edits\n ?collapse-left=${this.collapsed.left}\n ?collapse-right=${this.collapsed.right}\n @figure-rename=${(e: CustomEvent) => this.rename(e.detail.name)}\n @figure-type-change=${(e: CustomEvent) => this.typeChanged(e.detail.type)}\n @figure-catalog=${(e: CustomEvent) => this.catalog(e.detail)}\n @figure-released=${(e: CustomEvent) => this.released(e.detail.figure)}\n @figure-reverted=${() => this.figure?.id && this.load(this.figure.id)}\n @preview-sizing-fix=${(e: CustomEvent) => this.previewSizingFix(e.detail)}\n @apply-sizing-fix=${(e: CustomEvent) => this.applySizingFix(e.detail)}\n @add-part=${(e: CustomEvent) => this.addPart(e.detail.primitive)}\n @select-part=${(e: CustomEvent) => (this.selected = e.detail.index)}\n @remove-part=${(e: CustomEvent) => this.removePart(e.detail.index)}\n @duplicate-part=${(e: CustomEvent) => this.duplicatePart(e.detail.index)}\n @move-part-up=${(e: CustomEvent) => this.movePart(e.detail.index, e.detail.to)}\n @move-part-down=${(e: CustomEvent) => this.movePart(e.detail.index, e.detail.to)}\n @update-part=${(e: CustomEvent) => this.updatePart(e.detail.index, e.detail.part)}\n @part-changed=${(e: CustomEvent) => this.partChanged(e.detail.name, e.detail.part)}\n @toggle-part-hidden=${(e: CustomEvent) => this.toggleHidden(e.detail.index)}\n @set-all-parts-hidden=${(e: CustomEvent) => this.setAllHidden(e.detail.hidden)}\n @change-detail-level=${(e: CustomEvent) => this.changeDetailLevel(e.detail.level)}\n @update-draft=${(e: CustomEvent) => this.updateDraft(e.detail.draft)}\n @resize-base=${(e: CustomEvent) => this.resizeBase(e.detail.draft, e.detail.parts)}\n @part-tool=${(e: CustomEvent) => this.runTool(e.detail.index, e.detail.tool, e.detail.axis)}\n @pick-part=${(e: CustomEvent) => this.pickPart(e.detail.name)}\n >\n <figure-parts\n .parts=${this.parts}\n .level=${(this.draft?.detailLevel ?? 'M') as DetailLevel}\n .selected=${this.selected}\n ></figure-parts>\n\n <div middle>\n ${this.renderBar()}\n ${this.proposalSession ? this.renderDecision() : ''}\n ${this.proposalSession\n ? html`\n <div lanes>\n <div lane>\n <h4>${i18next.t('figure.label.now')}</h4>\n <figure-canvas\n .figureId=${this.figure?.id || this.figure?.type}\n .draft=${this.draft}\n .parts=${this.parts}\n .picked=${this.pickedName}\n .view=${this.view}\n .revision=${this.sceneRevision}\n ></figure-canvas>\n </div>\n <div lane candidate>\n <h4>\n ${i18next.t('figure.label.proposed')}\n ${this.proposalSession.metrics\n ? html`<small>\n ${this.proposalSession.metrics.grade} ยท\n ${i18next.t('figure.text.n-triangles', { count: this.proposalSession.metrics.triangles })}\n ${this.proposalSession.metrics.quality ? html` ยท ${this.proposalSession.metrics.quality.summary}` : ''}\n </small>`\n : ''}\n </h4>\n ${this.renderProposed()}\n </div>\n </div>\n `\n : this.mode === 'preview'\n ? html`<figure-preview\n .source=${source}\n .draft=${this.draft}\n .view=${this.view}\n @pose-as-default=${(e: CustomEvent) => this.poseAsDefault(e.detail.values)}\n ></figure-preview>`\n : html`<figure-canvas\n .figureId=${this.figure?.id || this.figure?.type}\n .draft=${this.draft}\n .parts=${this.parts}\n .picked=${this.pickedName}\n .gizmo=${this.jointGizmo}\n .view=${this.view}\n .revision=${this.sceneRevision}\n ></figure-canvas>`}\n </div>\n\n <figure-side\n .figureName=${this.figure?.name ?? ''}\n .figureCategory=${this.figure?.category}\n .figureTags=${this.figure?.tags}\n .figureId=${this.figure?.id ?? ''}\n .figureState=${this.figure?.state}\n .figureVersion=${this.figure?.version ?? 0}\n .dirty=${this.dirty}\n .draft=${this.draft}\n .parts=${this.parts}\n .source=${source}\n .contract=${this.contract}\n .contractFailure=${this.contractFailure}\n .selected=${this.selected}\n .view=${this.view}\n @update-view=${(e: CustomEvent) => (this.view = e.detail.view)}\n ></figure-side>\n </div>\n ${this.renderSaveModal()}\n `\n }\n\n /**\n * ๊ฐ€์šด๋ฐ ์นธ ์œ„์˜ ๋  โ€” ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋‚˜์™€, ์–‘์ชฝ ์นธ์„ ์ ‘๋Š” ์†์žก์ด.\n *\n * ## ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋‚˜\n *\n * ํŽธ์ง‘๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” ๊ฐ™์€ ์ž๋ฆฌ๋ฅผ ๋†“๊ณ  ๋‹คํˆฌ์ง€ ์•Š๋Š”๋‹ค. ๋‘˜ ๋‹ค ๋„“์ด๊ฐ€ ํ•„์š”ํ•˜๊ณ , ๋™์‹œ์—\n * ๋ด์•ผ ํ•  ์ผ์ด ์—†๋‹ค. ๋ถ€ํ’ˆ์„ ๊ณ ์น˜๋Š” ๋™์•ˆ์—๋Š” ํŽธ์ง‘๋งŒ ๋ณด๋ฉด ๋˜๊ณ , ๋‹ค ๊ณ ์นœ ๋’ค์— ๋„๋ฉด์—์„œ\n * ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ๋ณด๋ฉด ๋œ๋‹ค.\n *\n * ํ›„๋ณด๊ฐ€ ์™€ ์žˆ์„ ๋•Œ๋Š” ๊ทธ ๋‘ ๋‹จ์ถ”๋ฅผ ๋‚ด์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋•Œ๋Š” ๊ฐ€์šด๋ฐ๊ฐ€ ์ด๋ฏธ ๋‘˜๋กœ ๋‚˜๋‰˜์–ด\n * ์žˆ๋‹ค.\n *\n * ## ๋ ๋Š” ๋Š˜ ์žˆ๋‹ค\n *\n * ์†์žก์ด๊ฐ€ ์—ฌ๊ธฐ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ›„๋ณด๊ฐ€ ์™”๋‹ค๊ณ  ๋ ๋ฅผ ํ†ต์งธ๋กœ ๊ฐ์ถ”๋ฉด, ์นธ์„ ์ ‘์–ด ๋‘” ์ฑ„๋กœ\n * ํ›„๋ณด๋ฅผ ๋ฐ›์œผ๋ฉด ๋˜๋Œ๋ฆด ๋ฐฉ๋ฒ•์ด ์—†์–ด์ง„๋‹ค.\n */\n private renderBar() {\n const one = (mode: 'edit' | 'preview') => html`\n <button\n mode\n ?on=${this.mode === mode}\n title=${i18next.t('figure.text.mode-' + mode + '-explained')}\n @click=${() => (this.mode = mode)}\n >\n ${i18next.t('figure.label.mode-' + mode)}\n </button>\n `\n\n return html`<div modes>\n ${this.renderCollapse('left')}${this.proposalSession ? '' : html`${one('edit')}${one('preview')}`}\n ${this.mode === 'edit' && !this.proposalSession\n ? html`\n <div separator></div>\n <button\n history\n ?disabled=${this.undoStack.length === 0}\n title=\"${i18next.t('figure.button.undo')} (Ctrl+Z)\"\n @click=${() => this.undo()}\n >\n <md-icon>undo</md-icon>\n </button>\n <button\n history\n ?disabled=${this.redoStack.length === 0}\n title=\"${i18next.t('figure.button.redo')} (Ctrl+Y)\"\n @click=${() => this.redo()}\n >\n <md-icon>redo</md-icon>\n </button>\n `\n : ''}\n <div spacer></div>\n <!--\n AI ๋ฅผ ์—ฌ๋Š” ๋‹จ์ถ”๋Š” ์—ฌ๊ธฐ ๋‘์ง€ ์•Š๋Š”๋‹ค. ์•ฑ ๋จธ๋ฆฌ์ค„์— ์ด๋ฏธ ์žˆ๊ณ  ๊ฐ™์€ dock ์„ ์—ฐ๋‹ค โ€” ํ•œ ํ™”๋ฉด์—\n ๊ฐ™์€ ๊ณณ์œผ๋กœ ๊ฐ€๋Š” ๋ฌธ์ด ๋‘˜์ด๋ฉด ์‚ฌ๋žŒ์ด ๋‘˜์˜ ์ฐจ์ด๋ฅผ ์ฐพ๋Š”๋‹ค. ์—ด๋ฆฐ ๋„ํ˜•์„ dock ์— ๋„˜๊ธฐ๋Š” ์ผ์€\n ๋‹จ์ถ”๊ฐ€ ํ•˜๋˜ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค: dock ์ด ์—ด๋ฆด ๋•Œ figure-ai-context-request ๋กœ ๋ฌป๊ณ  ์ด ํ™”๋ฉด์ด\n ์ €์žฅ ์ „ ์ •๋ณธ ๊ทธ๋Œ€๋กœ ๋‹ตํ•œ๋‹ค.\n -->\n ${this.saveFailure ? html`<span save-failed title=${this.saveFailure}>${this.saveFailure}</span>` : ''}\n ${this.dirty ? html`<span dirty>${i18next.t('figure.text.unsaved-changes')}</span>` : ''}\n <button save ?disabled=${!this.canSave} @click=${() => this.save()}>\n ${this.saving ? i18next.t('figure.text.saving-figure') : i18next.t('figure.button.save')}\n </button>\n ${this.renderCollapse('right')}\n </div>`\n }\n\n /**\n * ํ•œ์ชฝ ์นธ์„ ์ ‘๊ณ  ํŽด๋Š” ์†์žก์ด.\n *\n * ํ™”์‚ดํ‘œ๋Š” **๋ˆ„๋ฅด๋ฉด ๋ฌด์—‡์ด ์ผ์–ด๋‚˜๋Š”์ง€**๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค โ€” ํŽด์ ธ ์žˆ์œผ๋ฉด ์นธ์ด ์‚ฌ๋ผ์งˆ ์ชฝ,\n * ์ ‘ํ˜€ ์žˆ์œผ๋ฉด ์นธ์ด ๋Œ์•„์˜ฌ ์ชฝ. ์ง€๊ธˆ ์ƒํƒœ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ํ•˜๋ฉด ๋ฐ˜๋Œ€๋กœ ์ฝํžŒ๋‹ค.\n */\n private renderCollapse(side: keyof Panes) {\n const gone = this.collapsed[side]\n const shut = side === 'left' ? 'chevron_left' : 'chevron_right'\n const back = side === 'left' ? 'chevron_right' : 'chevron_left'\n const pane = side === 'left' ? 'parts' : 'properties'\n\n return html`\n <button\n collapse\n title=${i18next.t(`figure.text.${gone ? 'expand' : 'collapse'}-${pane}-pane`)}\n @click=${() => this.toggleCollapse(side)}\n >\n <md-icon>${gone ? back : shut}</md-icon>\n </button>\n `\n }\n\n private toggleCollapse(side: keyof Panes): void {\n this.collapsed = { ...this.collapsed, [side]: !this.collapsed[side] }\n rememberPanes(this.collapsed)\n }\n\n /**\n * AI ํ›„๋ณด๋ฅผ ์‹ค์ œ ๋ฐฐ์น˜ ํฌ๊ธฐ์—์„œ๋„ ๋ณธ๋‹ค.\n *\n * ํŽธ์ง‘ ์บ”๋ฒ„์Šค ํ•œ ์žฅ์œผ๋กœ๋Š” `repeat`ยท`fixed`ยท`anchor`๊ฐ€ ๋งž๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค. ํ›„๋ณด๋Š” ์•„์ง\n * ์ •๋ณธ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์ €์žฅํ•˜์ง€ ์•Š๊ณ , `figure-preview`๊ฐ€ ๊ฐ™์€ FigureSource๋ฅผ ๊ธฐ์ค€ ํฌ๊ธฐ์™€\n * ์ผ๊ณฑ ๊ฐ€์ง€ ํ™•์žฅ ์ถ•์—์„œ ์‹ค์ œ FigureInstance๋กœ ์„ธ์šด๋‹ค. ๋”ฐ๋ผ์„œ ์ €์ž‘์ž๋Š” \"์ข‹์•„ ๋ณด์ด๋Š”\n * ํ•œ ์žฅ\"์ด ์•„๋‹ˆ๋ผ ๋ฐฐ์น˜๋  ๋•Œ๋„ ๋ถ™์–ด ์žˆ๋Š”์ง€๋ฅผ ๋ณด๊ณ  ์ˆ˜๋ฝํ•œ๋‹ค.\n */\n private renderProposed() {\n if (!this.proposalSession) return ''\n\n return html`<figure-preview .source=${this.proposalSession.source} .view=${this.view}></figure-preview>`\n }\n\n /**\n * ๋ฌด์—‡์„ ๋ฐ›์„์ง€ ๊ณ ๋ฅธ๋‹ค.\n *\n * ํ†ต์งธ๋กœ๋งŒ ๋ฐ›๊ฒŒ ๋‘๋ฉด ใ€Œ๋ชธํ†ต์€ ์ข‹์€๋ฐ ๋ชจํ„ฐ ์ž๋ฆฌ๊ฐ€ ํ‹€๋ ธ๋‹คใ€์— ๋‹ตํ•  ์ˆ˜ ์—†๋‹ค โ€” ๊ทธ๊ฒŒ\n * ๊ฐ€์žฅ ํ”ํ•œ ๋ฐ˜์‘์ด๋‹ค. ๋ณ€๊ฒฝ ํ•˜๋‚˜๊ฐ€ ์นฉ ํ•˜๋‚˜์ด๊ณ , ์ฒ˜์Œ์—๋Š” ์ „๋ถ€ ๊ณจ๋ผ ๋‘”๋‹ค. ํ†ต์งธ๋กœ\n * ๋ฐ›๋Š” ๊ฒƒ์ด ๋ณดํ†ต์˜ ๊ธธ์ด๋ฏ€๋กœ ๊ทธ๊ฒƒ์ด ํ•œ ๋ฒˆ ๋ˆ„๋ฅด๊ธฐ์—ฌ์•ผ ํ•œ๋‹ค.\n *\n * ๊ณ ๋ฅธ ์กฐํ•ฉ์„ **๋ฐ›๊ธฐ ์ „์— ์žฌ ๋ณธ๋‹ค.** ํ˜•์‹์„ ์–ด๊ธฐ๋ฉด ๋ชป ๋ˆ„๋ฅด๊ฒŒ ํ•˜๊ณ , ์ •์ฑ…์„ ๋„˜์œผ๋ฉด\n * ์•Œ๋ฆฐ๋‹ค. ๊ณจ๋ผ ๋ฐ›์œผ๋ฉด ํ›„๋ณด๋ฅผ ๋‚ผ ๋•Œ์™€ ๋‹ค๋ฅธ ์กฐํ•ฉ์ด ๋˜๋ฏ€๋กœ ํ›„๋ณด์˜ ์ ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ\n * ๋ฏฟ์„ ์ˆ˜ ์—†๋‹ค โ€” ์˜ˆ๋ฅผ ๋“ค์–ด ๋“ฑ๊ธ‰์€ ์•ˆ ๋ฐ›๊ณ  ๋ถ€ํ’ˆ๋งŒ ๋ฐ›์œผ๋ฉด ๋ถ€ํ’ˆ ์ˆ˜ ํ•œ๋„๋ฅผ ๋„˜๋Š”๋‹ค.\n *\n * ํ˜•์‹ ์˜ค๋ฅ˜๊ฐ€ ์‹ค์ œ๋กœ ๋‚˜๋Š” ์กฐํ•ฉ์€ ์•„์ง ๋ณด์ง€ ๋ชปํ–ˆ๋‹ค. ๊ทธ๋ž˜๋„ ๋‘๋Š” ๊ฒƒ์€ ๊ฐ’์ด ๊ฑฐ์˜\n * ์•ˆ ๋“ค๊ณ , ์šฐ๋ฆฌ๊ฐ€ ์ƒ๊ฐ ๋ชป ํ•œ ์กฐํ•ฉ์„ ๋ง‰์•„ ์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.\n */\n private renderDecision() {\n if (!this.proposalSession) return ''\n\n const stale = this.proposalBaseSource !== undefined && this.proposalBaseSource !== JSON.stringify(this.folded)\n\n const changes = proposals.diffProposal(this.folded, this.proposalSession.source)\n const picked = proposals.applyProposal(this.folded, this.proposalSession.source, this.proposalSession.picked)\n const { errors, violations } = validate(picked)\n\n return html`\n <div decide>\n <span>${this.proposalTitle(this.proposalSession)}</span>\n <div spacer></div>\n <button drop @click=${() => this.discard()}>${i18next.t('figure.button.discard')}</button>\n ${stale ? html`<span role=\"alert\">${i18next.t('figure.text.source-changed-ask-again')}</span>` : ''}\n <button take ?disabled=${stale || errors.length > 0 || this.proposalSession.picked.size === 0} @click=${() => this.take()}>\n ${i18next.t('figure.button.take-n-changes', { n: this.proposalSession.picked.size })}\n </button>\n </div>\n\n ${this.renderQualityReview()}\n <div proposal-note>\n <label>\n ${i18next.t('figure.text.proposal-feedback-note')}\n <input\n maxlength=\"500\"\n .value=${this.proposalSession.note}\n placeholder=${i18next.t('figure.text.proposal-feedback-placeholder')}\n @input=${(event: Event) => (this.proposalSession = { ...this.proposalSession!, note: (event.target as HTMLInputElement).value })}\n />\n </label>\n </div>\n\n ${changes.length > 0\n ? html`\n <div changes>\n ${changes.map(change => this.renderChange(change))}\n </div>\n `\n : ''}\n ${errors.length > 0\n ? html`<p blocked>${i18next.t('figure.text.picked-changes-do-not-validate')} โ€” ${errors[0].message}</p>`\n : violations.length > 0\n ? html`<p blocked warn>${violations[0].message}</p>`\n : ''}\n `\n }\n\n /** ํ›„๋ณด ํ’ˆ์งˆ ํŒ๋‹จ์˜ ๊ทผ๊ฑฐ๋ฅผ ์ˆ˜๋ฝ ์ „์— ์ „๋ถ€ ๋ณด์—ฌ ์ค€๋‹ค. */\n private renderQualityReview() {\n const quality = this.proposalSession?.metrics?.quality\n if (!quality) return ''\n\n return html`\n <div quality-review ?review=${quality.status === 'review'}>\n <strong>${i18next.t('figure.label.candidate-review')}</strong>\n <span>${quality.summary}</span>\n <span> ยท top-view ${Math.round(quality.visualReadability * 100)}% ยท ${quality.visualRegions} regions</span>\n ${quality.findings.length > 0\n ? html`<ul>\n ${quality.findings.map(finding => html`<li><code>${finding.dimension}</code> โ€” ${finding.message}</li>`)}\n </ul>`\n : ''}\n </div>\n `\n }\n\n /** ๋ณ€๊ฒฝ ํ•˜๋‚˜. ๋ฌด์—‡์„ ํ•˜๊ฒ ๋‹ค๋Š” ๊ฒƒ์ธ์ง€ ๋ถ€ํ˜ธ๋กœ ๋จผ์ € ๋งํ•œ๋‹ค. */\n private renderChange(change: proposals.ProposalChange) {\n /*\n Every ChangeKind needs a row here. A kind the map does not answer draws an empty icon and a\n key with no name beside it, which reads as a broken row rather than a missing case โ€” the\n `satisfies` makes the compiler say so instead.\n */\n const icon = ({\n added: 'add',\n changed: 'edit',\n removed: 'remove',\n base: 'aspect_ratio',\n placement: 'vertical_align_bottom',\n detail: 'tune',\n styleKit: 'palette',\n animations: 'animation',\n parameters: 'tune',\n capabilities: 'hub',\n joints: 'settings_input_component'\n } satisfies Record<proposals.ChangeKind, string>)[change.kind]\n\n const on = this.proposalSession?.picked.has(change.key)\n\n return html`\n <button\n change\n ?on=${on}\n title=${i18next.t('figure.text.change-' + change.kind)}\n @click=${() => this.toggle(change.key)}\n >\n <md-icon>${icon}</md-icon>\n <span key>${change.key}</span>\n </button>\n `\n }\n\n private toggle(key: string) {\n if (this.proposalSession) this.proposalSession = proposalSessions.toggleProposalChange(this.proposalSession, key)\n }\n\n /**\n * ํ›„๋ณด๊ฐ€ ์™”๋‹ค.\n *\n * ์›๋ณธ ๋ฐ์ดํ„ฐ์— ๋„ฃ์ง€ ์•Š๋Š”๋‹ค. ๋‚˜๋ž€ํžˆ ๋‘๊ณ  ์ €์ž‘์ž๊ฐ€ ๊ณ ๋ฅธ๋‹ค. AI ๊ฐ€ ์›๋ณธ ๋ฐ์ดํ„ฐ์— ์ง์ ‘ ์“ฐ๋ฉด\n * ๋ฌด์—‡์ด ์ž๊ธฐ ๊ฒƒ์ด๊ณ  ๋ฌด์—‡์ด ๊ธฐ๊ณ„ ๊ฒƒ์ธ์ง€ ๋ชจ๋ฅด๊ฒŒ ๋œ๋‹ค.\n */\n private receive(proposal: FigureProposal) {\n this.proposalBaseSource = JSON.stringify(this.folded)\n this.proposalSession = proposalSessions.openAiProposal(this.folded, proposal)\n }\n\n /**\n * ๋ฐœํ–‰ ๊ฒŒ์ดํŠธ์˜ ๊ตฌ์กฐํ™”๋œ ์ˆ˜์ •์•ˆ ํ•˜๋‚˜๋ฅผ ํ˜„์žฌ ์ž‘์—… ์‚ฌ๋ณธ์—๋งŒ ๊ฒน์ณ ๋ณธ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์ค€ ๊ฐ’์€\n * `part.anchor.axis = to`๋ผ๋Š” ๋ฐ์ดํ„ฐ์ด์ง€ ์‚ฌ๋žŒ์ด ์ฝ์„ ๋ฌธ์žฅ์„ ๋‹ค์‹œ ํ•ด์„ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹ˆ๋‹ค.\n * ์›๋ณธ `draft`/`parts`๋Š” ์—ฌ๊ธฐ์„œ ์ „ํ˜€ ๋ฐ”๊พธ์ง€ ์•Š๊ณ , ์•„๋ž˜์˜ `take()`๋งŒ ๋ฐ”๊พผ ์‚ฌ๋ณธ์„ ์ ์šฉํ•œ๋‹ค.\n */\n private previewSizingFix(fix: FigureGateFix) {\n if (!this.folded) return\n this.proposalBaseSource = JSON.stringify(this.folded)\n this.proposalSession = proposalSessions.openSizingFix(this.folded, fix)\n }\n\n /**\n * ์›ํด๋ฆญ ์ ์šฉ์€ ์„œ๋ฒ„๊ฐ€ ๋ชจ๋“  ํฌ๊ธฐ ๋ณ€๊ฒฝ case๋ฅผ ์žฌ๊ฒ€์‚ฌํ•ด ์•ˆ์ „ํ•˜๋‹ค๊ณ  ํ‘œ์‹œํ•œ ํ•œ-์•ต์ปค ํ›„๋ณด์—๋งŒ ์—ฐ๋‹ค.\n * ๊ทธ๋ž˜๋„ ์ €์žฅ์€ ์ €์ž‘์ž์˜ ๋ณ„๋„ ํ–‰์œ„๋‹ค. ์ž˜๋ชป ๋ˆŒ๋Ÿฌ๋„ DB ์ •๋ณธ์ด๋‚˜ ๋ฐœํ–‰ํŒ์€ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.\n */\n private applySizingFix(fix: FigureGateFix) {\n if (!fix.safe || !this.folded) return\n this.previewSizingFix(fix)\n if (!this.proposalSession) return\n this.take()\n }\n\n /**\n * ๊ณ ๋ฅธ ๋ณ€๊ฒฝ์„ ๋ฐ›์•„๋“ค์ธ๋‹ค.\n *\n * ํ›„๋ณด ๋น„๊ต๋Š” ์ €์žฅ ํ˜•์‹์œผ๋กœ ํ•œ๋‹ค โ€” ๋ถ€ํ’ˆ ์ด๋ฆ„์œผ๋กœ ์ง์ง“๊ณ  ๊ธฐ์ค€ ์น˜์ˆ˜ยท๋“ฑ๊ธ‰๊นŒ์ง€ ํ•จ๊ป˜ ๋ด์•ผ\n * ํ•˜๋ฏ€๋กœ ๊ทธ์ชฝ์ด ์ž์—ฐ์Šค๋Ÿฝ๋‹ค. ๋ฐ›์•„๋“ค์ธ ๊ฒฐ๊ณผ๋Š” ๋‹ค์‹œ ์”ฌ ๋ชจ๋ธ๋กœ ๋˜๋Œ๋ ค ํŽธ์ง‘์„ ์ด์–ด๊ฐ„๋‹ค.\n */\n private take() {\n if (!this.proposalSession) return\n if (this.proposalBaseSource !== undefined && this.proposalBaseSource !== JSON.stringify(this.folded)) return\n\n this.recordHistory()\n const taken = proposals.applyProposal(this.folded, this.proposalSession.source, this.proposalSession.picked)\n const { draft, parts } = fromFigureSource(taken)\n\n this.draft = draft\n this.parts = parts\n this.selected = parts.length ? 0 : -1\n this.dirty = true\n this.reportProposalFeedback('accepted')\n this.discard(false)\n }\n\n /**\n * ํ›„๋ณด์— ๋Œ€ํ•œ ์‚ฌ๋žŒ์˜ ๊ฒฐ์ •์„ ๋„ํฌ์—๋งŒ ์•Œ๋ฆฐ๋‹ค.\n *\n * ์ด ๊ธฐ๋ก์€ ๋ถ„์„ ์ด๋ฒคํŠธ๋‚˜ Figure ์†์„ฑ์ด ์•„๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ชจ๋ธ๋Ÿฌ ์„ธ์…˜์—์„œ ๋‹ค์Œ ์š”์ฒญ์„ ๋” ์ž˜\n * ์ดํ•ด์‹œํ‚ค๋Š” ์งง์€ ์‹ ํ˜ธ์ผ ๋ฟ์ด๋‹ค. ์›๋ณธ source๋‚˜ ์‚ฌ๋žŒ์˜ ์ž‘์—… ๋‚ด์šฉ์„ ๋‹ค์‹œ ๋ณด๋‚ด์ง€ ์•Š๊ณ , ์‚ฌ๋žŒ์ด\n * ํ›„๋ณด ๋ณ€๊ฒฝ ์ค‘ ๋ช‡ ๊ฐœ๋ฅผ ๋ฐ›์•„๋“ค์˜€๋Š”์ง€์™€ ์ด๋ฏธ ํ™”๋ฉด์— ๋ณด์˜€๋˜ ํ’ˆ์งˆ ํŒ์ •๋งŒ ๋ณด๋‚ธ๋‹ค.\n */\n private reportProposalFeedback(outcome: 'accepted' | 'discarded') {\n if (!this.proposalSession) return\n\n const feedback = proposalSessions.feedbackFromSession(this.folded, this.proposalSession, outcome)\n if (!feedback) return\n this.proposalFeedback = [...this.proposalFeedback, feedback].slice(-5)\n window.dispatchEvent(\n new CustomEvent('figure-ai-feedback', {\n detail: {\n figureId: this.figure?.id,\n ...feedback\n }\n })\n )\n }\n\n private discard(reportFeedback = true) {\n if (reportFeedback) this.reportProposalFeedback('discarded')\n this.proposalSession = undefined\n this.proposalBaseSource = undefined\n }\n\n /**\n * ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‚˜.\n *\n * ํ˜•์‹์„ ์–ด๊ธด ์›๋ณธ ๋ฐ์ดํ„ฐ๋กœ๋Š” ์ €์žฅ์„ ๋ˆ„๋ฅผ ์ˆ˜ ์—†๊ฒŒ ํ•œ๋‹ค. ๋ˆŒ๋ €๋‹ค๊ฐ€ ์„œ๋ฒ„๊ฐ€ ๋ฐ›์•„์ฃผ์ง€ ์•Š๋Š” ๊ฒƒ๋ณด๋‹ค,\n * ๋ˆ„๋ฅผ ์ˆ˜ ์—†๋Š” ์ด์œ ๊ฐ€ ํŒ๋‹จ ํŒจ๋„์— ๋–  ์žˆ๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค.\n *\n * ## ๋ฐ”๋€ ๊ฒƒ์ด ์—†์œผ๋ฉด ๋ˆ„๋ฅผ ์ˆ˜ ์—†๋‹ค\n *\n * **`dirty` ๋ฅผ ์•ˆ ๋ณด๊ณ  ์žˆ์—ˆ๋‹ค.** ์ €์žฅํ•œ ์งํ›„์—๋„ ๋‹จ์ถ”๊ฐ€ ์‚ด์•„ ์žˆ์–ด ๊ฐ™์€ ์ •๋ณธ์„ ๋ช‡ ๋ฒˆ์ด๊ณ  ๋‹ค์‹œ ์“ธ\n * ์ˆ˜ ์žˆ์—ˆ๊ณ , ์ €์žฅ๋งˆ๋‹ค ์ธ๋„ค์ผ์„ ์ƒˆ๋กœ ์ฐ์–ด 20KB ๋ฅผ ๋‹ค์‹œ ๋„ฃ๊ณ  `thumbnailUpdatedAt` ์„ ์˜ฌ๋ ธ๋‹ค โ€”\n * ๊ทธ๋ฆผ ์ฃผ์†Œ์˜ ํŒ์ด ๋ฐ”๋€Œ๋ฏ€๋กœ ๋ธŒ๋ผ์šฐ์ € ์บ์‹œ๋„ ํ•จ๊ป˜ ๋ฒ„๋ ค์ง„๋‹ค. ๋ฐ”๋€ ๊ฒƒ์ด ์—†๋Š”๋ฐ ์ผ์–ด๋‚  ์ผ์ด ์•„๋‹ˆ๋‹ค.\n *\n * ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ์ค‘์ธ ๊ฒƒ์€ `startNew` ๊ฐ€ `dirty` ๋กœ ์‹œ์ž‘ํ•˜๋ฏ€๋กœ ์ฒซ ์ €์žฅ์ด ๋ง‰ํžˆ์ง€ ์•Š๋Š”๋‹ค.\n * ์ €์žฅ์ด ๊ฑฐ์ ˆ๋˜๋ฉด `dirty` ๋ฅผ ๊ทธ๋Œ€๋กœ ๋‘๋ฏ€๋กœ ๋‹ค์‹œ ๋ˆ„๋ฅผ ์ˆ˜ ์žˆ๋‹ค.\n */\n private get canSave(): boolean {\n if (this.saving || !this.dirty || !this.folded) {\n return false\n }\n // ๊ธฐ์กด ์ €์žฅ๋œ ๋ชจ๋ธ์€ ์ด๋ฆ„์ด ์žˆ์–ด์•ผ ๋ฐ”๋กœ ์ €์žฅ ๊ฐ€๋Šฅ. ์‹ ๊ทœ ๋ชจ๋ธ์€ ์ €์žฅ ํŒ์—…์—์„œ ์ด๋ฆ„์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Œ\n if (this.figure?.id && !this.figure?.name) {\n return false\n }\n return validate(this.folded).errors.length === 0\n }\n\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n ++this.loadGeneration\n return\n }\n\n const id = lifecycle?.resourceId\n\n if (id) {\n if (changes.active || id !== this.figure?.id) {\n await this.load(id)\n }\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n /*\n โ”€โ”€ id ์—†์ด ๋“ค์–ด์˜จ ๊ฒƒ์€ ใ€Œ์ƒˆ๋กœ ๋งŒ๋“ ๋‹คใ€๋Š” ๋œป์ด๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n ์ „์—๋Š” `!this.figure` ์ผ ๋•Œ๋งŒ ์ƒˆ๋กœ ์‹œ์ž‘ํ–ˆ๋‹ค. ์…ธ์€ ํŽ˜์ด์ง€๋ฅผ ์‚ด๋ ค ๋‘๋ฏ€๋กœ(MDI ํƒญ) ์นดํƒˆ๋กœ๊ทธ์—์„œ\n ํ•˜๋‚˜ ์—ด์–ด ๋ณธ ๋’ค์—๋Š” ์ด ํŽ˜์ด์ง€๊ฐ€ ๊ทธ ๋„ํ˜•์„ ๊ณ„์† ๋“ค๊ณ  ์žˆ๊ณ , ๊ทธ๋ž˜์„œ ใ€Œ์ƒˆ figureใ€๊ฐ€ **์•„๋ฌด ์ผ๋„\n ํ•˜์ง€ ์•Š์•˜๋‹ค.**\n\n ๊ทธ๊ฒƒ๋ณด๋‹ค ๋‚˜์œ ๊ฒƒ์ด ์žˆ์—ˆ๋‹ค โ€” ํ™”๋ฉด์—๋Š” ์˜› ๋„ํ˜•์ด ๊ทธ๋Œ€๋กœ ์žˆ์œผ๋‹ˆ ์‚ฌ๋žŒ์€ ์ƒˆ ๊ฒƒ์ธ ์ค„ ์•Œ๊ณ  ๊ณ ์น˜๊ณ \n ์ €์žฅํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด **์—ด๋ ค ์žˆ๋˜ ๋„ํ˜•์— ๋ฎ์–ด์“ด๋‹ค.** ใ€Œ์•ˆ ์—ด๋ฆฐ๋‹คใ€๋กœ ๋ณด์ด๋˜ ๊ฒƒ์ด ์‹ค์€ ๊ทธ ๋ฌธ์ด์—ˆ๋‹ค.\n\n ๋“ค๊ณ  ์žˆ๋Š” ๊ฒƒ์ด **์ €์žฅ๋œ ๋„ํ˜•์ด๋ฉด**(id ๊ฐ€ ์žˆ์œผ๋ฉด) ์ƒˆ๋กœ ์‹œ์ž‘ํ•œ๋‹ค. ์•„์ง ์ €์žฅ ์•ˆ ํ•œ ์ƒˆ ๋„ํ˜•์„\n ๋“ค๊ณ  ์žˆ์œผ๋ฉด ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค โ€” ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋“ค๋ €๋‹ค๊ณ  ์ €์ž‘ ์ค‘์ธ ๊ฒƒ์„ ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n */\n if (!this.figure) {\n this.startNew()\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n if (this.figure.id) {\n this.startNew()\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n if (shouldStartNewFigure(changes, !!this.figure, this.figure?.id)) {\n this.startNew()\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n }\n\n private loadGeneration = 0\n private async load(id: string) {\n const generation = ++this.loadGeneration\n const figure = await fetchFigure(id)\n if (generation !== this.loadGeneration || !this.active) return\n\n this.proposalSession = undefined\n this.proposalBaseSource = undefined\n this.figure = figure\n\n /*\n ๊ทธ๋ฆผ์ด ์—†์œผ๋ฉด ์ด ์ž๋ฆฌ์—์„œ ์ฑ„์šด๋‹ค(์•„๋ž˜ `fillThumbnail`). ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค โ€” ํ™”๋ฉด์ด ๋จผ์ €๋‹ค.\n\n **`thumbnail` ์ด ์•„๋‹ˆ๋ผ `thumbnailUpdatedAt` ์œผ๋กœ ํŒ์ •ํ•œ๋‹ค.** ๊ทธ๋ฆผ ์ž์ฒด๋Š” ๋ชฉ๋กยท๋‚ฑ๊ฐœ ์งˆ์˜๊ฐ€\n ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋Š”๋‹ค(base64 ๋ผ์„œ ๋ฌด๊ฒ๊ณ  ์บ์‹œ๊ฐ€ ์•ˆ ๋œ๋‹ค โ€” ์ฃผ์†Œ๋กœ ๋ถ€๋ฅธ๋‹ค). ์—†๋Š” ์นธ์œผ๋กœ ํŒ์ •ํ•˜๋ฉด\n ๋Š˜ ใ€Œ๊ทธ๋ฆผ์ด ์—†๋‹คใ€๊ฐ€ ๋˜์–ด ์ €์ž‘๋ฉด์„ ์—ด ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ์ฐ์–ด ์ €์žฅํ•˜๊ฒŒ ๋œ๋‹ค.\n */\n if (!figure.thumbnailUpdatedAt && figure.id) {\n void this.fillThumbnail(figure.id)\n }\n\n // ์„œ๋ฒ„๊ฐ€ ์ค€ ๊ฒƒ์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค. ์ฝ์ง€ ๋ชปํ•˜๋ฉด ์˜ค๋ฅ˜๋ณด๊ณ ์—†์ด ๋นˆ ์ƒํƒœ๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.\n // ๊ทธ๋Ÿฌ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅ์„ ์„ ํƒํ•ด ์›๋ณธ์„ ๋ฎ์–ด์“ด๋‹ค.\n const model = figure.source ? fromFigureSource(JSON.parse(figure.source) as FigureSource) : undefined\n this.draft = model?.draft\n this.parts = model?.parts ?? []\n\n /*\n ๋ฐœํ–‰๋œ ์ ์ด ์žˆ์œผ๋ฉด ๊ทธ ํŒ์„ ๊ฐ€์ ธ๋‹ค ๋‘”๋‹ค โ€” ๊ณ ์น˜๋Š” ๋™์•ˆ ๊ฒฌ์ค„ ์ƒ๋Œ€๋‹ค. ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค:\n ๋„ํ˜•์€ ์ด๋ฏธ ํ™”๋ฉด์— ์žˆ๊ณ , ํŒ์ •์€ ๋„์ฐฉํ•˜๋Š” ๋Œ€๋กœ ๋ถ™๋Š”๋‹ค.\n */\n this.releasedSource = undefined\n if (figure.id && (figure.version ?? 0) > 0) {\n void this.loadReleased(figure.id, generation)\n }\n\n /*\n **์•„๋ฌด๊ฒƒ๋„ ๊ณ ๋ฅด์ง€ ์•Š์€ ์ฑ„๋กœ ์—ฐ๋‹ค.**\n\n ์ „์—๋Š” ์ฒซ ๋ถ€ํ’ˆ์„ ์žก์•„ ๋‘์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๋ฅธ ์  ์—†๋Š” ๋ถ€ํ’ˆ์˜ ์†์„ฑ์ด ๋–  ์žˆ๊ณ ,\n ์บ”๋ฒ„์Šค์—๋Š” ๊ทธ ๋ถ€ํ’ˆ์˜ ์œค๊ณฝ์„ ์ด ๊ทธ๋ ค์ง„๋‹ค. ๋ชจ๋ธ ์ „์ฒด๋ฅผ ๋ณด๋ ค๋ฉด ๋จผ์ € ์„ ํƒ์„ ํ’€์–ด์•ผ ํ–ˆ๋‹ค.\n\n ์†์„ฑ ํŒจ๋„์˜ ๋นˆ ์ƒํƒœ๋„ ๊ทธ๋ž˜์„œ **์—ด์—ˆ์„ ๋•Œ ํ•œ ๋ฒˆ๋„ ์•ˆ ๋ณด์˜€๋‹ค.** ์ฒ˜์Œ ์—ฌ๋Š” ์‚ฌ๋žŒ์—๊ฒŒ\n ใ€Œ์™ผ์ชฝ์—์„œ ๋ถ€ํ’ˆ์„ ๊ณ ๋ฅด๋Š” ๋„๊ตฌใ€๋ผ๊ณ  ์•Œ๋ ค ์ค„ ์ž๋ฆฌ๊ฐ€ ๊ทธ ํ™”๋ฉด์ด๋‹ค.\n\n ์ƒˆ๋กœ ๋งŒ๋“ค ๋•Œ(`startNew`)๋Š” ๋‹ค๋ฅด๋‹ค. ๋ฐฉ๊ธˆ ๋ถ€ํ’ˆ์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ๋ฐ”๋กœ ํŽธ์ง‘ํ•˜๋ ค๋Š” ๊ฒƒ์ด ๋งž๋‹ค.\n */\n this.selected = -1\n this.dirty = false\n this.violations = []\n this.undoStack = []\n this.redoStack = []\n this.lastCoalesceKey = undefined\n }\n\n /** ์ง์ „ ๋ฐœํ–‰ํŒ์„ ๊ฐ€์ ธ๋‹ค ๋‘”๋‹ค. ๋ชป ์ฝ์œผ๋ฉด ๊ฒฌ์ฃผ์ง€ ์•Š๋Š”๋‹ค โ€” ์—†๋Š” ํŒ์ •์„ ์ง€์–ด๋‚ด์ง€ ์•Š๋Š”๋‹ค. */\n private async loadReleased(id: string, generation: number) {\n try {\n const source = await fetchReleasedSource(id)\n if (generation !== this.loadGeneration || !this.active) return\n this.releasedSource = source ? (JSON.parse(source) as FigureSource) : undefined\n } catch (e) {\n console.warn(`[figure] ์ง์ „ ๋ฐœํ–‰ํŒ์„ ์ฝ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ${(e as Error).message}`)\n }\n }\n\n private startNew() {\n ++this.loadGeneration\n this.proposalSession = undefined\n this.proposalBaseSource = undefined\n this.proposalFeedback = []\n this.mode = 'edit'\n // ํƒ€์ž… ์ด๋ฆ„์„ ์‚ฌ๋žŒ์ด ์ •ํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ž„์‹œ ์ด๋ฆ„์„ ์“ด๋‹ค. ์ €์žฅํ•  ๋•Œ ํ™•์ •ํ•œ๋‹ค.\n const draftType = placeholderType()\n const { draft, parts } = startingModel(draftType)\n\n this.figure = { id: '', type: draftType, name: '' } as Figure\n this.draft = draft\n this.parts = parts\n /* ๊ณ ๋ฅธ ๊ฒƒ์ด ์—†๋‹ค โ€” ๋ถ€ํ’ˆ์ด ์—†์œผ๋‹ˆ ์†์„ฑ ํŒจ๋„๋„ ๊ณ ๋ฅผ ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. */\n this.selected = -1\n this.dirty = true\n this.violations = []\n this.undoStack = []\n this.redoStack = []\n this.lastCoalesceKey = undefined\n }\n\n private rename(name: string) {\n if (!this.figure) return\n\n this.figure = { ...this.figure, name }\n this.dirty = true\n }\n\n private typeChanged(type: string) {\n if (!type || this.figure?.id) return\n const sanitized = type.replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n if (this.draft) this.draft.figureType = sanitized\n this.figure = { ...this.figure, type: sanitized } as Figure\n this.folded = this.draft ? toFigureSource(this.draft, this.parts) : undefined\n this.dirty = true\n }\n\n /**\n * ๋ถ„๋ฅ˜์™€ ํƒœ๊ทธ๋ฅผ ๊ณ ์นœ๋‹ค.\n *\n * ์ •๋ณธ(`source`)์ด ์•„๋‹ˆ๋ผ **์นดํƒˆ๋กœ๊ทธ์˜ ๊ฐ’**์ด๋‹ค โ€” ๋„ํ˜•์ด ๋ฌด์—‡์œผ๋กœ ์ƒ๊ฒผ๋Š”์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ์–ด๋””์—\n * ๊ฑธ๋ฆฌ๋Š”์ง€๋‹ค. ๊ทธ๋ž˜์„œ ์ ์ˆ˜๋„ ๋ฐœํ–‰๋„ ํ”๋“ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n */\n private catalog(patch: { category?: string; tags?: string[] }) {\n if (!this.figure) return\n\n this.figure = { ...this.figure, ...patch }\n this.dirty = true\n }\n\n private snapshotState(): { draft?: FigureDraft; parts: PartModel[]; selected: number } {\n return {\n draft: this.draft ? structuredClone(this.draft) : undefined,\n parts: structuredClone(this.parts),\n selected: this.selected\n }\n }\n\n private recordHistory(coalesceKey?: string) {\n const now = Date.now()\n if (coalesceKey && this.lastCoalesceKey === coalesceKey && now - this.lastHistoryTime < 600) {\n this.lastHistoryTime = now\n return\n }\n\n this.lastCoalesceKey = coalesceKey\n this.lastHistoryTime = now\n\n this.undoStack = [...this.undoStack.slice(-49), this.snapshotState()]\n this.redoStack = []\n }\n\n private undo() {\n if (this.undoStack.length === 0) return\n const prev = this.undoStack[this.undoStack.length - 1]\n this.undoStack = this.undoStack.slice(0, -1)\n this.redoStack = [this.snapshotState(), ...this.redoStack.slice(0, 49)]\n\n this.draft = prev.draft ? structuredClone(prev.draft) : undefined\n this.parts = structuredClone(prev.parts)\n this.selected = this.parts.length ? Math.max(-1, Math.min(prev.selected, this.parts.length - 1)) : -1\n this.dirty = true\n this.lastCoalesceKey = undefined\n }\n\n private redo() {\n if (this.redoStack.length === 0) return\n const next = this.redoStack[0]\n this.redoStack = this.redoStack.slice(1)\n this.undoStack = [...this.undoStack.slice(-49), this.snapshotState()]\n\n this.draft = next.draft ? structuredClone(next.draft) : undefined\n this.parts = structuredClone(next.parts)\n this.selected = this.parts.length ? Math.max(-1, Math.min(next.selected, this.parts.length - 1)) : -1\n this.dirty = true\n this.lastCoalesceKey = undefined\n }\n\n /**\n * ๋ถ€ํ’ˆ ๋ชฉ๋ก์„ ๊ฐˆ์•„ ๋ผ์šด๋‹ค.\n *\n * ํŽธ์ง‘ ๊ทœ์น™์€ `part-edits` ๊ฐ€ ๊ฐ–๋Š”๋‹ค โ€” ํ™”๋ฉด๊ณผ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ™์€ ๊ฒƒ์„ ์“ฐ๊ฒŒ ํ•˜๋ ค๊ณ  ๋–ผ์–ด\n * ๋†“์•˜๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋‹ด๊ณ  ์„ ํƒ ์ธ๋ฑ์Šค๋ฅผ ์ •๋ฆฌํ•œ๋‹ค.\n */\n private put(parts: PartModel[], selected = this.selected, coalesceKey?: string) {\n this.recordHistory(coalesceKey)\n this.parts = parts\n this.selected = Math.min(selected, parts.length - 1)\n this.dirty = true\n }\n\n private addPart(primitive: PrimitiveKind) {\n if (!this.draft) return\n // ์ถ”๊ฐ€ํ•œ ๊ฒƒ์„ ๋ฐ”๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ ํƒ ์ƒํƒœ๋กœ ๋‘”๋‹ค.\n this.put(edits.addPart(this.draft, this.parts, primitive), this.parts.length)\n }\n\n private removePart(index: number) {\n this.put(edits.removePart(this.parts, index), Math.max(0, index - 1))\n }\n\n private duplicatePart(index: number) {\n this.put(edits.duplicatePart(this.parts, index), index + 1)\n }\n\n private movePart(index: number, to: number) {\n this.put(edits.movePart(this.parts, index, to), to)\n }\n\n private updatePart(index: number, part: PartModel) {\n this.put(edits.updatePart(this.parts, index, part), this.selected, `part:${part.name || index}`)\n }\n\n /**\n * ํŒ ์ž์ฒด์˜ ๊ฐ’์„ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ๋Šฅ๋ ฅ์ฒ˜๋Ÿผ ๋ถ€ํ’ˆ๋ณด๋‹ค ์œ„์— ์žˆ๋Š” ๊ฒƒ๋“ค.\n *\n * ๋ถ€ํ’ˆ ๋ชฉ๋ก์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. `put` ์€ ๋ถ€ํ’ˆ์„ ํ•จ๊ป˜ ๋ฐ›์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์“ฐ๋ฉด ๋Šฅ๋ ฅ ํ•˜๋‚˜๋ฅผ\n * ๊ณ ์น  ๋•Œ๋งˆ๋‹ค ๋ถ€ํ’ˆ ๋ชฉ๋ก์ด ์ƒˆ ๋ฐฐ์—ด์ด ๋˜๊ณ , ๊ทธ๋ฆฌ๋Š” ์ชฝ์ด ์ „๋ถ€ ๋‹ค์‹œ ๊ทธ๋ฆฐ๋‹ค.\n */\n /**\n * ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ์„ธ์šด ์ž์„ธ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์•‰ํžŒ๋‹ค.\n *\n * ๊ด€์ ˆ์ด ์žˆ๋Š” ๋„ํ˜•์€ ๊ฐ’์ด 0 ์ธ ์ž์„ธ(ํœด์ง€ ์ž์„ธ)๊ฐ€ ๊ณง๊ฒŒ ์„  ๊ธฐ๋‘ฅ์ด๋ผ, ๊ธฐ๋ณธ๊ฐ’์ด ์—†์œผ๋ฉด ๋ชฉ๋ก๊ณผ\n * ๋„๋ฉด์—์„œ ๊ทธ ๋ชจ์–‘์œผ๋กœ ์„ ๋‹ค. ์ €์ž‘์ž๊ฐ€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ๋งž์ถ˜ ์ž์„ธ๊ฐ€ ๊ณง ๊ทธ ๋‹ต์ด๋‹ค.\n */\n private poseAsDefault(values: Record<string, number>) {\n if (!this.draft) return\n\n const next = poseAsDefaults(this.draft, values)\n /* ์•‰ํž ๊ฒƒ์ด ์—†์œผ๋ฉด ์ดˆ์•ˆ์ด ๊ทธ๋Œ€๋กœ ์˜จ๋‹ค. ๊ทธ๋•Œ ๋˜๋Œ๋ฆฌ๊ธฐ ์นธ๊ณผ ใ€Œ์ €์žฅ ์•ˆ ๋จใ€์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. */\n if (next !== this.draft) this.updateDraft(next)\n }\n\n /**\n * ๊ธฐ์ค€ ์ƒ์ž๋ฅผ ๊ณ ์นœ๋‹ค โ€” ์ดˆ์•ˆ๊ณผ ๋ถ€ํ’ˆ์ด **ํ•œ ๊ฑธ์Œ์œผ๋กœ** ๋ฐ”๋€๋‹ค.\n *\n * ์ƒ์ž๊ฐ€ ์ปค์ง€๋ฉด ํ•œ๊ฐ€์šด๋ฐ๊ฐ€ ์˜ฎ๊ฒจ ๊ฐ€๋ฏ€๋กœ ๋ถ€ํ’ˆ์„ ๊ทธ๋งŒํผ ๋˜๋ฐ€์–ด์•ผ ์ €์žฅ๋˜๋Š” ์ž๋ฆฌ๊ฐ€ ์œ ์ง€๋œ๋‹ค\n * (`base-box.ts`). ๊ทธ ๋‘˜์„ ๋”ฐ๋กœ ๊ธฐ๋กํ•˜๋ฉด ํ•œ ๋ฒˆ ๋˜๋Œ๋ ธ์„ ๋•Œ ๋ถ€ํ’ˆ์ด ๋ฐ˜์ฏค ๋ฐ€๋ฆฐ ํ™”๋ฉด์ด ๋‚จ๋Š”๋‹ค.\n */\n private resizeBase(draft: FigureDraft, parts: PartModel[]) {\n this.recordHistory('base')\n this.draft = draft\n this.parts = parts\n this.dirty = true\n }\n\n private updateDraft(draft: FigureDraft) {\n this.recordHistory('draft')\n this.draft = draft\n this.dirty = true\n }\n\n /**\n * ๋””ํ…Œ์ผ ๋“ฑ๊ธ‰์„ ๋ฐ”๊พผ๋‹ค.\n *\n * ์ด ๊ฐ’์€ ๋ถ€ํ’ˆ ์ˆ˜ ํ•œ๋„๋ฅผ ์ •ํ•˜๊ณ  ์„ฑ์ˆ™๋„์˜ ํ•œ ์ถ•์ด ๋œ๋‹ค. ๋ถ€ํ’ˆ๋ณด๋‹ค ์œ„์— ์žˆ๋Š” ๊ฐ’์ด๋ผ\n * ์ดˆ์•ˆ์— ๋‹ด๋Š”๋‹ค.\n *\n * ํ•œ๋„๋ฅผ ๋‚ด๋ ค์„œ ์ด๋ฏธ ๋„ฃ์€ ๋ถ€ํ’ˆ ์ˆ˜๋ณด๋‹ค ์ž‘์•„์งˆ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋•Œ ๋ถ€ํ’ˆ์„ ์ง€์šฐ์ง€ ์•Š๋Š”๋‹ค.\n * ๊ฒ€์ฆ์ด ์œ„๋ฐ˜์œผ๋กœ ์•Œ๋ฆฌ๊ณ , ์„ฑ์ˆ™๋„๊ฐ€ ๋“ฑ๊ธ‰์— ์•ˆ ๋งž๋Š”๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์ €์ž‘์ž๊ฐ€ ๋ฌด์—‡์„ ๋บ„์ง€\n * ์ •ํ•  ์ผ์ด์ง€ ๋„๊ตฌ๊ฐ€ ์ •ํ•  ์ผ์ด ์•„๋‹ˆ๋‹ค.\n */\n private changeDetailLevel(level: DetailLevel) {\n if (!this.draft) return\n\n this.recordHistory('detailLevel')\n this.draft = { ...this.draft, detailLevel: level }\n this.dirty = true\n }\n\n /**\n * ๋ถ€ํ’ˆ์„ ์ž ๊น ๊ฐ€๋ฆฐ๋‹ค.\n *\n * ์”ฌ์ด ์ด๋ฏธ ๊ฐ–๊ณ  ์žˆ๋Š” ์ƒํƒœ๋ฅผ ์ผœ๊ณ  ๋„๋Š” ๊ฒƒ๋ฟ์ด๋‹ค โ€” ํŽธ์ง‘ ๋ชจ๋“œ์—์„œ๋Š” faint ๋กœ ๋ณด์ด๊ณ \n * ๋ทฐ ๋ชจ๋“œ์—์„œ๋Š” ์•ˆ ๊ทธ๋ ค์ง„๋‹ค. ์ €์žฅ ํ˜•์‹์—๋Š” ์‹ค๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์‹œ ์—ด๋ฉด ์ „๋ถ€ ๋ณด์ธ๋‹ค.\n *\n * `dirty` ๋กœ ์น˜์ง€ ์•Š๋Š”๋‹ค. ์ €์žฅ๋  ๊ฒƒ์ด ํ•˜๋‚˜๋„ ๋ฐ”๋€Œ์ง€ ์•Š์•˜๋Š”๋ฐ \"์ €์žฅํ•˜์ง€ ์•Š์€ ๋ณ€๊ฒฝ\"์ด\n * ๋œจ๋ฉด, ์ €์žฅ ๋ฒ„ํŠผ์ด ๋ฌด์—‡์„ ์ €์žฅํ•˜๊ฒ ๋‹ค๋Š” ๊ฒƒ์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.\n */\n private toggleHidden(index: number) {\n const part = this.parts[index]\n if (!part) return\n\n this.parts = edits.updatePart(this.parts, index, { ...part, hidden: !part.hidden })\n }\n\n /**\n * ํ•œ๊บผ๋ฒˆ์— ๊ฐ์ถ”๊ฑฐ๋‚˜ ๋˜๋Œ๋ฆฐ๋‹ค.\n *\n * ๋ถ€ํ’ˆ ์Šค๋ฌผ์งœ๋ฆฌ์—์„œ ์•ˆ์ชฝ ํ•˜๋‚˜๋ฅผ ๋ณด๋ ค๊ณ  ์—ด์•„ํ™‰์„ ๋ˆ„๋ฅด๋˜ ๊ฒƒ์„ ์—†์•ค๋‹ค. ํ•œ ๋ฒˆ์— ๋ฐ”๊พธ๋ฏ€๋กœ\n * ๋ชฉ๋ก์„ ํ•œ ๋ฒˆ๋งŒ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ํ•˜๋‚˜์”ฉ ์Šค๋ฌด ๋ฒˆ ๊ฐˆ๋ฉด ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ์Šค๋ฌด ๋ฒˆ ๋‹ค์‹œ ์„ ๋‹ค.\n */\n private setAllHidden(hidden: boolean) {\n this.parts = this.parts.map(part => (part.hidden === hidden ? part : { ...part, hidden }))\n }\n\n /**\n * ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ๊ธฐ์ฆˆ๋ชจ๋กœ ์˜ฎ๊ธฐ๊ฑฐ๋‚˜ 2D ์—์„œ ๋Œ์—ˆ๋‹ค.\n *\n * ์•Œ๋ ค ์˜ค๋Š” ๊ฒƒ์€ ๋ถ€ํ’ˆ **์ด๋ฆ„**์ด๋‹ค. ๋ชฉ๋ก์˜ ๋ช‡ ๋ฒˆ์งธ์ธ์ง€๋Š” ์—ฌ๊ธฐ์„œ ์ฐพ๋Š”๋‹ค โ€” ์ด๋ฒคํŠธ๊ฐ€\n * ์˜ค๋Š” ์‚ฌ์ด์— ๋ชฉ๋ก์ด ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด ์ฐพ์ง€ ๋ชปํ•˜๊ณ , ๊ทธ๋•Œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n */\n private partChanged(name: string, part: PartModel) {\n const at = this.parts.findIndex(item => item.name === name)\n if (at >= 0) this.put(edits.updatePart(this.parts, at, part), this.selected, `drag:${name}`)\n }\n\n /**\n * ์ •๋ฐ€ ๋ฐฐ์น˜ ๋„๊ตฌ.\n *\n * ๊ทœ์น™์€ `part-edits` ๊ฐ€ ๊ฐ–๋Š”๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์–ด๋А ๊ฒƒ์„ ๋ถ€๋ฅผ์ง€๋งŒ ์ •ํ•œ๋‹ค.\n */\n private runTool(index: number, tool: string, axis: PlaneAxis) {\n const draft = this.draft\n if (!draft) return\n\n if (tool === 'center') this.put(edits.centerPart(draft, this.parts, index, axis))\n else if (tool === 'floor') this.put(edits.alignPartToStart(this.parts, index, axis))\n else if (tool === 'snap') this.put(edits.snapPart(draft, this.parts, index))\n // ๋Œ€์นญ ๋ณต์ œ๋Š” ๋ถ€ํ’ˆ์ด ํ•˜๋‚˜ ๋Š˜์–ด๋‚œ๋‹ค โ€” ์ƒˆ๋กœ ์ƒ๊ธด ๊ฒƒ์„ ์„ ํƒ ์ƒํƒœ๋กœ ๋‘”๋‹ค\n else if (tool === 'mirror') this.put(edits.mirrorPart(draft, this.parts, index, axis), index + 1)\n }\n\n /**\n * ์บ”๋ฒ„์Šค์—์„œ ๋ถ€ํ’ˆ์„ ์„ ํƒํ–ˆ๋‹ค.\n *\n * ์บ”๋ฒ„์Šค๊ฐ€ ์•Œ๋ฆฌ๋Š” ๊ฒƒ์€ **์ด๋ฆ„**์ด๋‹ค. ๋ชฉ๋ก์˜ ๋ช‡ ๋ฒˆ์งธ์ธ์ง€๋Š” ์—ฌ๊ธฐ์„œ ์ฐพ๋Š”๋‹ค โ€” ์•Œ๋ฆผ์ด\n * ์˜ค๋Š” ์‚ฌ์ด์— ๋ชฉ๋ก์ด ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด ๋ชป ์ฐพ๊ณ , ๊ทธ๋•Œ๋Š” ๊ณ ๋ฅธ ๊ฒƒ์„ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.\n * ์—†๋Š” ์ž๋ฆฌ๋ฅผ ๊ณ ๋ฅด๋ฉด ์†์„ฑ ์นธ์ด ๋นˆ ์ฑ„๋กœ ์—ด๋ ค ใ€Œ์‚ฌ๋ผ์กŒ๋‚˜ใ€๋กœ ์ฝํžŒ๋‹ค.\n */\n /** ์ง€๊ธˆ ๊ณ ๋ฅธ ๋ถ€ํ’ˆ ์ด๋ฆ„. ์บ”๋ฒ„์Šค๊ฐ€ ์ธ๋ฑ์Šค๊ฐ€ ์•„๋‹ˆ๋ผ ์ด๋ฆ„์„ ๋ฐ›๋Š”๋‹ค. */\n private get pickedName(): string | undefined {\n return this.parts[this.selected]?.name\n }\n\n /**\n * ๊ณ ๋ฅธ ๋ถ€ํ’ˆ์— ๊ด€์ ˆ์ด ์žˆ์œผ๋ฉด **๊ทธ ๊ด€์ ˆ์ด ๋„๋Š” ์ž๋ฆฌ**๋ฅผ ์ €์ž‘๋ฉด์— ํ•จ๊ป˜ ๊ทธ๋ฆฐ๋‹ค(`joint-gizmo.ts`).\n *\n * ์ถ•์„ ๊ณ ๋ฅด๋Š” ์นธ์€ ์ด ํ™”๋ฉด์— ์žˆ๋Š”๋ฐ ๋„๋Š” ๋ชจ์Šต์€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—๋งŒ ์žˆ์—ˆ๋‹ค. ๊ณ ๋ฅด๋Š” ์ž๋ฆฌ์™€ ๋ณด์ด๋Š”\n * ์ž๋ฆฌ๊ฐ€ ๊ฐˆ๋ฆฌ๋ฉด ์‚ฌ๋žŒ์€ ์™•๋ณตํ•˜๋ฉฐ ๋งž์ถ˜๋‹ค โ€” ์—ฌ์„ฏ ์ถ•์งœ๋ฆฌ ๋กœ๋ด‡์ด๋ฉด ์—ฌ์„ฏ ๋ฒˆ์ด๋‹ค.\n */\n private get jointGizmo(): PartModel | undefined {\n const name = this.pickedName\n if (!name || !this.draft) return undefined\n\n const joint = jointOf(this.draft, name)\n if (!joint) return undefined\n\n return gizmoPart(joint, this.parts.find(part => part.name === name), this.draft)\n }\n\n private pickPart(name: string) {\n const at = this.parts.findIndex(part => part.name === name)\n if (at >= 0) this.selected = at\n }\n\n /**\n * ๊ทธ๋ฆผ์ด ์—†๋Š” ๋„ํ˜•์„ **์—ด์—ˆ์„ ๋•Œ ํ•œ ๋ฒˆ** ์ฐ์–ด ์ฑ„์šด๋‹ค.\n *\n * ## ์™œ ์—ฌ๊ธฐ์ธ๊ฐ€\n *\n * ๋„๋ฉด์€ ์ €์žฅํ•  ๋•Œ ์„œ๋ฒ„๊ฐ€ ์ฐ๋Š”๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐฑ์—”๋“œ์— GPU ๊ฐ€ ์—†๋Š” ๋ฐฐํฌ๊ฐ€ ๋งŽ์•„ ํ—ค๋“œ๋ฆฌ์Šค๋กœ 3D ๋ฅผ ์ฐ๋Š”\n * ๊ฒƒ์ด ์ž์ฃผ ์‹คํŒจํ•œ๋‹ค. ๋„ํ˜•์„ ๊ทธ๋ฆฌ๋Š” GPU ๋Š” ์ด๋ฏธ ๋ณด๋Š” ์‚ฌ๋žŒ ์†์— ์žˆ๊ณ , ์”ฌ์€ ์ง€๊ธˆ ์—ฌ๊ธฐ ์„œ ์žˆ๋‹ค.\n *\n * ์ €์ž‘๋ฉด์„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ์ƒ๊ธด ๊ฒƒ๋“ค์ด ๊ทธ๋ฆผ์ด ์—†๋‹ค โ€” ์”จ์•—์œผ๋กœ ๋„ฃ์€ ํ‘œ๋ณธ, AI ๊ฐ€ ๋‚ธ ํ›„๋ณด, ๋ฐ˜์ž…ํ•œ ์ž์‚ฐ.\n * ๊ทธ๊ฒƒ๋“ค์„ ํ•œ ๋ฒˆ ์—ด์–ด ๋ณด๋ฉด ๊ทธ๋•Œ ์ฑ„์›Œ์ง„๋‹ค.\n *\n * ## ์ €์žฅ์ด ์•„๋‹ˆ๋‹ค\n *\n * ๊ทธ๋ฆผ๋งŒ ๋ณด๋‚ธ๋‹ค. ์ •๋ณธ์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฏธ์ €์žฅ ํ‘œ์‹œ๋„ ์ผœ์ง€ ์•Š๋Š”๋‹ค โ€” ์—ด์–ด ๋ดค๋‹ค๋Š” ๊ฒƒ์ด ๊ณ ์ณค๋‹ค๋Š”\n * ๋œป์ด ๋˜๋ฉด ์ €์ž‘์ž๊ฐ€ ์ €์žฅํ• ์ง€ ๋ง์ง€๋ฅผ ๋งค๋ฒˆ ๊ณ ๋ฏผํ•œ๋‹ค.\n */\n /**\n * ๋ฐœํ–‰๋˜์—ˆ๋‹ค โ€” **์ •๋ณธ์„ ๋“  ์ž๋ฆฌ๊ฐ€ ๊ทธ ์‚ฌ์‹ค์„ ๋ฐ›๋Š”๋‹ค.**\n *\n * ์ด๋ ฅ ํŒจ๋„์€ ๋ฐœํ–‰์„ ์‹œํ‚ค๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์•Œ๋ฆด ๋ฟ, ์ด ํŽ˜์ด์ง€์˜ ์ƒํƒœ๋ฅผ ๊ณ ์ณ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. ํŒ ๋ฒˆํ˜ธ์™€ ์ƒํƒœ๊ฐ€\n * ์—ฌ๊ธฐ์„œ ๊ฐฑ์‹ ๋˜์–ด์•ผ ๋จธ๋ฆฌ์ค„ยท์ด๋ ฅ ํƒญ์ด ๊ฐ™์€ ๊ฒƒ์„ ๋ณธ๋‹ค.\n *\n * ์ •๋ณธ(source)์€ ๋ฐœํ–‰์ด ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š๋Š”๋‹ค โ€” ์ €์ž‘ ์ค‘์ธ ํ™”๋ฉด์„ ๋˜๊ฐ์ง€ ์•Š๋Š”๋‹ค.\n */\n private released(figure: Figure): void {\n if (!this.figure || figure?.id !== this.figure.id) {\n return\n }\n\n this.figure = { ...this.figure, version: figure.version, state: figure.state, updatedAt: figure.updatedAt }\n }\n\n private async fillThumbnail(id: string): Promise<void> {\n await this.updateComplete\n\n const thumbnail = await captureThumbnail(this.draft, this.parts)\n if (!thumbnail || this.figure?.id !== id) {\n return\n }\n\n try {\n const { figure } = await updateFigure(id, { thumbnail })\n /* ์„œ๋ฒ„๊ฐ€ ์ฐ์€ ์‹œ๊ฐ์„ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค โ€” ์ด ๊ฐ’์ด ๊ทธ๋ฆผ์„ ๋ถ€๋ฅด๋Š” ์ฃผ์†Œ์˜ ํŒ ๋ฒˆํ˜ธ๊ฐ€ ๋œ๋‹ค. */\n this.figure = { ...this.figure, thumbnailUpdatedAt: figure?.thumbnailUpdatedAt }\n } catch (e) {\n /* ๋ณผ ๊ถŒํ•œ๋งŒ ์žˆ๋Š” ์‚ฌ๋žŒ์ผ ์ˆ˜ ์žˆ๋‹ค. ํ™”๋ฉด์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์กฐ์šฉํžˆ ๋„˜์–ด๊ฐ„๋‹ค. */\n console.warn(`[thumbnail] ${id} ๋ฅผ ์ฑ„์šฐ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ${(e as Error).message}`)\n }\n }\n\n private save() {\n if (!this.canSave || !this.folded || !this.figure) {\n return\n }\n\n if (!this.figure.id) {\n // ์‹ ๊ทœ ์ €์žฅ: ํƒ€์ž… ์ฝ”๋“œ์™€ ํ‘œ์‹œ ์ด๋ฆ„์„ ๋ช…ํ™•ํžˆ ํŒ์—…์œผ๋กœ ํ™•์ธ/์ž…๋ ฅ๋ฐ›์Œ\n let defaultType = this.draft?.figureType || this.figure.type || ''\n /*\n A placeholder is not prefilled. This used to prefill 'FIGURE', inviting the author to confirm a\n meaningless name that cannot change later and that the release gate refuses.\n */\n if (!defaultType || isPlaceholderType(defaultType)) {\n defaultType = ''\n }\n this.saveModalType = defaultType\n this.saveModalName = this.figure.name || ''\n this.saveModalError = ''\n this.showSaveModal = true\n return\n }\n\n void this.doSave()\n }\n\n private async doSave() {\n if (!this.folded || !this.figure) return\n\n this.saving = true\n this.saveFailure = ''\n this.saveModalError = ''\n\n try {\n if (!this.figure.id) {\n const typeToSave = (this.saveModalType || this.draft?.figureType || this.figure.type || 'FIGURE').trim().toUpperCase()\n const nameToSave = (this.saveModalName || this.figure.name || typeToSave).trim()\n\n if (!typeToSave) {\n throw new Error(i18next.t('figure.text.enter-a-type-name'))\n }\n\n const taken = await fetchFigureTypeNames()\n if (taken.includes(typeToSave)) {\n throw new Error(i18next.t('figure.text.type-name-already-taken', { type: typeToSave }))\n }\n\n if (this.draft) this.draft.figureType = typeToSave\n this.figure = { ...this.figure, type: typeToSave, name: nameToSave }\n this.folded = { ...this.folded, type: typeToSave }\n const sourceJson = JSON.stringify(this.folded)\n const thumbnail = await captureThumbnail(this.draft, this.parts)\n\n const result = await createFigure({\n type: typeToSave,\n name: nameToSave,\n category: this.figure.category,\n tags: this.figure.tags,\n source: sourceJson,\n thumbnail\n })\n\n this.figure = result.figure\n this.violations = result.violations\n this.showSaveModal = false\n this.dirty = false\n navigate(`figure-modeller/${result.figure.id}`)\n return\n }\n\n // ๊ธฐ์กด ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ: folded.type์ด figure.type๊ณผ ์ผ์น˜ํ•˜๋„๋ก ๋ณด์žฅ\n if (this.figure.type && this.folded.type !== this.figure.type) {\n this.folded = { ...this.folded, type: this.figure.type }\n if (this.draft) this.draft.figureType = this.figure.type\n }\n const sourceJson = JSON.stringify(this.folded)\n const thumbnail = await captureThumbnail(this.draft, this.parts)\n\n const result = await updateFigure(this.figure.id, {\n name: this.figure.name,\n category: this.figure.category,\n tags: this.figure.tags,\n source: sourceJson,\n thumbnail\n })\n this.figure = result.figure\n this.violations = result.violations\n this.dirty = false\n } catch (e) {\n const msg = (e as Error).message\n this.saveFailure = msg\n this.saveModalError = msg\n } finally {\n this.saving = false\n }\n }\n\n /** Title of the candidate bar. A candidate opened from a release-check fix is described by translating that fix here. */\n private proposalTitle(session: proposalSessions.ProposalSession): string {\n const fix = session.fix\n if (fix) {\n return i18next.t('figure.text.release-check-fix', {\n part: fix.part,\n axis: fix.axis.toUpperCase(),\n from: fix.from ?? 'auto',\n to: fix.to\n })\n }\n return session.title || i18next.t('figure.text.assistant-proposed-a-figure')\n }\n\n private renderSaveModal() {\n if (!this.showSaveModal) return nothing\n\n return html`\n <div class=\"save-modal-backdrop\" @click=${(e: Event) => { if (e.target === e.currentTarget) this.showSaveModal = false }}>\n <div class=\"save-modal-card\" role=\"dialog\" aria-modal=\"true\">\n <h3 class=\"save-modal-title\">${i18next.t('figure.title.save-new-figure')}</h3>\n <p class=\"save-modal-desc\">${i18next.t('figure.text.save-new-figure-desc')}</p>\n\n <div class=\"save-modal-field\">\n <label>${i18next.t('figure.label.figure-type')}</label>\n <input\n .value=${this.saveModalType}\n placeholder=${i18next.t('figure.text.figure-type-example')}\n @input=${(e: Event) => (this.saveModalType = (e.target as HTMLInputElement).value.trim().toUpperCase())}\n />\n ${this.saveModalType && !releasableType(this.saveModalType)\n ? html`<div class=\"save-modal-error\">${i18next.t('figure.text.type-name-will-not-release')}</div>`\n : nothing}\n </div>\n\n <div class=\"save-modal-field\">\n <label>${i18next.t('figure.label.name')}</label>\n <input\n .value=${this.saveModalName}\n placeholder=${i18next.t('figure.text.figure-name-example')}\n @input=${(e: Event) => (this.saveModalName = (e.target as HTMLInputElement).value)}\n />\n </div>\n\n ${this.saveModalError ? html`<div class=\"save-modal-error\">${this.saveModalError}</div>` : nothing}\n\n <div class=\"save-modal-actions\">\n <button class=\"btn-cancel\" @click=${() => (this.showSaveModal = false)}>\n ${i18next.t('figure.button.cancel')}\n </button>\n <button class=\"btn-confirm\" ?disabled=${this.saving || !this.saveModalType} @click=${() => this.doSave()}>\n ${this.saving ? i18next.t('figure.text.saving-figure') : i18next.t('figure.button.save')}\n </button>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"figure-modeller-page.js","sourceRoot":"","sources":["../../client/pages/figure-modeller-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '../modeller/figure-side.js'\nimport '../modeller/figure-canvas.js'\nimport { captureThumbnail } from '../modeller/figure-thumbnail.js'\nimport '../modeller/figure-preview.js'\nimport { shouldStartNewFigure } from '../modeller/new-figure-entry.js'\nimport '../modeller/figure-parts.js'\nimport '../modeller/figure-inspector.js'\n\nimport { css, html, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { i18next, localize } from '@operato/i18n'\nimport { navigate, PageView } from '@operato/shell'\nimport { consumePendingFigureProposal } from '../modeller/figure-ai-target.js'\nimport { contractChanges, isPlaceholderType, placeholderType, TYPE_LENGTH, TYPE_PATTERN, validate } from '@hatiolab/figure-model'\nimport type { ContractChange, DetailLevel, FigureSource, PrimitiveKind } from '@hatiolab/figure-model'\n\nimport * as edits from '../modeller/part-edits.js'\nimport * as proposals from '../modeller/proposal.js'\nimport * as proposalSessions from '../modeller/proposal-session.js'\nimport type { ProposalSession } from '../modeller/proposal-session.js'\nimport { toFigureSource, fromFigureSource } from '../modeller/figure-source.js'\nimport type { FigureDraft, PartModel, PlaneAxis } from '../modeller/figure-source.js'\nimport { jointOf } from '../modeller/joint-edits.js'\nimport { poseAsDefaults } from '../modeller/pose-defaults.js'\nimport { gizmoPart } from '../modeller/joint-gizmo.js'\nimport { DEFAULT_VIEW } from '../modeller/figure-view.js'\nimport type { ViewSettings } from '../modeller/figure-view.js'\nimport { createFigure, fetchFigure, fetchFigureTypeNames, fetchReleasedSource, updateFigure } from '../graphql/index.js'\nimport type { Figure, FigureFinding, FigureGateFix, FigureProposal, ProposalFeedback } from '../types.js'\n\nconst FigureModellerPageBase = localize(i18next)(PageView) as typeof PageView\n\n/**\n * ์–ด๋А ์นธ์ด ์ ‘ํ˜€ ์žˆ๋‚˜. ์™ผ์ชฝ์€ ๋ถ€ํ’ˆ ๋ชฉ๋ก, ์˜ค๋ฅธ์ชฝ์€ ์†์„ฑยทํŒ์ •ยท์„ค์ •์ด๋‹ค.\n *\n * ์ด๋ฆ„์— fold ๋ฅผ ์•ˆ ์“ด๋‹ค. ์ด ํŒŒ์ผ์—์„œ ๊ทธ ๋‚ฑ๋ง์€ ์ด๋ฏธ fold.ts ์˜ ๊ฒƒ์ด๋‹ค โ€” ํŽธ์ง‘ ์ƒํƒœ๋ฅผ\n * ์ €์žฅ ํ˜•์‹์œผ๋กœ ์ ‘๋Š”๋‹ค๋Š” ๋œป์ด๋ผ ์นธ์„ ์ ‘๋Š” ๊ฒƒ๊ณผ ์•„๋ฌด ์ƒ๊ด€์ด ์—†๋‹ค. ํ•œ ํŒŒ์ผ์—์„œ ํ•œ ๋‚ฑ๋ง์ด\n * ๋‘ ๊ฐ€์ง€๋ฅผ ๋œปํ•˜๋ฉด ์ฝ๋Š” ์‚ฌ๋žŒ์ด ๋งค๋ฒˆ ์–ด๋А ์ชฝ์ธ์ง€ ๊ฐ€๋ ค์•ผ ํ•œ๋‹ค.\n */\ninterface Panes {\n left: boolean\n right: boolean\n}\n\n/**\n * ์ ‘์–ด ๋‘” ๊ฒƒ์€ **์ด ๋ธŒ๋ผ์šฐ์ €์—๋งŒ** ๋‚จ๋Š”๋‹ค.\n *\n * ์ €์ž‘ํ•œ Figure ์— ์‹ค๋ฆฌ๋Š” ๊ฐ’์ด ์•„๋‹ˆ๋‹ค. ํ™”๋ฉด์„ ์–ด๋–ป๊ฒŒ ๋ฒŒ๋ ค ๋†“๊ณ  ์ผํ•˜๋А๋ƒ๋Š” ์‚ฌ๋žŒ๋งˆ๋‹ค\n * ๋‹ค๋ฅด๊ณ , ๊ฐ™์€ Figure ๋ฅผ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์—ด์—ˆ์„ ๋•Œ ๋‚ด ํ™”๋ฉด ๋ฐฐ์น˜๊ฐ€ ๋”ฐ๋ผ๊ฐ€๋ฉด ๊ณค๋ž€ํ•˜๋‹ค.\n * ํ™”๋ฉด ์„ค์ •(`figure-view`)์„ ์ €์žฅํ•˜์ง€ ์•Š๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ด์œ ๋‹ค.\n *\n * ์‹œํฌ๋ฆฟ ์ฐฝ์ด๋‚˜ ์‚ฌ์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ง‰์€ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” **์ฝ๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ๋˜์ง„๋‹ค.** ๊ทธ๋•Œ๋Š”\n * ๋‘˜ ๋‹ค ํŽด์ง„ ์ฑ„๋กœ ์‹œ์ž‘ํ•œ๋‹ค โ€” ์ €์ž‘๋ฉด์ด ์•ˆ ๋œจ๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ซ๋‹ค.\n */\nconst PANES_KEY = 'figure-modeller.collapsed'\n\nfunction recallPanes(): Panes {\n try {\n const said = JSON.parse(localStorage.getItem(PANES_KEY) ?? 'null')\n return { left: said?.left === true, right: said?.right === true }\n } catch {\n return { left: false, right: false }\n }\n}\n\nfunction rememberPanes(panes: Panes): void {\n try {\n localStorage.setItem(PANES_KEY, JSON.stringify(panes))\n } catch {\n // ๋ชป ์ ์–ด๋„ ์ด๋ฒˆ ํ™”๋ฉด์€ ๊ทธ๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค. ๋‹ค์Œ์— ์—ด ๋•Œ ํŽด์ ธ ์žˆ์„ ๋ฟ์ด๋‹ค.\n }\n}\n\n/**\n * ์ƒˆ๋กœ ๋งŒ๋“  Figure ์˜ ์‹œ์ž‘ ์ƒํƒœ โ€” **๋นˆ ์ƒ์ž ํ•˜๋‚˜๋ฟ์ด๋‹ค.**\n *\n * ์ „์—๋Š” ๋ถ€ํ’ˆ(300 ์ƒ์ž)์„ ํ•˜๋‚˜ ๋†“์•„ ์ฃผ์—ˆ๋‹ค. ํ˜•์‹์ด ๋ถ€ํ’ˆ 0๊ฐœ๋ฅผ ์˜ค๋ฅ˜๋กœ ๋ณด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค\n * (`missing-parts` โ€” parts ๊ฐ€ ์—†๋‹ค). ๊ทธ๋ž˜์„œ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ–ˆ๋Š”๋ฐ ๋ถ‰์€ ํŒ์ •์ด ๋œจ๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋ ค๊ณ \n * ์ƒ์ž๋ฅผ ๊น”์•˜๋‹ค.\n *\n * **๊ทธ๋Ÿฐ๋ฐ ใ€Œ๋ฌด์กฐ๊ฑด ์ƒ์žใ€๋Š” ๋Œ€๊ฐœ ์ฒซ ๋™์ž‘์ด ๊ทธ๊ฒƒ์„ ์ง€์šฐ๋Š” ์ผ์ด ๋œ๋‹ค** โ€” ์ปจ๋ฒ ์ด์–ด๋ฅผ ๋งŒ๋“ค ์‚ฌ๋žŒ๋„,\n * ์‹œ๊ทธ๋„ํƒ€์›Œ๋ฅผ ๋งŒ๋“ค ์‚ฌ๋žŒ๋„ ์ƒ์ž๋ถ€ํ„ฐ ์น˜์šด๋‹ค. ๊ทธ๋ž˜์„œ ๋น„์šด๋‹ค. ํ˜•์‹์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ (๋ถ€ํ’ˆ ์—†๋Š” ๊ฒƒ์€\n * ๋ฐœํ–‰ํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด ๋งž๋‹ค) **ํ™”๋ฉด์ด ใ€Œ์•„์ง ์ €์ž‘ ์•ˆ ํ•จใ€๊ณผ ใ€Œํ˜•์‹์„ ์–ด๊น€ใ€์„ ๊ฐ€๋ฅธ๋‹ค.**\n *\n * ๋นˆ ํ™”๋ฉด์ด ๊ฐ€๋ฅด์น˜๋Š” ์ž๋ฆฌ๋‹ค: ๋ถ€ํ’ˆ ๋ชฉ๋ก์ด ใ€Œ์œ„์—์„œ ๋„ํ˜•์„ ๊ณจ๋ผ ์‹œ์ž‘ํ•˜์„ธ์š”ใ€๋ฅผ ์ด๋ฏธ ๋งํ•˜๊ณ  ํŒ”๋ ˆํŠธ๊ฐ€\n * ๋ฐ”๋กœ ๊ทธ ์œ„์— ์žˆ๋‹ค(`figure-parts`).\n */\nfunction startingModel(type: string): { draft: FigureDraft; parts: PartModel[] } {\n return {\n draft: { width: 300, height: 300, depth: 300, figureType: type, detailLevel: 'M' },\n parts: []\n }\n}\n\n/**\n * Figure ์ €์ž‘๋ฉด.\n *\n * ## ์„ธ ์นธ์ด๋‹ค\n *\n * ์™ผ์ชฝ ๋„ํ˜• ํŒ”๋ ˆํŠธ ยท ๋ถ€ํ’ˆ ๋ชฉ๋ก ยท ์†์„ฑ\n * ๊ฐ€์šด๋ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ โ€” `FigureInstance` ๋ฅผ ์‹ค์ œ๋กœ ์„ธ์šด๋‹ค\n * ์˜ค๋ฅธ์ชฝ ํŒ๋‹จ โ€” ๋Š˜ ๋–  ์žˆ๋‹ค\n *\n * ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ์”ฌ ์œ„์—์„œ ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ด ์ด ๋„๊ตฌ์˜ ์ „์ œ๋‹ค. **ํŽธ์ง‘ํ•œ ๊ฒƒ์ด ๊ณง ์‹ค์ œ ๊ฒฐ๊ณผ๋ฌผ**์ด๋ผ์•ผ\n * \"๋ชจ๋ธ๋Ÿฌ์—์„œ๋Š” ๋˜๋Š”๋ฐ ๋„๋ฉด์—์„œ๋Š” ๋‹ค๋ฅด๋‹ค\"๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š๋Š”๋‹ค.\n *\n * ## ํŽธ์ง‘ ๋ฐ์ดํ„ฐ๋Š” ์”ฌ ๋ชจ๋ธ ํ•˜๋‚˜๋ฟ์ด๋‹ค\n *\n * ์ด ํŽ˜์ด์ง€๊ฐ€ ๊ฐ–๋Š” ๊ฒƒ์€ ์ดˆ์•ˆ ํ•˜๋‚˜์™€ ๋ถ€ํ’ˆ ๋ชฉ๋ก โ€” ๋ฏธ๋ฆฌ๋ณด๊ธฐ์— ๊ทธ๋Œ€๋กœ ์„ธ์›Œ์ง€๋Š” ์”ฌ ๋ชจ๋ธ์ด๋‹ค.\n * ์ €์žฅ ํ˜•์‹(`FigureSource`)์œผ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ **๊ฒฝ๊ณ„์—์„œ๋งŒ** ํ•œ๋‹ค: ์ €์žฅํ•  ๋•Œ, ํŒ๋‹จํ•  ๋•Œ,\n * AI ํ›„๋ณด์™€ ๋น„๊ตํ•  ๋•Œ. ๊ทธ ๋ณ€ํ™˜์€ `figure-source.ts` ํ•œ ๊ณณ์—์„œ๋งŒ ์ผ์–ด๋‚œ๋‹ค.\n *\n * ํŒ”๋ ˆํŠธ๋„ ์†์„ฑ ํŽธ์ง‘๊ธฐ๋„ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ฌด์—‡์„ ํ•˜๊ฒ ๋‹ค๋Š” ๊ฒƒ๋งŒ ์•Œ๋ฆฌ๊ณ ,\n * ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ ์ด ํŽ˜์ด์ง€๋‹ค. ๋‘ ๊ณณ์—์„œ ๊ณ ์น˜๋ฉด ์‹คํ–‰ ์ทจ์†Œ๋„ ์ €์žฅ๋„ ์–ด๋А ์ชฝ์ด ๋งž๋Š”์ง€ ์•Œ ์ˆ˜\n * ์—†๊ฒŒ ๋œ๋‹ค.\n */\n/**\n * Whether this name could be released, by the format's own rules (`type-not-identifier`,\n * `type-is-placeholder`). Saving is not blocked, but the type cannot change after creation, so the\n * author has to hear it now to avoid a draft that can never be released.\n */\nfunction releasableType(type: string): boolean {\n return (\n !isPlaceholderType(type) &&\n TYPE_PATTERN.test(type) &&\n type.length >= TYPE_LENGTH.min &&\n type.length <= TYPE_LENGTH.max\n )\n}\n\n@customElement('figure-modeller-page')\nexport class FigureModellerPage extends FigureModellerPageBase {\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n background-color: var(--md-sys-color-surface);\n color: var(--md-sys-color-on-surface);\n /*\n ์ด ํ™”๋ฉด์˜ ๊ธ€์ž ํฌ๊ธฐ๋ฅผ ์—ฌ๊ธฐ์„œ ์ •ํ•œ๋‹ค.\n\n ์•ˆ ์ ์–ด ๋‘๋ฉด ๋ธŒ๋ผ์šฐ์ € ๊ธฐ๋ณธ ํฌ๊ธฐ(16px)๋ฅผ ๋ฌผ๋ ค๋ฐ›๋Š”๋‹ค. ์•„๋ž˜ ํŒจ๋„๋“ค์€ ์ „๋ถ€ 12px\n ์–ธ์ €๋ฆฌ๋ผ ์œ„ ๋‘ ์ค„๋งŒ ์ปค ๋ณด์ด๊ณ , ๋ฒ„ํŠผ์ด ํ™”๋ฉด ์ „์ฒด์™€ ๋”ฐ๋กœ ๋†€์•˜๋‹ค.\n\n form ์š”์†Œ(input ยท button)๋Š” ๊ธ€๊ผด์„ ๋ฌผ๋ ค๋ฐ›์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฐ์ž ์ ์–ด์•ผ ํ•œ๋‹ค.\n */\n font-size: 0.76rem;\n }\n\n /* ์„ธ ์นธ. 3D ๋ฅผ ๊ฐ€์žฅ ํฌ๊ฒŒ ๋‘”๋‹ค โ€” ์ด ๋„๊ตฌ์˜ ์ฃผ์–ด๋Š” ํ˜•์ƒ์ด์ง€ ์†์„ฑํ‘œ๊ฐ€ ์•„๋‹ˆ๋‹ค */\n div[edits] {\n flex: 1;\n display: grid;\n grid-template-columns: 220px 1fr 320px;\n min-height: 0;\n overflow: hidden;\n }\n\n /*\n **์„ธ ์นธ์˜ ์ž๋ฆฌ๋ฅผ ๋ชป ๋ฐ•๋Š”๋‹ค.**\n\n ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ์™ผ์ชฝ์„ ์ ‘์—ˆ์„ ๋•Œ ์˜ค๋ฅธ์ชฝ ์นธ์ด ๊ฐ€์šด๋ฐ ์ž๋ฆฌ๋กœ ๋ฐ€๋ ค ๋“ค์–ด์™€ ์บ”๋ฒ„์Šค๊ฐ€\n ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ง„๋‹ค. display none ์ธ ํ˜•์ œ๋Š” ๊ทธ๋ฆฌ๋“œ ์ž๋ฆฌ๋ฅผ ์ฐจ์ง€ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์ฒซ์งธ๊ฐ€\n ๋น ์ง€๋ฉด ๋‚˜๋จธ์ง€๊ฐ€ ํ•œ ์นธ์”ฉ ๋‹น๊ฒจ์ง„๋‹ค. ์˜ค๋ฅธ์ชฝ์€ ๋งˆ์ง€๋ง‰์ด๋ผ ๋น ์ ธ๋„ ๋ฐ€ ๊ฒƒ์ด ์—†์–ด์„œ\n ๋ฉ€์ฉกํ–ˆ๋‹ค โ€” ํ•œ์ชฝ๋งŒ ๊ณ ์žฅ ๋‚˜๋Š” ์ข…๋ฅ˜๋‹ค.\n\n ์ด ํŒŒ์ผ์ด ์ด๋ฏธ ํ•œ ๋ฒˆ ์ ์–ด ๋‘” ํ•จ์ •์ด๋‹ค. ์•„๋ž˜ div[lanes] ์ฃผ์„์— ๊ฐ™์€ ๋ง์ด ์žˆ๋‹ค.\n */\n div[edits] > figure-parts {\n grid-column: 1;\n }\n div[edits] > div[middle] {\n grid-column: 2;\n }\n div[edits] > figure-side {\n grid-column: 3;\n }\n\n /*\n ์ ‘ํžŒ ์นธ์€ **์ž๋ฆฌ๋ฅผ ์•„์˜ˆ ์•ˆ ์“ด๋‹ค.**\n\n ํญ๋งŒ 0 ์œผ๋กœ ์ค„์ด๋ฉด ์•ˆ์ชฝ์ด ๋„˜์ณ ๋‚˜์™€ ๊ฐ€์šด๋ฐ๋ฅผ ๋ฎ๋Š”๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ๋–ผ์–ด ๋‚ด๋ฉด ์˜ค๋ฅธ์ชฝ\n ์นธ์ด ํŽผ์ณ ๋‘” ์ถ”์ด ๊ทธ๋ฆผ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ์žƒ๋Š”๋‹ค โ€” figure-side ๊ฐ€ ํƒญ์„ ๊ฐ์ถœ ๋•Œ๋„ DOM ์—๋Š”\n ๋‚จ๊ธฐ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ด์œ ๋‹ค. display none ์ด ๋‘˜์„ ๋‹ค ์ง€ํ‚จ๋‹ค.\n\n ๊ฐ€์šด๋ฐ๋Š” ์ ‘์„ ๋•Œ๋งˆ๋‹ค ๋„“์ด๊ฐ€ ๋ฐ”๋€Œ๋Š”๋ฐ, ํŽธ์ง‘ ์บ”๋ฒ„์Šค์™€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ๊ฐ์ž ResizeObserver\n ๋ฅผ ๊ฐ–๊ณ  ์žˆ์–ด 3D ๊ฐ€ ์•Œ์•„์„œ ๋”ฐ๋ผ์˜จ๋‹ค.\n */\n div[edits][collapse-left] {\n grid-template-columns: 0 1fr 320px;\n }\n div[edits][collapse-right] {\n grid-template-columns: 220px 1fr 0;\n }\n div[edits][collapse-left][collapse-right] {\n grid-template-columns: 0 1fr 0;\n }\n div[edits][collapse-left] > figure-parts,\n div[edits][collapse-right] > figure-side {\n display: none;\n }\n\n div[spacer] {\n flex: 1;\n }\n\n /*\n ์นธ์„ ๋ฒกํ„ฐ ์ €์ž‘๋„๊ตฌ์˜ ๊ด€๋ก€๋Œ€๋กœ ๋†“๋Š”๋‹ค โ€” ์™ผ์ชฝ์— ๋ฌด์—‡์ด ์žˆ๋‚˜(๋ ˆ์ด์–ด), ์˜ค๋ฅธ์ชฝ์—\n ๊ณ ๋ฅธ ๊ฒƒ์˜ ์†์„ฑ. ์“ฐ๋Š” ์‚ฌ๋žŒ์ด ์ด๋ฏธ ์•„๋Š” ์ž๋ฆฌ๋ผ ๋ฐฐ์šธ ๊ฒƒ์ด ํ•˜๋‚˜ ์ค„์–ด๋“ ๋‹ค.\n ์ฐจ๋ณ„์„ฑ์€ ๋ฐฐ์น˜๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ€์šด๋ฐ์˜ ๋กœ์šฐํด๋ฆฌ 3D ์— ์žˆ์œผ๋ฏ€๋กœ, ๋ฐฐ์น˜๋กœ ๋‚ฏ์„ค๊ฒŒ ํ•˜์ง€\n ์•Š๋Š”๋‹ค.\n\n ์˜ค๋ฅธ์ชฝ์€ figure-side ๊ฐ€ ์†์„ฑ๊ณผ ํŒ๋‹จ์„ ํƒญ์œผ๋กœ ๊ฒน์ณ ๋‘”๋‹ค. ์œ„์•„๋ž˜๋กœ ๋‚˜๋ˆ„๋ฉด ๋‘˜ ๋‹ค\n ์ž˜๋ ค์„œ ์†์„ฑ์€ ์žฌ์งˆ๊นŒ์ง€, ํŒ๋‹จ์€ ๋ฌด๊ฒŒ๊นŒ์ง€ ๋ชป ๋‚ด๋ ค๊ฐ„๋‹ค. ๊ฐœ์ˆ˜๋Š” ํƒญ ์ด๋ฆ„ํ‘œ์— ๋‚จ์•„\n ํŒ๋‹จ์„ ๋ฎ์–ด ๋‘์–ด๋„ ๋ณด์ธ๋‹ค.\n */\n figure-side {\n min-height: 0;\n }\n\n figure-canvas,\n figure-preview {\n min-width: 0;\n min-height: 0;\n }\n\n /*\n ํŽธ์ง‘ ยท ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ „ํ™˜ ๋ฐ ์กฐ์ž‘ ๋ .\n\n ๊ฐ€์šด๋ฐ ์นธ ์œ„์— ์–‡๊ฒŒ ๋‘”๋‹ค. ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋Š”์ง€๊ฐ€ ๋Š˜ ๋ณด์—ฌ์•ผ ํ•˜๊ณ , ๋‘ ํ™”๋ฉด์ด ๊ฐ™์€\n ์ž๋ฆฌ๋ฅผ ์“ฐ๋ฏ€๋กœ ์–ด๋А ์ชฝ์ธ์ง€ ๋ชจ๋ฅด๋ฉด ํŽธ์ง‘์ด ์•ˆ ๋จน๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ฝํžŒ๋‹ค.\n */\n div[modes] {\n display: flex;\n align-items: center;\n flex: none;\n gap: 2px;\n padding: 4px var(--spacing-medium, 8px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n div[modes] button[mode] {\n padding: 3px 12px;\n font: var(--label-font, inherit);\n font-size: 0.74rem;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[mode]:hover:not([on]) {\n background-color: var(--md-sys-color-surface-container);\n }\n div[modes] button[mode][on] {\n font-weight: 700;\n color: var(--md-sys-color-on-primary-container);\n background-color: var(--md-sys-color-primary-container);\n }\n div[modes] div[separator] {\n width: 1px;\n height: 16px;\n margin: 0 4px;\n background-color: var(--md-sys-color-outline-variant);\n }\n div[modes] button[history] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n padding: 0;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[history]:hover:not([disabled]) {\n background-color: var(--md-sys-color-surface-container);\n color: var(--md-sys-color-on-surface);\n }\n div[modes] button[history][disabled] {\n opacity: 0.3;\n cursor: default;\n }\n div[modes] button[history] md-icon {\n --md-icon-size: 18px;\n }\n div[modes] button[collapse] {\n display: flex;\n align-items: center;\n flex: none;\n padding: 2px 3px;\n color: var(--md-sys-color-on-surface-variant, var(--md-sys-color-on-surface));\n background: none;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[collapse]:hover {\n background-color: var(--md-sys-color-surface-container);\n }\n div[modes] button[collapse] md-icon {\n --md-icon-size: 18px;\n }\n div[modes] span[dirty] {\n align-self: center;\n margin-right: 6px;\n font: var(--label-font, inherit);\n font-size: 0.72rem;\n color: var(--md-sys-color-tertiary);\n }\n div[modes] span[save-failed] {\n align-self: center;\n max-width: 240px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin-right: 6px;\n padding: 3px 7px;\n font: var(--label-font, inherit);\n font-size: 0.7rem;\n color: var(--md-sys-color-on-error-container, var(--md-sys-color-on-surface));\n background-color: var(--md-sys-color-error-container);\n border-radius: 4px;\n }\n div[modes] button[save] {\n padding: 4px 14px;\n margin-right: 4px;\n font: var(--label-font, inherit);\n font-size: 0.74rem;\n font-weight: 600;\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n border: none;\n border-radius: 6px;\n cursor: pointer;\n }\n div[modes] button[save]:hover:not([disabled]) {\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n }\n div[modes] button[save][disabled] {\n opacity: 0.35;\n cursor: default;\n }\n /*\n ํ›„๋ณด๊ฐ€ ์˜ค๋ฉด ๊ฐ€์šด๋ฐ๋ฅผ ๋‘˜๋กœ ๋‚˜๋ˆˆ๋‹ค.\n\n ์ˆซ์ž์™€ JSON ์œผ๋กœ ์Šน๋‚™์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค โ€” ์ €์ž‘ํ•œ ๊ฒƒ์ด ๊ณง ์‹ค๋ฌผ์ด๋ฏ€๋กœ **๋‘˜ ๋‹ค ์„ธ์›Œ\n ๋†“๊ณ ** ๊ณ ๋ฅด๊ฒŒ ํ•œ๋‹ค. ๋‚˜๋ž€ํžˆ ๋‘์ง€ ์•Š์œผ๋ฉด ๋ฌด์—‡์ด ๋‹ฌ๋ผ์กŒ๋Š”์ง€ ๊ธฐ์–ต์œผ๋กœ ๊ฒฌ์ค˜์•ผ ํ•œ๋‹ค.\n */\n /*\n ์„ธ๋กœ๋กœ ์Œ“์•„ ๋‘”๋‹ค. ๊ทธ๋ฆฌ๋“œ๋กœ ๋‘์—ˆ๋”๋‹ˆ ํ›„๋ณด๊ฐ€ ์—†์„ ๋•Œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ์ฒซ ์ค„(auto)์—\n ๋–จ์–ด์ ธ **๋†’์ด๊ฐ€ 0** ์ด ๋๋‹ค โ€” 3D ๊ฐ€ ํ†ต์งธ๋กœ ์‚ฌ๋ผ์กŒ๋‹ค. display:none ์ธ ํ˜•์ œ๋Š”\n ๊ทธ๋ฆฌ๋“œ ์ž๋ฆฌ๋ฅผ ์ฐจ์ง€ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n */\n div[middle] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n }\n /*\n ๊ฐ€์šด๋ฐ๋ฅผ ์ฑ„์šด๋‹ค. ๋‘˜ ๋‹ค ์ ์–ด์•ผ ํ•œ๋‹ค โ€” ํ•˜๋‚˜๋งŒ ์ ์œผ๋ฉด ๊ฐ์ถฐ์กŒ๋‹ค ๋‚˜ํƒ€๋‚œ ์ชฝ์ด ๋†’์ด 0 ์ด\n ๋˜์–ด ํ™”๋ฉด์ด ํ•˜์–—๊ฒŒ ๋œฌ๋‹ค. ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ๋‚ฌ๋‹ค.\n */\n div[middle] > figure-canvas,\n div[middle] > figure-preview {\n flex: 1;\n }\n div[lanes] {\n flex: 1;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 1px;\n min-height: 0;\n background-color: var(--md-sys-color-outline-variant);\n }\n div[lane] {\n display: grid;\n grid-template-rows: auto 1fr;\n min-width: 0;\n min-height: 0;\n background-color: var(--md-sys-color-surface);\n }\n div[lane] h4 {\n display: flex;\n align-items: center;\n gap: 6px;\n margin: 0;\n padding: 5px var(--spacing-medium, 8px);\n font: var(--label-font, inherit);\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 div[lane][candidate] h4 {\n color: var(--md-sys-color-primary);\n }\n div[lane] h4 small {\n font-weight: 400;\n letter-spacing: 0;\n text-transform: none;\n }\n\n /* ๋ฐ›๊ฑฐ๋‚˜ ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ ์‚ฌ์ด์— ๋‹ค๋ฅธ ๊ฒƒ์€ ์—†๋‹ค */\n div[decide] {\n display: flex;\n align-items: center;\n gap: var(--spacing-medium, 8px);\n padding: var(--spacing-medium, 8px) var(--spacing-large, 12px);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n background-color: var(--md-sys-color-primary-container);\n color: var(--md-sys-color-on-primary-container);\n font-size: 0.76rem;\n }\n div[decide] div[spacer] {\n flex: 1;\n }\n div[decide] button {\n padding: 5px 14px;\n font: var(--label-font, inherit);\n border-radius: 6px;\n cursor: pointer;\n }\n button[take] {\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n border: none;\n }\n button[drop] {\n color: inherit;\n background: none;\n border: 1px solid var(--md-sys-color-outline-variant);\n }\n button[take][disabled] {\n opacity: 0.4;\n cursor: default;\n }\n\n /* AI์˜ ํ‰๊ฐ€๋Š” ํŒ์ •๋ฌธ์ด ์•„๋‹ˆ๋ผ ์ˆ˜๋ฝ ์ „์— ์ฝ์„ ๊ทผ๊ฑฐ๋‹ค. ์ˆจ๊ธฐ์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ ์นฉ ์œ„์— ๋‘”๋‹ค. */\n div[quality-review] {\n padding: 7px var(--spacing-large, 12px);\n background-color: var(--md-sys-color-primary-container);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n color: var(--md-sys-color-on-primary-container);\n font-size: 0.72rem;\n line-height: 1.45;\n }\n div[quality-review][review] {\n background-color: var(--md-sys-color-tertiary-container);\n color: var(--md-sys-color-on-tertiary-container);\n }\n div[quality-review] strong { display: block; font-size: 0.74rem; }\n div[quality-review] ul { margin: 4px 0 0; padding-left: 18px; }\n div[proposal-note] {\n padding: 6px var(--spacing-large, 12px) 8px;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n background-color: var(--md-sys-color-surface-container-low);\n }\n div[proposal-note] label { display: block; font-size: 0.71rem; color: var(--md-sys-color-on-surface-variant); }\n div[proposal-note] input {\n box-sizing: border-box;\n width: 100%;\n margin-top: 4px;\n padding: 5px 7px;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: 5px;\n color: var(--md-sys-color-on-surface);\n background: var(--md-sys-color-surface);\n font: inherit;\n }\n\n /*\n ๋ฌด์—‡์„ ๋ฐ”๊พธ์ž๋Š” ๊ฒƒ์ธ๊ฐ€ โ€” ์นฉ ํ•˜๋‚˜๊ฐ€ ๋ณ€๊ฒฝ ํ•˜๋‚˜๋‹ค.\n\n ํ†ต์งธ๋กœ๋งŒ ๋ฐ›๊ฒŒ ๋‘๋ฉด ใ€Œ๋ชธํ†ต์€ ์ข‹์€๋ฐ ๋ชจํ„ฐ ์ž๋ฆฌ๊ฐ€ ํ‹€๋ ธ๋‹คใ€์— ๋‹ตํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๊ฒŒ\n ๊ฐ€์žฅ ํ”ํ•œ ๋ฐ˜์‘์ด๋‹ค. ์ฒ˜์Œ์—๋Š” ์ „๋ถ€ ๊ณจ๋ผ ๋‘”๋‹ค โ€” ํ†ต์งธ๋กœ ๋ฐ›๋Š” ๊ฒƒ์ด ๋ณดํ†ต์˜ ๊ธธ์ด๊ณ ,\n ๋นผ๊ณ  ์‹ถ์€ ๊ฒƒ๋งŒ ๋„๋ฉด ๋œ๋‹ค.\n */\n div[changes] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n padding: 0 var(--spacing-large, 12px) var(--spacing-medium, 8px) var(--spacing-large, 12px);\n background-color: var(--md-sys-color-primary-container);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n button[change] {\n display: flex;\n align-items: center;\n gap: 3px;\n padding: 2px 9px 2px 5px;\n font: var(--label-font, inherit);\n font-size: 0.72rem;\n color: var(--md-sys-color-on-surface-variant, 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: 999em;\n cursor: pointer;\n }\n button[change][on] {\n color: var(--md-sys-color-on-primary, #fff);\n background-color: var(--md-sys-color-primary);\n border-color: transparent;\n }\n button[change] md-icon {\n --md-icon-size: 14px;\n }\n /* ์•ˆ ๊ณ ๋ฅธ ๊ฒƒ์€ ๊ธ€์ž์— ์ค„์„ ๊ทธ์–ด ใ€Œ์•ˆ ๋ฐ›๋Š”๋‹คใ€๋ฅผ ๋งํ•œ๋‹ค */\n button[change]:not([on]) span[key] {\n text-decoration: line-through;\n }\n\n /* ๊ณ ๋ฅธ ์กฐํ•ฉ์ด ํ˜•์‹์„ ์–ด๊ธฐ๋ฉด ๋ฏธ๋ฆฌ ์•Œ๋ฆฐ๋‹ค. ์„ ํƒํ•œ ๋’ค์— ๊ฑฐ์ ˆ๋‹นํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ซ๋‹ค */\n /* ์ •์ฑ…์„ ๋„˜์€ ๊ฒƒ์€ ๋ง‰์ง€ ์•Š๋Š”๋‹ค โ€” ์•Œ๋ฆฌ๊ธฐ๋งŒ ํ•œ๋‹ค. ์ƒ‰์œผ๋กœ ๊ฐˆ๋ผ ๋‘”๋‹ค */\n p[blocked][warn] {\n color: var(--md-sys-color-tertiary);\n }\n p[blocked] {\n margin: 0;\n padding: 0 var(--spacing-large, 12px) var(--spacing-medium, 8px) var(--spacing-large, 12px);\n background-color: var(--md-sys-color-primary-container);\n font-size: 0.72rem;\n line-height: 1.5;\n color: var(--md-sys-color-error);\n }\n .save-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n }\n .save-modal-card {\n background: var(--md-sys-color-surface-container-high, #fff);\n color: var(--md-sys-color-on-surface, #1f1f1f);\n border-radius: 12px;\n padding: 24px;\n width: 380px;\n max-width: 90vw;\n box-shadow: 0 12px 36px rgba(0, 0, 0, 0.28);\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n .save-modal-title {\n margin: 0;\n font-size: 1.15rem;\n font-weight: 700;\n }\n .save-modal-desc {\n margin: 0;\n font-size: 0.8rem;\n color: var(--md-sys-color-on-surface-variant, #666);\n }\n .save-modal-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .save-modal-field label {\n font-size: 0.76rem;\n font-weight: 600;\n color: var(--md-sys-color-on-surface-variant, #555);\n }\n .save-modal-field input {\n padding: 8px 10px;\n border: 1px solid var(--md-sys-color-outline, #ccc);\n border-radius: 6px;\n font-size: 0.88rem;\n background: var(--md-sys-color-surface, #fff);\n color: inherit;\n }\n .save-modal-error {\n color: var(--md-sys-color-error, #b00020);\n font-size: 0.78rem;\n }\n .save-modal-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n margin-top: 8px;\n }\n .save-modal-actions button {\n padding: 6px 14px;\n border-radius: 6px;\n font-size: 0.82rem;\n cursor: pointer;\n border: none;\n }\n .save-modal-actions .btn-cancel {\n background: none;\n color: var(--md-sys-color-on-surface, inherit);\n }\n .save-modal-actions .btn-confirm {\n background: var(--md-sys-color-primary, #0066cc);\n color: var(--md-sys-color-on-primary, #fff);\n font-weight: 600;\n }\n .save-modal-actions .btn-confirm:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n `\n\n /** ํŽธ์ง‘ ์ค‘์ธ Figure. ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ์ค‘์ด๋ฉด `id` ๊ฐ€ ์—†๋‹ค. */\n @state() private figure?: Figure\n /** ํŽธ์ง‘ ๋ฐ์ดํ„ฐ โ€” ๋ฏธ๋ฆฌ๋ณด๊ธฐ์— ๊ทธ๋Œ€๋กœ ์„ธ์›Œ์ง€๋Š” ์”ฌ ๋ชจ๋ธ์ด๋‹ค. */\n @state() private draft?: FigureDraft\n @state() private parts: PartModel[] = []\n @state() private selected = -1\n /*\n ์ €์ž‘ ํ™”๋ฉด์˜ **๋ณด๊ธฐ ์„ค์ •**. ์กฐ๋ช… ยท ๋ฐ”๋‹ฅ ยท ์ถ• ๊ฐ€์ด๋“œ ๊ฐ™์€ ๊ฒƒ์ด๋‹ค.\n\n ์ž์‚ฐ์ด ์•„๋‹ˆ๋ผ ์ž‘์—…๋Œ€๋ผ ์ €์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค โ€” `toFigureSource` ๊ฐ€ ์ง‘์–ด ๊ฐ€์ง€ ์•Š๋Š”๋‹ค.\n ์ž์„ธํ•œ ๊ฒƒ์€ `modeller/figure-view.ts` ๋ฅผ ๋ณด๋ผ.\n */\n @state() private view: ViewSettings = DEFAULT_VIEW\n /**\n * ๊ฐ€์šด๋ฐ ์นธ์ด ๋ฌด์—‡์„ ๋ณด์ด๋‚˜.\n *\n * ํŽธ์ง‘ ์บ”๋ฒ„์Šค๋Š” ๋Š˜ ๊ธฐ์ค€ ํฌ๊ธฐ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ํ˜•์‹์˜ ๊ฐ’ ์ ˆ๋ฐ˜์€ ํฌ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์กŒ์„ ๋•Œ ์–ด๋–ป๊ฒŒ\n * ๋ฐ˜์‘ํ•˜๋Š”๊ฐ€์— ์žˆ๋‹ค. ๊ทธ๊ฒƒ์„ ๋ณผ ์ž๋ฆฌ๊ฐ€ ์—†์—ˆ๋‹ค.\n */\n @state() private mode: 'edit' | 'preview' = 'edit'\n /**\n * ์–ด๋А ์นธ์ด ์ ‘ํ˜€ ์žˆ๋‚˜.\n *\n * ํ†ต์งธ๋กœ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ์•ˆ์ชฝ ๊ฐ’๋งŒ ๋ฐ”๊พธ๋ฉด Lit ์ด ๊ฐ™์€ ๊ฐ์ฒด๋กœ ๋ณด๊ณ  ๋‹ค์‹œ ๊ทธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n */\n @state() private collapsed: Panes = recallPanes()\n @state() private dirty = false\n @state() private saving = false\n @state() private violations: FigureFinding[] = []\n /**\n * ์ €์žฅ์ด ์™œ ์•ˆ ๋๋‚˜.\n *\n * **`save()` ์— `catch` ๊ฐ€ ์—†์—ˆ๋‹ค.** `try โ€ฆ finally` ๋ฟ์ด๋ผ ์„œ๋ฒ„๊ฐ€ ๊ฑฐ์ ˆํ•˜๋ฉด(ํ˜•์‹ ์œ„๋ฐ˜ ยท ํƒ€์ž… ์ด๋ฆ„\n * ์ค‘๋ณต ยท ๊ถŒํ•œ ์—†์Œ ยท ๋„คํŠธ์›Œํฌ) ๋‹จ์ถ”๋งŒ ๋ฉˆ์ถ”๊ณ  ํ™”๋ฉด์€ **์•„๋ฌด ๋ง๋„ ํ•˜์ง€ ์•Š์•˜๋‹ค.** ์ €์ž‘์ž๋Š” ๋ˆŒ๋ €๋Š”๋ฐ\n * ์•„๋ฌด ์ผ๋„ ์•ˆ ๋‚œ ๊ฒƒ์œผ๋กœ ๋ณด๊ณ , ์ €์žฅ๋๋Š”์ง€ ์•„๋‹Œ์ง€๋„ ์•Œ ์ˆ˜ ์—†์—ˆ๋‹ค.\n *\n * ๋ฏธ์ €์žฅ ํ‘œ์‹œ(`dirty`)๋Š” ๊ทธ๋Œ€๋กœ ๋‚จ์œผ๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์žƒ์ง€๋Š” ์•Š์•˜๋‹ค. ์žƒ์€ ๊ฒƒ์€ **์‚ฌ์œ **๋‹ค.\n */\n @state() private saveFailure = ''\n @state() private showSaveModal = false\n @state() private saveModalType = ''\n @state() private saveModalName = ''\n @state() private saveModalError = ''\n /** ์ €์ž‘ ๋ณด์กฐ ๋˜๋Š” ๋ฐœํ–‰ ๊ฒŒ์ดํŠธ๊ฐ€ ์—ฐ ํ›„๋ณด ๊ฒ€ํ†  ์„ธ์…˜. ๋ฐ›๊ธฐ ์ „์—๋Š” ์ •๋ณธ์ด ์•„๋‹ˆ๋‹ค. */\n @state() private proposalSession?: ProposalSession\n /** ์ธ๋ผ์ธ ์š”์ฒญ์ฐฝ๊ณผ AI ๋„ํฌ๊ฐ€ ํ•จ๊ป˜ ์“ฐ๋Š” ํ˜„์žฌ Figure์˜ ์ œํ•œ๋œ ์„ธ์…˜ ํ”ผ๋“œ๋ฐฑ. */\n @state() private proposalFeedback: ProposalFeedback[] = []\n\n @state() private undoStack: { draft?: FigureDraft; parts: PartModel[]; selected: number }[] = []\n @state() private redoStack: { draft?: FigureDraft; parts: PartModel[]; selected: number }[] = []\n @state() private sceneRevision = 0\n private lastCoalesceKey?: string\n private lastHistoryTime = 0\n\n /**\n * ์ €์žฅ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•œ ๊ฒƒ. ์”ฌ ๋ชจ๋ธ์ด ๋ฐ”๋€” ๋•Œ๋งŒ ๋‹ค์‹œ ๋งŒ๋“ ๋‹ค.\n *\n * ์ €์žฅยทํŒ๋‹จยทAI ํ›„๋ณด ๋น„๊ต๊ฐ€ ์ด๊ฒƒ์„ ์“ด๋‹ค. ๋ Œ๋”๋งํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ ๋งŒ๋“ค๋ฉด ํŒ๋‹จ ํŒจ๋„์ด ๋งค๋ฒˆ\n * ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜๋ฏ€๋กœ, ๋ณ€ํ™˜์€ `willUpdate` ์—์„œ ํ•œ ๋ฒˆ๋งŒ ํ•œ๋‹ค.\n */\n private folded?: FigureSource\n\n /**\n * ์ง์ „ ๋ฐœํ–‰ํŒ์˜ ์ •๋ณธ. ํ•œ ๋ฒˆ๋„ ๋ฐœํ–‰ ์•ˆ ํ•œ ๋„ํ˜•์—๋Š” ์—†๋‹ค.\n *\n * ๋ฐœํ–‰๋œ ๋„ํ˜•์„ ๊ณ ์น˜๋Š” ๋™์•ˆ ใ€Œ์ด ๊ณ ์นจ์ด ์ด๋ฏธ ๋†“์ธ ๊ฒƒ์„ ๊นจ๋‚˜ใ€๋ฅผ ๊ฒฌ์ฃผ๋ ค๋ฉด ์ด๊ฒƒ์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.\n * ์ €์žฅ ์ „ ์ดˆ์•ˆ์€ ์„œ๋ฒ„๊ฐ€ ๋ชจ๋ฅด๋ฏ€๋กœ ํŒ์ •๋„ ์—ฌ๊ธฐ์„œ ํ•œ๋‹ค โ€” ํ˜•์‹์˜ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ์“ด๋‹ค(ADR-0063 ๊ฒฐ์ • 3).\n */\n @state() private releasedSource?: FigureSource\n\n /** ์ง€๊ธˆ ๊ณ ์นœ ๊ฒƒ์ด ๋†“์ธ ๊ฒƒ์— ๋ฌด์—‡์„ ํ•˜๋‚˜. ๋ฐœํ–‰ ์ „์—๋Š” ๋น„์–ด ์žˆ๋‹ค. */\n @state() private contract: ContractChange[] = []\n\n /** ๊ฒฌ์ฃผ์ง€ ๋ชปํ•œ ๊นŒ๋‹ญ. **๋น„์–ด ์žˆ๋Š” ๋ชฉ๋ก๊ณผ ๋‹ค๋ฅด๋‹ค** โ€” ใ€Œ๊นจ์ง€ ์•Š๋Š”๋‹คใ€๊ฐ€ ์•„๋‹ˆ๋ผ ใ€Œ๋ชจ๋ฅธ๋‹คใ€๋‹ค. */\n @state() private contractFailure?: string\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('draft') || changed.has('parts') || changed.has('releasedSource')) {\n this.folded = this.draft ? toFigureSource(this.draft, this.parts) : undefined\n this.contract = this.compareWithReleased()\n }\n }\n\n /**\n * ๊ณ ์น˜๋Š” ์ค‘์˜ ์ •๋ณธ์€ **์ž ๊น ํ˜•์‹์„ ๋ชป ์ง€๋‚  ์ˆ˜ ์žˆ๋‹ค** โ€” ๋ถ€ํ’ˆ ์ด๋ฆ„์„ ์ง€์› ๋‹ค๊ฐ€ ๋‹ค์‹œ ์ ๋Š” ์‚ฌ์ด๊ฐ€\n * ๊ทธ๋ ‡๋‹ค. ๊ทธ๋•Œ ๊ฒฌ์ฃผ๊ธฐ๊ฐ€ ๋˜์ง€๋ฉด ํ™”๋ฉด์ด ํ†ต์งธ๋กœ ๋ฉˆ์ถ”๋ฏ€๋กœ ๋ชฉ๋ก์€ ๋น„์šด๋‹ค.\n *\n * **๋นˆ ๋ชฉ๋ก์„ ใ€Œ๊นจ์ง€ ์•Š๋Š”๋‹คใ€๋กœ ๋ณด์ด๊ฒŒ ๋‘์ง€ ์•Š๋Š”๋‹ค.** ๊นŒ๋‹ญ์„ ํ•จ๊ป˜ ๋“ค๊ณ  ์žˆ๋‹ค๊ฐ€ ํ™”๋ฉด์ด ใ€Œํ™•์ธํ•˜์ง€\n * ๋ชปํ–ˆ๋‹คใ€๋กœ ๋งํ•œ๋‹ค. ์กฐ์šฉํžˆ ๋น„์šฐ๋ฉด ์ €์ž‘์ž๋Š” ์•ˆ์ „ํ•˜๋‹ค๊ณ  ์ฝ๋Š”๋‹ค โ€” ์‹ค์ œ๋กœ ๊ทธ๋Ÿฐ ์ผ์ด ๋‚ฌ๋‹ค: ์•ฑ์—\n * ๋“  ํ˜•์‹์ด ์˜› ํŒ์ด๋ผ ๊ฒฌ์ฃผ๋Š” ํ•จ์ˆ˜ ์ž์ฒด๊ฐ€ ์—†์—ˆ๋Š”๋ฐ, ํ™”๋ฉด์€ ์•„๋ฌด ๋ง๋„ ์•ˆ ํ•˜๊ณ  ์กฐ์šฉํ–ˆ๋‹ค.\n */\n private compareWithReleased(): ContractChange[] {\n this.contractFailure = undefined\n if (!this.releasedSource || !this.folded) return []\n\n try {\n return contractChanges(this.releasedSource, this.folded)\n } catch (e) {\n this.contractFailure = (e as Error).message\n return []\n }\n }\n\n /**\n * ํŽ˜์ด์ง€ ๋ฐ– AI ๋„ํฌ์—๋„ **์ง€๊ธˆ ์†์— ๋“  ์ •๋ณธ**์„ ์•Œ๋ ค ์ค€๋‹ค.\n *\n * asidebar ๋„ํฌ๋Š” ์ฃผ์†Œ์˜ id๋งŒ\n * ์•Œ์•˜์œผ๋ฏ€๋กœ DB์— ์ €์žฅ๋œ ์˜› ํŒ์„ ์ฝ์—ˆ๋‹ค. ์ €์žฅ ์ „ ๋ณ€๊ฒฝ์„ ๋‘๊ณ  \"์™œ ๋ฐœํ–‰์ด ์•ˆ ๋˜๋‚˜\" ๋˜๋Š”\n * \"์ด ๋ถ€ํ’ˆ์„ ์ค„์—ฌ ๋‹ฌ๋ผ\"๊ณ  ๋ฌผ์œผ๋ฉด ๋Œ€ํ™”๊ฐ€ ์—‰๋šฑํ•œ ํŒ์„ ๋ณด๊ฒŒ ๋˜๋Š” ๊ฒฐํ•จ์ด์—ˆ๋‹ค.\n *\n * ์ด ์ด๋ฒคํŠธ๋Š” ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋„ํฌ๊ฐ€ ๋Œ€ํ™” ์ธ์ž๋กœ๋งŒ ์ž ์‹œ ๋“ค๊ณ  ์žˆ๊ณ , ์„œ๋ฒ„ ๋„๊ตฌ๋„ ๋„๋ฉ”์ธ ์†Œ์†\n * Figure๋ฅผ ๋จผ์ € ํ™•์ธํ•œ ๋’ค์—๋งŒ ์ด source๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ํŽ˜์ด์ง€๋ฅผ ๋– ๋‚  ๋•Œ๋Š” ์•„๋ž˜์—์„œ ์ง€์›Œ ์˜ค๋ž˜๋œ\n * ์ดˆ์•ˆ์ด ๋‹ค์Œ Figure์— ์„ž์ด์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค.\n */\n updated(changed: Map<string, unknown>) {\n if (changed.has('figure') && (changed.get('figure') as Figure | undefined)?.id !== this.figure?.id) {\n this.proposalFeedback = []\n }\n if (changed.has('draft') || changed.has('parts') || changed.has('figure')) {\n window.dispatchEvent(\n new CustomEvent('figure-ai-context', {\n detail: { figureId: this.figure?.id, source: this.folded }\n })\n )\n }\n }\n\n private announceAiContext = () => {\n if (!this.active) return\n window.dispatchEvent(new CustomEvent('figure-ai-context', {\n detail: { figureId: this.figure?.id, source: this.folded }\n }))\n }\n\n /*\n `detail.staged` is the dock's answer to \"did anything actually happen to my candidate\". The\n listener is synchronous, so the dock reads it right after dispatching and only then tells the\n chat card to say so. It used to be called `accepted`, which claimed more than it knew โ€” the\n page had put the candidate somewhere, not the person had agreed to it.\n */\n private receiveDockProposal = (event: Event) => {\n const detail = (event as CustomEvent).detail\n if (!detail) return\n\n const source =\n detail?.source ||\n (detail?.proposal?.source\n ? typeof detail.proposal.source === 'string'\n ? JSON.parse(detail.proposal.source)\n : detail.proposal.source\n : undefined)\n\n if (source) {\n detail.staged = this.openProposalForReview(source, detail.proposal)\n }\n }\n\n private onFigureAiStage = (event: Event) => {\n const detail = (event as CustomEvent).detail\n const source = detail?.source || (detail?.proposal?.source ? (typeof detail.proposal.source === 'string' ? JSON.parse(detail.proposal.source) : detail.proposal.source) : undefined)\n if (source) {\n this.openProposalForReview(source, detail.proposal)\n }\n }\n\n /**\n * A candidate from the dock opens the review lane. It does not become the draft.\n *\n * This used to write `draft` and `parts` straight onto the working draft and then set\n * `proposalSession = undefined` โ€” the dock's candidate overwrote what the author had open and\n * erased the lane built to review it. There was no moment where the person said yes, and a\n * draft has no version history to go back to, so the previous work was simply gone.\n *\n * `ai-client-base`'s analysis contract states this as a literal type: `approvalRequired: true`,\n * and of `proposal`, \"It is never an instruction to mutate canonical state.\" The in-page\n * The in-page entrance honoured this through `openAiProposal` before it was removed, and the dock\n * entrance is now the same door. Applying is what the take button does, from the lane, per change.\n *\n * ## Why the overwrite was there โ€” so it does not come back\n *\n * The dock opened the review lane originally. Before `c42a0b63df` this handler read:\n *\n * if (!this.active || (detail?.figureId ?? '') !== (this.figure?.id ?? '')) return\n * if (detail?.draftType && detail.draftType !== this.folded?.type) return\n * if (detail?.baseSource && detail.baseSource !== JSON.stringify(this.folded)) return\n * this.receive(detail.proposal)\n *\n * Three guards, each a silent `return`, and the last compared a serialized source byte for byte.\n * Any difference that changed nothing at all dropped the candidate with no message, so the dock\n * would report a candidate and the screen would show none.\n *\n * The `ox-assistant-chat` migration fixed that by replacing the path rather than the guards, and\n * the review lane went out with them โ€” `applySourceDirectly` wrote straight to the draft, and\n * cleared `proposalSession` on the way past. **Nobody decided that reviews were unwanted here.**\n * It was the cost of loosening a guard by deleting what stood around it.\n *\n * So the guards are gone and stay gone, and the lane is back. If a candidate ever seems not to\n * arrive, the thing to look for is a silent `return` on this path โ€” not the lane.\n *\n * Returns whether the lane opened, so the dock can tell the chat whether to say it staged.\n */\n private openProposalForReview(rawSource: any, proposal?: any): boolean {\n const source = typeof rawSource === 'string' ? JSON.parse(rawSource) : rawSource\n if (!source || !Array.isArray(source.parts)) return false\n\n try {\n /*\n The dock sends a FigureProposal whose metrics sit at the top level; the staged-on-navigate\n path carries the raw tool result, which nests them under score/cost. Read both rather than\n making one of the two entrances reshape its payload.\n */\n this.proposalSession = proposalSessions.openAiProposal(this.folded, {\n source: JSON.stringify(source),\n grade: proposal?.grade ?? proposal?.score?.grade,\n triangles: proposal?.triangles ?? proposal?.cost?.triangles,\n groups: proposal?.groups ?? proposal?.cost?.groups,\n attempts: proposal?.attempts,\n quality: proposal?.quality\n } as FigureProposal)\n\n this.mode = 'edit'\n this.requestUpdate()\n return true\n } catch (e) {\n console.error('[FigureModeller] Failed to open the AI candidate for review:', e)\n return false\n }\n }\n\n private proposalBaseSource?: string\n\n connectedCallback() {\n super.connectedCallback()\n window.addEventListener('figure-ai-context-request', this.announceAiContext)\n window.addEventListener('figure-ai-review', this.receiveDockProposal)\n window.addEventListener('figure-ai-stage', this.onFigureAiStage)\n window.addEventListener('figure-save-request', this.onSaveRequest)\n window.addEventListener('keydown', this.handleKeyDown)\n }\n\n disconnectedCallback() {\n window.removeEventListener('keydown', this.handleKeyDown)\n window.removeEventListener('figure-save-request', this.onSaveRequest)\n window.removeEventListener('figure-ai-stage', this.onFigureAiStage)\n window.removeEventListener('figure-ai-context-request', this.announceAiContext)\n window.removeEventListener('figure-ai-review', this.receiveDockProposal)\n window.dispatchEvent(new CustomEvent('figure-ai-context', { detail: undefined }))\n super.disconnectedCallback()\n }\n\n private onSaveRequest = () => {\n if (this.active) this.save()\n }\n\n private handleKeyDown = (e: KeyboardEvent) => {\n if (!this.active || this.mode !== 'edit' || this.proposalSession) return\n const target = e.composedPath()[0] as HTMLElement | undefined\n const tag = target?.tagName?.toLowerCase()\n if (tag === 'input' || tag === 'textarea' || target?.isContentEditable) return\n\n if ((e.metaKey || e.ctrlKey) && !e.altKey) {\n if (e.key === 'z' && !e.shiftKey) {\n e.preventDefault()\n this.undo()\n } else if ((e.key === 'z' && e.shiftKey) || e.key === 'y') {\n e.preventDefault()\n this.redo()\n }\n }\n }\n\n get context() {\n return {\n title: this.figure?.name ?? i18next.t('figure.title.new-figure'),\n /*\n **์ œ๋ชฉ ์ค„์€ ๋ˆ๋‹ค.** ์ด๋ฆ„์€ ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค.\n\n ์ด ํ™”๋ฉด์˜ ์ฃผ์ธ๊ณต์€ ์บ”๋ฒ„์Šค๋‹ค. ์ด๋ฆ„์€ ์†์„ฑ ํŒจ๋„์—์„œ ๋ณด๊ณ  ๊ณ ์น˜๊ณ , ๋ธŒ๋ผ์šฐ์ € ํƒญ์—๋„\n ใ€Œoperato figure ยท ์ž‘์—…์žใ€๋กœ ๋‚˜์˜จ๋‹ค. ๊ทธ ์œ„์— ์ œ๋ชฉ ์ค„์„ ํ•œ ๊ฒน ๋” ์–น์œผ๋ฉด ๊ฐ™์€ ๋ง์ด ์„ธ ๋ฒˆ์ด๊ณ ,\n ๊ทธ๋งŒํผ ์บ”๋ฒ„์Šค๊ฐ€ ๋‚ฎ์•„์ง„๋‹ค.\n\n `title` ์„ ๋น„์šฐ๋Š” ๊ฒƒ์œผ๋กœ ๋„์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๊ทธ ๊ฐ’์„ MDI ํƒญ๋„ ์ฝ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ๋น„์šฐ๋ฉด ํƒญ ์ด๋ฆ„์ด\n ํ•จ๊ป˜ ์‚ฌ๋ผ์ง„๋‹ค. ์ด๋ฆ„์€ ํ•˜๋‚˜์ด๊ณ  ๊ทธ๋ฆฌ๋Š” ์ž๋ฆฌ๋งˆ๋‹ค ์ผœ๊ณ  ๋ˆ๋‹ค(`ox-page-title-bar`).\n */\n heading: false\n }\n }\n\n render() {\n const source = this.folded\n\n return html`\n <div\n edits\n ?collapse-left=${this.collapsed.left}\n ?collapse-right=${this.collapsed.right}\n @figure-rename=${(e: CustomEvent) => this.rename(e.detail.name)}\n @figure-type-change=${(e: CustomEvent) => this.typeChanged(e.detail.type)}\n @figure-catalog=${(e: CustomEvent) => this.catalog(e.detail)}\n @figure-released=${(e: CustomEvent) => this.released(e.detail.figure)}\n @figure-reverted=${() => this.figure?.id && this.load(this.figure.id)}\n @preview-sizing-fix=${(e: CustomEvent) => this.previewSizingFix(e.detail)}\n @apply-sizing-fix=${(e: CustomEvent) => this.applySizingFix(e.detail)}\n @add-part=${(e: CustomEvent) => this.addPart(e.detail.primitive)}\n @select-part=${(e: CustomEvent) => (this.selected = e.detail.index)}\n @remove-part=${(e: CustomEvent) => this.removePart(e.detail.index)}\n @duplicate-part=${(e: CustomEvent) => this.duplicatePart(e.detail.index)}\n @move-part-up=${(e: CustomEvent) => this.movePart(e.detail.index, e.detail.to)}\n @move-part-down=${(e: CustomEvent) => this.movePart(e.detail.index, e.detail.to)}\n @update-part=${(e: CustomEvent) => this.updatePart(e.detail.index, e.detail.part)}\n @part-changed=${(e: CustomEvent) => this.partChanged(e.detail.name, e.detail.part)}\n @toggle-part-hidden=${(e: CustomEvent) => this.toggleHidden(e.detail.index)}\n @set-all-parts-hidden=${(e: CustomEvent) => this.setAllHidden(e.detail.hidden)}\n @change-detail-level=${(e: CustomEvent) => this.changeDetailLevel(e.detail.level)}\n @update-draft=${(e: CustomEvent) => this.updateDraft(e.detail.draft)}\n @resize-base=${(e: CustomEvent) => this.resizeBase(e.detail.draft, e.detail.parts)}\n @part-tool=${(e: CustomEvent) => this.runTool(e.detail.index, e.detail.tool, e.detail.axis)}\n @pick-part=${(e: CustomEvent) => this.pickPart(e.detail.name)}\n >\n <figure-parts\n .parts=${this.parts}\n .level=${(this.draft?.detailLevel ?? 'M') as DetailLevel}\n .selected=${this.selected}\n ></figure-parts>\n\n <div middle>\n ${this.renderBar()}\n ${this.proposalSession ? this.renderDecision() : ''}\n ${this.proposalSession\n ? html`\n <div lanes>\n <div lane>\n <h4>${i18next.t('figure.label.now')}</h4>\n <figure-canvas\n .figureId=${this.figure?.id || this.figure?.type}\n .draft=${this.draft}\n .parts=${this.parts}\n .picked=${this.pickedName}\n .view=${this.view}\n .revision=${this.sceneRevision}\n ></figure-canvas>\n </div>\n <div lane candidate>\n <h4>\n ${i18next.t('figure.label.proposed')}\n ${this.proposalSession.metrics\n ? html`<small>\n ${this.proposalSession.metrics.grade} ยท\n ${i18next.t('figure.text.n-triangles', { count: this.proposalSession.metrics.triangles })}\n ${this.proposalSession.metrics.quality ? html` ยท ${this.proposalSession.metrics.quality.summary}` : ''}\n </small>`\n : ''}\n </h4>\n ${this.renderProposed()}\n </div>\n </div>\n `\n : this.mode === 'preview'\n ? html`<figure-preview\n .source=${source}\n .draft=${this.draft}\n .view=${this.view}\n @pose-as-default=${(e: CustomEvent) => this.poseAsDefault(e.detail.values)}\n ></figure-preview>`\n : html`<figure-canvas\n .figureId=${this.figure?.id || this.figure?.type}\n .draft=${this.draft}\n .parts=${this.parts}\n .picked=${this.pickedName}\n .gizmo=${this.jointGizmo}\n .view=${this.view}\n .revision=${this.sceneRevision}\n ></figure-canvas>`}\n </div>\n\n <figure-side\n .figureName=${this.figure?.name ?? ''}\n .figureCategory=${this.figure?.category}\n .figureTags=${this.figure?.tags}\n .figureId=${this.figure?.id ?? ''}\n .figureState=${this.figure?.state}\n .figureVersion=${this.figure?.version ?? 0}\n .dirty=${this.dirty}\n .draft=${this.draft}\n .parts=${this.parts}\n .source=${source}\n .contract=${this.contract}\n .contractFailure=${this.contractFailure}\n .selected=${this.selected}\n .view=${this.view}\n @update-view=${(e: CustomEvent) => (this.view = e.detail.view)}\n ></figure-side>\n </div>\n ${this.renderSaveModal()}\n `\n }\n\n /**\n * ๊ฐ€์šด๋ฐ ์นธ ์œ„์˜ ๋  โ€” ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋‚˜์™€, ์–‘์ชฝ ์นธ์„ ์ ‘๋Š” ์†์žก์ด.\n *\n * ## ๋ฌด์—‡์„ ๋ณด๊ณ  ์žˆ๋‚˜\n *\n * ํŽธ์ง‘๊ณผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” ๊ฐ™์€ ์ž๋ฆฌ๋ฅผ ๋†“๊ณ  ๋‹คํˆฌ์ง€ ์•Š๋Š”๋‹ค. ๋‘˜ ๋‹ค ๋„“์ด๊ฐ€ ํ•„์š”ํ•˜๊ณ , ๋™์‹œ์—\n * ๋ด์•ผ ํ•  ์ผ์ด ์—†๋‹ค. ๋ถ€ํ’ˆ์„ ๊ณ ์น˜๋Š” ๋™์•ˆ์—๋Š” ํŽธ์ง‘๋งŒ ๋ณด๋ฉด ๋˜๊ณ , ๋‹ค ๊ณ ์นœ ๋’ค์— ๋„๋ฉด์—์„œ\n * ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ๋ณด๋ฉด ๋œ๋‹ค.\n *\n * ํ›„๋ณด๊ฐ€ ์™€ ์žˆ์„ ๋•Œ๋Š” ๊ทธ ๋‘ ๋‹จ์ถ”๋ฅผ ๋‚ด์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋•Œ๋Š” ๊ฐ€์šด๋ฐ๊ฐ€ ์ด๋ฏธ ๋‘˜๋กœ ๋‚˜๋‰˜์–ด\n * ์žˆ๋‹ค.\n *\n * ## ๋ ๋Š” ๋Š˜ ์žˆ๋‹ค\n *\n * ์†์žก์ด๊ฐ€ ์—ฌ๊ธฐ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ›„๋ณด๊ฐ€ ์™”๋‹ค๊ณ  ๋ ๋ฅผ ํ†ต์งธ๋กœ ๊ฐ์ถ”๋ฉด, ์นธ์„ ์ ‘์–ด ๋‘” ์ฑ„๋กœ\n * ํ›„๋ณด๋ฅผ ๋ฐ›์œผ๋ฉด ๋˜๋Œ๋ฆด ๋ฐฉ๋ฒ•์ด ์—†์–ด์ง„๋‹ค.\n */\n private renderBar() {\n const one = (mode: 'edit' | 'preview') => html`\n <button\n mode\n ?on=${this.mode === mode}\n title=${i18next.t('figure.text.mode-' + mode + '-explained')}\n @click=${() => (this.mode = mode)}\n >\n ${i18next.t('figure.label.mode-' + mode)}\n </button>\n `\n\n return html`<div modes>\n ${this.renderCollapse('left')}${this.proposalSession ? '' : html`${one('edit')}${one('preview')}`}\n ${this.mode === 'edit' && !this.proposalSession\n ? html`\n <div separator></div>\n <button\n history\n ?disabled=${this.undoStack.length === 0}\n title=\"${i18next.t('figure.button.undo')} (Ctrl+Z)\"\n @click=${() => this.undo()}\n >\n <md-icon>undo</md-icon>\n </button>\n <button\n history\n ?disabled=${this.redoStack.length === 0}\n title=\"${i18next.t('figure.button.redo')} (Ctrl+Y)\"\n @click=${() => this.redo()}\n >\n <md-icon>redo</md-icon>\n </button>\n `\n : ''}\n <div spacer></div>\n <!--\n AI ๋ฅผ ์—ฌ๋Š” ๋‹จ์ถ”๋Š” ์—ฌ๊ธฐ ๋‘์ง€ ์•Š๋Š”๋‹ค. ์•ฑ ๋จธ๋ฆฌ์ค„์— ์ด๋ฏธ ์žˆ๊ณ  ๊ฐ™์€ dock ์„ ์—ฐ๋‹ค โ€” ํ•œ ํ™”๋ฉด์—\n ๊ฐ™์€ ๊ณณ์œผ๋กœ ๊ฐ€๋Š” ๋ฌธ์ด ๋‘˜์ด๋ฉด ์‚ฌ๋žŒ์ด ๋‘˜์˜ ์ฐจ์ด๋ฅผ ์ฐพ๋Š”๋‹ค. ์—ด๋ฆฐ ๋„ํ˜•์„ dock ์— ๋„˜๊ธฐ๋Š” ์ผ์€\n ๋‹จ์ถ”๊ฐ€ ํ•˜๋˜ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค: dock ์ด ์—ด๋ฆด ๋•Œ figure-ai-context-request ๋กœ ๋ฌป๊ณ  ์ด ํ™”๋ฉด์ด\n ์ €์žฅ ์ „ ์ •๋ณธ ๊ทธ๋Œ€๋กœ ๋‹ตํ•œ๋‹ค.\n -->\n ${this.saveFailure ? html`<span save-failed title=${this.saveFailure}>${this.saveFailure}</span>` : ''}\n ${this.dirty ? html`<span dirty>${i18next.t('figure.text.unsaved-changes')}</span>` : ''}\n <button save ?disabled=${!this.canSave} @click=${() => this.save()}>\n ${this.saving ? i18next.t('figure.text.saving-figure') : i18next.t('figure.button.save')}\n </button>\n ${this.renderCollapse('right')}\n </div>`\n }\n\n /**\n * ํ•œ์ชฝ ์นธ์„ ์ ‘๊ณ  ํŽด๋Š” ์†์žก์ด.\n *\n * ํ™”์‚ดํ‘œ๋Š” **๋ˆ„๋ฅด๋ฉด ๋ฌด์—‡์ด ์ผ์–ด๋‚˜๋Š”์ง€**๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค โ€” ํŽด์ ธ ์žˆ์œผ๋ฉด ์นธ์ด ์‚ฌ๋ผ์งˆ ์ชฝ,\n * ์ ‘ํ˜€ ์žˆ์œผ๋ฉด ์นธ์ด ๋Œ์•„์˜ฌ ์ชฝ. ์ง€๊ธˆ ์ƒํƒœ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ํ•˜๋ฉด ๋ฐ˜๋Œ€๋กœ ์ฝํžŒ๋‹ค.\n */\n private renderCollapse(side: keyof Panes) {\n const gone = this.collapsed[side]\n const shut = side === 'left' ? 'chevron_left' : 'chevron_right'\n const back = side === 'left' ? 'chevron_right' : 'chevron_left'\n const pane = side === 'left' ? 'parts' : 'properties'\n\n return html`\n <button\n collapse\n title=${i18next.t(`figure.text.${gone ? 'expand' : 'collapse'}-${pane}-pane`)}\n @click=${() => this.toggleCollapse(side)}\n >\n <md-icon>${gone ? back : shut}</md-icon>\n </button>\n `\n }\n\n private toggleCollapse(side: keyof Panes): void {\n this.collapsed = { ...this.collapsed, [side]: !this.collapsed[side] }\n rememberPanes(this.collapsed)\n }\n\n /**\n * AI ํ›„๋ณด๋ฅผ ์‹ค์ œ ๋ฐฐ์น˜ ํฌ๊ธฐ์—์„œ๋„ ๋ณธ๋‹ค.\n *\n * ํŽธ์ง‘ ์บ”๋ฒ„์Šค ํ•œ ์žฅ์œผ๋กœ๋Š” `repeat`ยท`fixed`ยท`anchor`๊ฐ€ ๋งž๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค. ํ›„๋ณด๋Š” ์•„์ง\n * ์ •๋ณธ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์ €์žฅํ•˜์ง€ ์•Š๊ณ , `figure-preview`๊ฐ€ ๊ฐ™์€ FigureSource๋ฅผ ๊ธฐ์ค€ ํฌ๊ธฐ์™€\n * ์ผ๊ณฑ ๊ฐ€์ง€ ํ™•์žฅ ์ถ•์—์„œ ์‹ค์ œ FigureInstance๋กœ ์„ธ์šด๋‹ค. ๋”ฐ๋ผ์„œ ์ €์ž‘์ž๋Š” \"์ข‹์•„ ๋ณด์ด๋Š”\n * ํ•œ ์žฅ\"์ด ์•„๋‹ˆ๋ผ ๋ฐฐ์น˜๋  ๋•Œ๋„ ๋ถ™์–ด ์žˆ๋Š”์ง€๋ฅผ ๋ณด๊ณ  ์ˆ˜๋ฝํ•œ๋‹ค.\n */\n private renderProposed() {\n if (!this.proposalSession) return ''\n\n return html`<figure-preview .source=${this.proposalSession.source} .view=${this.view}></figure-preview>`\n }\n\n /**\n * ๋ฌด์—‡์„ ๋ฐ›์„์ง€ ๊ณ ๋ฅธ๋‹ค.\n *\n * ํ†ต์งธ๋กœ๋งŒ ๋ฐ›๊ฒŒ ๋‘๋ฉด ใ€Œ๋ชธํ†ต์€ ์ข‹์€๋ฐ ๋ชจํ„ฐ ์ž๋ฆฌ๊ฐ€ ํ‹€๋ ธ๋‹คใ€์— ๋‹ตํ•  ์ˆ˜ ์—†๋‹ค โ€” ๊ทธ๊ฒŒ\n * ๊ฐ€์žฅ ํ”ํ•œ ๋ฐ˜์‘์ด๋‹ค. ๋ณ€๊ฒฝ ํ•˜๋‚˜๊ฐ€ ์นฉ ํ•˜๋‚˜์ด๊ณ , ์ฒ˜์Œ์—๋Š” ์ „๋ถ€ ๊ณจ๋ผ ๋‘”๋‹ค. ํ†ต์งธ๋กœ\n * ๋ฐ›๋Š” ๊ฒƒ์ด ๋ณดํ†ต์˜ ๊ธธ์ด๋ฏ€๋กœ ๊ทธ๊ฒƒ์ด ํ•œ ๋ฒˆ ๋ˆ„๋ฅด๊ธฐ์—ฌ์•ผ ํ•œ๋‹ค.\n *\n * ๊ณ ๋ฅธ ์กฐํ•ฉ์„ **๋ฐ›๊ธฐ ์ „์— ์žฌ ๋ณธ๋‹ค.** ํ˜•์‹์„ ์–ด๊ธฐ๋ฉด ๋ชป ๋ˆ„๋ฅด๊ฒŒ ํ•˜๊ณ , ์ •์ฑ…์„ ๋„˜์œผ๋ฉด\n * ์•Œ๋ฆฐ๋‹ค. ๊ณจ๋ผ ๋ฐ›์œผ๋ฉด ํ›„๋ณด๋ฅผ ๋‚ผ ๋•Œ์™€ ๋‹ค๋ฅธ ์กฐํ•ฉ์ด ๋˜๋ฏ€๋กœ ํ›„๋ณด์˜ ์ ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ\n * ๋ฏฟ์„ ์ˆ˜ ์—†๋‹ค โ€” ์˜ˆ๋ฅผ ๋“ค์–ด ๋“ฑ๊ธ‰์€ ์•ˆ ๋ฐ›๊ณ  ๋ถ€ํ’ˆ๋งŒ ๋ฐ›์œผ๋ฉด ๋ถ€ํ’ˆ ์ˆ˜ ํ•œ๋„๋ฅผ ๋„˜๋Š”๋‹ค.\n *\n * ํ˜•์‹ ์˜ค๋ฅ˜๊ฐ€ ์‹ค์ œ๋กœ ๋‚˜๋Š” ์กฐํ•ฉ์€ ์•„์ง ๋ณด์ง€ ๋ชปํ–ˆ๋‹ค. ๊ทธ๋ž˜๋„ ๋‘๋Š” ๊ฒƒ์€ ๊ฐ’์ด ๊ฑฐ์˜\n * ์•ˆ ๋“ค๊ณ , ์šฐ๋ฆฌ๊ฐ€ ์ƒ๊ฐ ๋ชป ํ•œ ์กฐํ•ฉ์„ ๋ง‰์•„ ์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.\n */\n private renderDecision() {\n if (!this.proposalSession) return ''\n\n const stale = this.proposalBaseSource !== undefined && this.proposalBaseSource !== JSON.stringify(this.folded)\n\n const changes = proposals.diffProposal(this.folded, this.proposalSession.source)\n const picked = proposals.applyProposal(this.folded, this.proposalSession.source, this.proposalSession.picked)\n const { errors, violations } = validate(picked)\n\n return html`\n <div decide>\n <span>${this.proposalTitle(this.proposalSession)}</span>\n <div spacer></div>\n <button drop @click=${() => this.discard()}>${i18next.t('figure.button.discard')}</button>\n ${stale ? html`<span role=\"alert\">${i18next.t('figure.text.source-changed-ask-again')}</span>` : ''}\n <button take ?disabled=${stale || errors.length > 0 || this.proposalSession.picked.size === 0} @click=${() => this.take()}>\n ${i18next.t('figure.button.take-n-changes', { n: this.proposalSession.picked.size })}\n </button>\n </div>\n\n ${this.renderQualityReview()}\n <div proposal-note>\n <label>\n ${i18next.t('figure.text.proposal-feedback-note')}\n <input\n maxlength=\"500\"\n .value=${this.proposalSession.note}\n placeholder=${i18next.t('figure.text.proposal-feedback-placeholder')}\n @input=${(event: Event) => (this.proposalSession = { ...this.proposalSession!, note: (event.target as HTMLInputElement).value })}\n />\n </label>\n </div>\n\n ${changes.length > 0\n ? html`\n <div changes>\n ${changes.map(change => this.renderChange(change))}\n </div>\n `\n : ''}\n ${errors.length > 0\n ? html`<p blocked>${i18next.t('figure.text.picked-changes-do-not-validate')} โ€” ${errors[0].message}</p>`\n : violations.length > 0\n ? html`<p blocked warn>${violations[0].message}</p>`\n : ''}\n `\n }\n\n /** ํ›„๋ณด ํ’ˆ์งˆ ํŒ๋‹จ์˜ ๊ทผ๊ฑฐ๋ฅผ ์ˆ˜๋ฝ ์ „์— ์ „๋ถ€ ๋ณด์—ฌ ์ค€๋‹ค. */\n private renderQualityReview() {\n const quality = this.proposalSession?.metrics?.quality\n if (!quality) return ''\n\n return html`\n <div quality-review ?review=${quality.status === 'review'}>\n <strong>${i18next.t('figure.label.candidate-review')}</strong>\n <span>${quality.summary}</span>\n <span> ยท top-view ${Math.round(quality.visualReadability * 100)}% ยท ${quality.visualRegions} regions</span>\n ${quality.findings.length > 0\n ? html`<ul>\n ${quality.findings.map(finding => html`<li><code>${finding.dimension}</code> โ€” ${finding.message}</li>`)}\n </ul>`\n : ''}\n </div>\n `\n }\n\n /** ๋ณ€๊ฒฝ ํ•˜๋‚˜. ๋ฌด์—‡์„ ํ•˜๊ฒ ๋‹ค๋Š” ๊ฒƒ์ธ์ง€ ๋ถ€ํ˜ธ๋กœ ๋จผ์ € ๋งํ•œ๋‹ค. */\n private renderChange(change: proposals.ProposalChange) {\n /*\n Every ChangeKind needs a row here. A kind the map does not answer draws an empty icon and a\n key with no name beside it, which reads as a broken row rather than a missing case โ€” the\n `satisfies` makes the compiler say so instead.\n */\n const icon = ({\n added: 'add',\n changed: 'edit',\n removed: 'remove',\n base: 'aspect_ratio',\n placement: 'vertical_align_bottom',\n detail: 'tune',\n styleKit: 'palette',\n animations: 'animation',\n parameters: 'tune',\n capabilities: 'hub',\n joints: 'settings_input_component'\n } satisfies Record<proposals.ChangeKind, string>)[change.kind]\n\n const on = this.proposalSession?.picked.has(change.key)\n\n return html`\n <button\n change\n ?on=${on}\n title=${i18next.t('figure.text.change-' + change.kind)}\n @click=${() => this.toggle(change.key)}\n >\n <md-icon>${icon}</md-icon>\n <span key>${change.key}</span>\n </button>\n `\n }\n\n private toggle(key: string) {\n if (this.proposalSession) this.proposalSession = proposalSessions.toggleProposalChange(this.proposalSession, key)\n }\n\n /**\n * ํ›„๋ณด๊ฐ€ ์™”๋‹ค.\n *\n * ์›๋ณธ ๋ฐ์ดํ„ฐ์— ๋„ฃ์ง€ ์•Š๋Š”๋‹ค. ๋‚˜๋ž€ํžˆ ๋‘๊ณ  ์ €์ž‘์ž๊ฐ€ ๊ณ ๋ฅธ๋‹ค. AI ๊ฐ€ ์›๋ณธ ๋ฐ์ดํ„ฐ์— ์ง์ ‘ ์“ฐ๋ฉด\n * ๋ฌด์—‡์ด ์ž๊ธฐ ๊ฒƒ์ด๊ณ  ๋ฌด์—‡์ด ๊ธฐ๊ณ„ ๊ฒƒ์ธ์ง€ ๋ชจ๋ฅด๊ฒŒ ๋œ๋‹ค.\n */\n private receive(proposal: FigureProposal) {\n this.proposalBaseSource = JSON.stringify(this.folded)\n this.proposalSession = proposalSessions.openAiProposal(this.folded, proposal)\n }\n\n /**\n * ๋ฐœํ–‰ ๊ฒŒ์ดํŠธ์˜ ๊ตฌ์กฐํ™”๋œ ์ˆ˜์ •์•ˆ ํ•˜๋‚˜๋ฅผ ํ˜„์žฌ ์ž‘์—… ์‚ฌ๋ณธ์—๋งŒ ๊ฒน์ณ ๋ณธ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์ค€ ๊ฐ’์€\n * `part.anchor.axis = to`๋ผ๋Š” ๋ฐ์ดํ„ฐ์ด์ง€ ์‚ฌ๋žŒ์ด ์ฝ์„ ๋ฌธ์žฅ์„ ๋‹ค์‹œ ํ•ด์„ํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹ˆ๋‹ค.\n * ์›๋ณธ `draft`/`parts`๋Š” ์—ฌ๊ธฐ์„œ ์ „ํ˜€ ๋ฐ”๊พธ์ง€ ์•Š๊ณ , ์•„๋ž˜์˜ `take()`๋งŒ ๋ฐ”๊พผ ์‚ฌ๋ณธ์„ ์ ์šฉํ•œ๋‹ค.\n */\n private previewSizingFix(fix: FigureGateFix) {\n if (!this.folded) return\n this.proposalBaseSource = JSON.stringify(this.folded)\n this.proposalSession = proposalSessions.openSizingFix(this.folded, fix)\n }\n\n /**\n * ์›ํด๋ฆญ ์ ์šฉ์€ ์„œ๋ฒ„๊ฐ€ ๋ชจ๋“  ํฌ๊ธฐ ๋ณ€๊ฒฝ case๋ฅผ ์žฌ๊ฒ€์‚ฌํ•ด ์•ˆ์ „ํ•˜๋‹ค๊ณ  ํ‘œ์‹œํ•œ ํ•œ-์•ต์ปค ํ›„๋ณด์—๋งŒ ์—ฐ๋‹ค.\n * ๊ทธ๋ž˜๋„ ์ €์žฅ์€ ์ €์ž‘์ž์˜ ๋ณ„๋„ ํ–‰์œ„๋‹ค. ์ž˜๋ชป ๋ˆŒ๋Ÿฌ๋„ DB ์ •๋ณธ์ด๋‚˜ ๋ฐœํ–‰ํŒ์€ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.\n */\n private applySizingFix(fix: FigureGateFix) {\n if (!fix.safe || !this.folded) return\n this.previewSizingFix(fix)\n if (!this.proposalSession) return\n this.take()\n }\n\n /**\n * ๊ณ ๋ฅธ ๋ณ€๊ฒฝ์„ ๋ฐ›์•„๋“ค์ธ๋‹ค.\n *\n * ํ›„๋ณด ๋น„๊ต๋Š” ์ €์žฅ ํ˜•์‹์œผ๋กœ ํ•œ๋‹ค โ€” ๋ถ€ํ’ˆ ์ด๋ฆ„์œผ๋กœ ์ง์ง“๊ณ  ๊ธฐ์ค€ ์น˜์ˆ˜ยท๋“ฑ๊ธ‰๊นŒ์ง€ ํ•จ๊ป˜ ๋ด์•ผ\n * ํ•˜๋ฏ€๋กœ ๊ทธ์ชฝ์ด ์ž์—ฐ์Šค๋Ÿฝ๋‹ค. ๋ฐ›์•„๋“ค์ธ ๊ฒฐ๊ณผ๋Š” ๋‹ค์‹œ ์”ฌ ๋ชจ๋ธ๋กœ ๋˜๋Œ๋ ค ํŽธ์ง‘์„ ์ด์–ด๊ฐ„๋‹ค.\n */\n private take() {\n if (!this.proposalSession) return\n if (this.proposalBaseSource !== undefined && this.proposalBaseSource !== JSON.stringify(this.folded)) return\n\n this.recordHistory()\n const taken = proposals.applyProposal(this.folded, this.proposalSession.source, this.proposalSession.picked)\n const { draft, parts } = fromFigureSource(taken)\n\n this.draft = draft\n this.parts = parts\n this.selected = parts.length ? 0 : -1\n this.dirty = true\n this.reportProposalFeedback('accepted')\n this.discard(false)\n }\n\n /**\n * ํ›„๋ณด์— ๋Œ€ํ•œ ์‚ฌ๋žŒ์˜ ๊ฒฐ์ •์„ ๋„ํฌ์—๋งŒ ์•Œ๋ฆฐ๋‹ค.\n *\n * ์ด ๊ธฐ๋ก์€ ๋ถ„์„ ์ด๋ฒคํŠธ๋‚˜ Figure ์†์„ฑ์ด ์•„๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ชจ๋ธ๋Ÿฌ ์„ธ์…˜์—์„œ ๋‹ค์Œ ์š”์ฒญ์„ ๋” ์ž˜\n * ์ดํ•ด์‹œํ‚ค๋Š” ์งง์€ ์‹ ํ˜ธ์ผ ๋ฟ์ด๋‹ค. ์›๋ณธ source๋‚˜ ์‚ฌ๋žŒ์˜ ์ž‘์—… ๋‚ด์šฉ์„ ๋‹ค์‹œ ๋ณด๋‚ด์ง€ ์•Š๊ณ , ์‚ฌ๋žŒ์ด\n * ํ›„๋ณด ๋ณ€๊ฒฝ ์ค‘ ๋ช‡ ๊ฐœ๋ฅผ ๋ฐ›์•„๋“ค์˜€๋Š”์ง€์™€ ์ด๋ฏธ ํ™”๋ฉด์— ๋ณด์˜€๋˜ ํ’ˆ์งˆ ํŒ์ •๋งŒ ๋ณด๋‚ธ๋‹ค.\n */\n private reportProposalFeedback(outcome: 'accepted' | 'discarded') {\n if (!this.proposalSession) return\n\n const feedback = proposalSessions.feedbackFromSession(this.folded, this.proposalSession, outcome)\n if (!feedback) return\n this.proposalFeedback = [...this.proposalFeedback, feedback].slice(-5)\n window.dispatchEvent(\n new CustomEvent('figure-ai-feedback', {\n detail: {\n figureId: this.figure?.id,\n ...feedback\n }\n })\n )\n }\n\n private discard(reportFeedback = true) {\n if (reportFeedback) this.reportProposalFeedback('discarded')\n this.proposalSession = undefined\n this.proposalBaseSource = undefined\n }\n\n /**\n * ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‚˜.\n *\n * ํ˜•์‹์„ ์–ด๊ธด ์›๋ณธ ๋ฐ์ดํ„ฐ๋กœ๋Š” ์ €์žฅ์„ ๋ˆ„๋ฅผ ์ˆ˜ ์—†๊ฒŒ ํ•œ๋‹ค. ๋ˆŒ๋ €๋‹ค๊ฐ€ ์„œ๋ฒ„๊ฐ€ ๋ฐ›์•„์ฃผ์ง€ ์•Š๋Š” ๊ฒƒ๋ณด๋‹ค,\n * ๋ˆ„๋ฅผ ์ˆ˜ ์—†๋Š” ์ด์œ ๊ฐ€ ํŒ๋‹จ ํŒจ๋„์— ๋–  ์žˆ๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค.\n *\n * ## ๋ฐ”๋€ ๊ฒƒ์ด ์—†์œผ๋ฉด ๋ˆ„๋ฅผ ์ˆ˜ ์—†๋‹ค\n *\n * **`dirty` ๋ฅผ ์•ˆ ๋ณด๊ณ  ์žˆ์—ˆ๋‹ค.** ์ €์žฅํ•œ ์งํ›„์—๋„ ๋‹จ์ถ”๊ฐ€ ์‚ด์•„ ์žˆ์–ด ๊ฐ™์€ ์ •๋ณธ์„ ๋ช‡ ๋ฒˆ์ด๊ณ  ๋‹ค์‹œ ์“ธ\n * ์ˆ˜ ์žˆ์—ˆ๊ณ , ์ €์žฅ๋งˆ๋‹ค ์ธ๋„ค์ผ์„ ์ƒˆ๋กœ ์ฐ์–ด 20KB ๋ฅผ ๋‹ค์‹œ ๋„ฃ๊ณ  `thumbnailUpdatedAt` ์„ ์˜ฌ๋ ธ๋‹ค โ€”\n * ๊ทธ๋ฆผ ์ฃผ์†Œ์˜ ํŒ์ด ๋ฐ”๋€Œ๋ฏ€๋กœ ๋ธŒ๋ผ์šฐ์ € ์บ์‹œ๋„ ํ•จ๊ป˜ ๋ฒ„๋ ค์ง„๋‹ค. ๋ฐ”๋€ ๊ฒƒ์ด ์—†๋Š”๋ฐ ์ผ์–ด๋‚  ์ผ์ด ์•„๋‹ˆ๋‹ค.\n *\n * ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ์ค‘์ธ ๊ฒƒ์€ `startNew` ๊ฐ€ `dirty` ๋กœ ์‹œ์ž‘ํ•˜๋ฏ€๋กœ ์ฒซ ์ €์žฅ์ด ๋ง‰ํžˆ์ง€ ์•Š๋Š”๋‹ค.\n * ์ €์žฅ์ด ๊ฑฐ์ ˆ๋˜๋ฉด `dirty` ๋ฅผ ๊ทธ๋Œ€๋กœ ๋‘๋ฏ€๋กœ ๋‹ค์‹œ ๋ˆ„๋ฅผ ์ˆ˜ ์žˆ๋‹ค.\n */\n private get canSave(): boolean {\n if (this.saving || !this.dirty || !this.folded) {\n return false\n }\n // ๊ธฐ์กด ์ €์žฅ๋œ ๋ชจ๋ธ์€ ์ด๋ฆ„์ด ์žˆ์–ด์•ผ ๋ฐ”๋กœ ์ €์žฅ ๊ฐ€๋Šฅ. ์‹ ๊ทœ ๋ชจ๋ธ์€ ์ €์žฅ ํŒ์—…์—์„œ ์ด๋ฆ„์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Œ\n if (this.figure?.id && !this.figure?.name) {\n return false\n }\n return validate(this.folded).errors.length === 0\n }\n\n async pageUpdated(changes: Record<string, unknown>, lifecycle: { resourceId?: string }) {\n if (!this.active) {\n ++this.loadGeneration\n return\n }\n\n const id = lifecycle?.resourceId\n\n if (id) {\n if (changes.active || id !== this.figure?.id) {\n await this.load(id)\n }\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n /*\n โ”€โ”€ id ์—†์ด ๋“ค์–ด์˜จ ๊ฒƒ์€ ใ€Œ์ƒˆ๋กœ ๋งŒ๋“ ๋‹คใ€๋Š” ๋œป์ด๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n ์ „์—๋Š” `!this.figure` ์ผ ๋•Œ๋งŒ ์ƒˆ๋กœ ์‹œ์ž‘ํ–ˆ๋‹ค. ์…ธ์€ ํŽ˜์ด์ง€๋ฅผ ์‚ด๋ ค ๋‘๋ฏ€๋กœ(MDI ํƒญ) ์นดํƒˆ๋กœ๊ทธ์—์„œ\n ํ•˜๋‚˜ ์—ด์–ด ๋ณธ ๋’ค์—๋Š” ์ด ํŽ˜์ด์ง€๊ฐ€ ๊ทธ ๋„ํ˜•์„ ๊ณ„์† ๋“ค๊ณ  ์žˆ๊ณ , ๊ทธ๋ž˜์„œ ใ€Œ์ƒˆ figureใ€๊ฐ€ **์•„๋ฌด ์ผ๋„\n ํ•˜์ง€ ์•Š์•˜๋‹ค.**\n\n ๊ทธ๊ฒƒ๋ณด๋‹ค ๋‚˜์œ ๊ฒƒ์ด ์žˆ์—ˆ๋‹ค โ€” ํ™”๋ฉด์—๋Š” ์˜› ๋„ํ˜•์ด ๊ทธ๋Œ€๋กœ ์žˆ์œผ๋‹ˆ ์‚ฌ๋žŒ์€ ์ƒˆ ๊ฒƒ์ธ ์ค„ ์•Œ๊ณ  ๊ณ ์น˜๊ณ \n ์ €์žฅํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด **์—ด๋ ค ์žˆ๋˜ ๋„ํ˜•์— ๋ฎ์–ด์“ด๋‹ค.** ใ€Œ์•ˆ ์—ด๋ฆฐ๋‹คใ€๋กœ ๋ณด์ด๋˜ ๊ฒƒ์ด ์‹ค์€ ๊ทธ ๋ฌธ์ด์—ˆ๋‹ค.\n\n ๋“ค๊ณ  ์žˆ๋Š” ๊ฒƒ์ด **์ €์žฅ๋œ ๋„ํ˜•์ด๋ฉด**(id ๊ฐ€ ์žˆ์œผ๋ฉด) ์ƒˆ๋กœ ์‹œ์ž‘ํ•œ๋‹ค. ์•„์ง ์ €์žฅ ์•ˆ ํ•œ ์ƒˆ ๋„ํ˜•์„\n ๋“ค๊ณ  ์žˆ์œผ๋ฉด ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค โ€” ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋“ค๋ €๋‹ค๊ณ  ์ €์ž‘ ์ค‘์ธ ๊ฒƒ์„ ๋ฒ„๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n */\n if (!this.figure) {\n this.startNew()\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n if (this.figure.id) {\n this.startNew()\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n if (shouldStartNewFigure(changes, !!this.figure, this.figure?.id)) {\n this.startNew()\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n return\n }\n\n const pending = consumePendingFigureProposal()\n if (pending?.source) {\n this.openProposalForReview(pending.source, pending.proposal)\n }\n }\n\n private loadGeneration = 0\n private async load(id: string) {\n const generation = ++this.loadGeneration\n const figure = await fetchFigure(id)\n if (generation !== this.loadGeneration || !this.active) return\n\n this.proposalSession = undefined\n this.proposalBaseSource = undefined\n this.figure = figure\n\n /*\n ๊ทธ๋ฆผ์ด ์—†์œผ๋ฉด ์ด ์ž๋ฆฌ์—์„œ ์ฑ„์šด๋‹ค(์•„๋ž˜ `fillThumbnail`). ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค โ€” ํ™”๋ฉด์ด ๋จผ์ €๋‹ค.\n\n **`thumbnail` ์ด ์•„๋‹ˆ๋ผ `thumbnailUpdatedAt` ์œผ๋กœ ํŒ์ •ํ•œ๋‹ค.** ๊ทธ๋ฆผ ์ž์ฒด๋Š” ๋ชฉ๋กยท๋‚ฑ๊ฐœ ์งˆ์˜๊ฐ€\n ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋Š”๋‹ค(base64 ๋ผ์„œ ๋ฌด๊ฒ๊ณ  ์บ์‹œ๊ฐ€ ์•ˆ ๋œ๋‹ค โ€” ์ฃผ์†Œ๋กœ ๋ถ€๋ฅธ๋‹ค). ์—†๋Š” ์นธ์œผ๋กœ ํŒ์ •ํ•˜๋ฉด\n ๋Š˜ ใ€Œ๊ทธ๋ฆผ์ด ์—†๋‹คใ€๊ฐ€ ๋˜์–ด ์ €์ž‘๋ฉด์„ ์—ด ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ์ฐ์–ด ์ €์žฅํ•˜๊ฒŒ ๋œ๋‹ค.\n */\n if (!figure.thumbnailUpdatedAt && figure.id) {\n void this.fillThumbnail(figure.id)\n }\n\n // ์„œ๋ฒ„๊ฐ€ ์ค€ ๊ฒƒ์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค. ์ฝ์ง€ ๋ชปํ•˜๋ฉด ์˜ค๋ฅ˜๋ณด๊ณ ์—†์ด ๋นˆ ์ƒํƒœ๋กœ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.\n // ๊ทธ๋Ÿฌ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ €์žฅ์„ ์„ ํƒํ•ด ์›๋ณธ์„ ๋ฎ์–ด์“ด๋‹ค.\n const model = figure.source ? fromFigureSource(JSON.parse(figure.source) as FigureSource) : undefined\n this.draft = model?.draft\n this.parts = model?.parts ?? []\n\n /*\n ๋ฐœํ–‰๋œ ์ ์ด ์žˆ์œผ๋ฉด ๊ทธ ํŒ์„ ๊ฐ€์ ธ๋‹ค ๋‘”๋‹ค โ€” ๊ณ ์น˜๋Š” ๋™์•ˆ ๊ฒฌ์ค„ ์ƒ๋Œ€๋‹ค. ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค:\n ๋„ํ˜•์€ ์ด๋ฏธ ํ™”๋ฉด์— ์žˆ๊ณ , ํŒ์ •์€ ๋„์ฐฉํ•˜๋Š” ๋Œ€๋กœ ๋ถ™๋Š”๋‹ค.\n */\n this.releasedSource = undefined\n if (figure.id && (figure.version ?? 0) > 0) {\n void this.loadReleased(figure.id, generation)\n }\n\n /*\n **์•„๋ฌด๊ฒƒ๋„ ๊ณ ๋ฅด์ง€ ์•Š์€ ์ฑ„๋กœ ์—ฐ๋‹ค.**\n\n ์ „์—๋Š” ์ฒซ ๋ถ€ํ’ˆ์„ ์žก์•„ ๋‘์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๋ฅธ ์  ์—†๋Š” ๋ถ€ํ’ˆ์˜ ์†์„ฑ์ด ๋–  ์žˆ๊ณ ,\n ์บ”๋ฒ„์Šค์—๋Š” ๊ทธ ๋ถ€ํ’ˆ์˜ ์œค๊ณฝ์„ ์ด ๊ทธ๋ ค์ง„๋‹ค. ๋ชจ๋ธ ์ „์ฒด๋ฅผ ๋ณด๋ ค๋ฉด ๋จผ์ € ์„ ํƒ์„ ํ’€์–ด์•ผ ํ–ˆ๋‹ค.\n\n ์†์„ฑ ํŒจ๋„์˜ ๋นˆ ์ƒํƒœ๋„ ๊ทธ๋ž˜์„œ **์—ด์—ˆ์„ ๋•Œ ํ•œ ๋ฒˆ๋„ ์•ˆ ๋ณด์˜€๋‹ค.** ์ฒ˜์Œ ์—ฌ๋Š” ์‚ฌ๋žŒ์—๊ฒŒ\n ใ€Œ์™ผ์ชฝ์—์„œ ๋ถ€ํ’ˆ์„ ๊ณ ๋ฅด๋Š” ๋„๊ตฌใ€๋ผ๊ณ  ์•Œ๋ ค ์ค„ ์ž๋ฆฌ๊ฐ€ ๊ทธ ํ™”๋ฉด์ด๋‹ค.\n\n ์ƒˆ๋กœ ๋งŒ๋“ค ๋•Œ(`startNew`)๋Š” ๋‹ค๋ฅด๋‹ค. ๋ฐฉ๊ธˆ ๋ถ€ํ’ˆ์„ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ๋ฐ”๋กœ ํŽธ์ง‘ํ•˜๋ ค๋Š” ๊ฒƒ์ด ๋งž๋‹ค.\n */\n this.selected = -1\n this.dirty = false\n this.violations = []\n this.undoStack = []\n this.redoStack = []\n this.lastCoalesceKey = undefined\n }\n\n /** ์ง์ „ ๋ฐœํ–‰ํŒ์„ ๊ฐ€์ ธ๋‹ค ๋‘”๋‹ค. ๋ชป ์ฝ์œผ๋ฉด ๊ฒฌ์ฃผ์ง€ ์•Š๋Š”๋‹ค โ€” ์—†๋Š” ํŒ์ •์„ ์ง€์–ด๋‚ด์ง€ ์•Š๋Š”๋‹ค. */\n private async loadReleased(id: string, generation: number) {\n try {\n const source = await fetchReleasedSource(id)\n if (generation !== this.loadGeneration || !this.active) return\n this.releasedSource = source ? (JSON.parse(source) as FigureSource) : undefined\n } catch (e) {\n console.warn(`[figure] ์ง์ „ ๋ฐœํ–‰ํŒ์„ ์ฝ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ${(e as Error).message}`)\n }\n }\n\n private startNew() {\n ++this.loadGeneration\n this.proposalSession = undefined\n this.proposalBaseSource = undefined\n this.proposalFeedback = []\n this.mode = 'edit'\n // ํƒ€์ž… ์ด๋ฆ„์„ ์‚ฌ๋žŒ์ด ์ •ํ•˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์ž„์‹œ ์ด๋ฆ„์„ ์“ด๋‹ค. ์ €์žฅํ•  ๋•Œ ํ™•์ •ํ•œ๋‹ค.\n const draftType = placeholderType()\n const { draft, parts } = startingModel(draftType)\n\n this.figure = { id: '', type: draftType, name: '' } as Figure\n this.draft = draft\n this.parts = parts\n /* ๊ณ ๋ฅธ ๊ฒƒ์ด ์—†๋‹ค โ€” ๋ถ€ํ’ˆ์ด ์—†์œผ๋‹ˆ ์†์„ฑ ํŒจ๋„๋„ ๊ณ ๋ฅผ ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. */\n this.selected = -1\n this.dirty = true\n this.violations = []\n this.undoStack = []\n this.redoStack = []\n this.lastCoalesceKey = undefined\n }\n\n private rename(name: string) {\n if (!this.figure) return\n\n this.figure = { ...this.figure, name }\n this.dirty = true\n }\n\n /**\n * ํƒ€์ž…์„ ๊ณ ์นœ๋‹ค โ€” **๋ฐœํ–‰ ์ „๊นŒ์ง€๋งŒ**(ADR-0063 ๊ฒฐ์ • 4).\n *\n * ์ „์—๋Š” ์ €์žฅ๋œ ๋„ํ˜•์ด๋ฉด ๋ฌด์กฐ๊ฑด ๋Œ์•„์„ฐ๋‹ค. ๊ทธ๋ž˜์„œ ๊ธฐ๊ณ„๊ฐ€ ์ง€์€ ์ด๋ฆ„์ด ๊ทธ๋Œ€๋กœ ๊ตณ์—ˆ๋‹ค. ๋ฐœํ–‰๋œ\n * ๋’ค์—๋Š” ๋ฌธ์ด ๊ฑฐ์ ˆํ•˜๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ๋„ ๋ง‰๋Š”๋‹ค โ€” ๋ˆŒ๋Ÿฌ์„œ ๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ณ  ์ €์žฅ์—์„œ ์‹คํŒจํ•˜๋ฉด\n * ์ €์ž‘์ž๊ฐ€ ๋ฌด์—‡์„ ์ž˜๋ชปํ–ˆ๋Š”์ง€ ๋ชป ์ฝ๋Š”๋‹ค.\n */\n private typeChanged(type: string) {\n if (!type || (this.figure?.version ?? 0) > 0) return\n const sanitized = type.replace(/[^A-Za-z0-9_]/g, '_').toUpperCase()\n if (this.draft) this.draft.figureType = sanitized\n this.figure = { ...this.figure, type: sanitized } as Figure\n this.folded = this.draft ? toFigureSource(this.draft, this.parts) : undefined\n this.dirty = true\n }\n\n /**\n * ๋ถ„๋ฅ˜์™€ ํƒœ๊ทธ๋ฅผ ๊ณ ์นœ๋‹ค.\n *\n * ์ •๋ณธ(`source`)์ด ์•„๋‹ˆ๋ผ **์นดํƒˆ๋กœ๊ทธ์˜ ๊ฐ’**์ด๋‹ค โ€” ๋„ํ˜•์ด ๋ฌด์—‡์œผ๋กœ ์ƒ๊ฒผ๋Š”์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ์–ด๋””์—\n * ๊ฑธ๋ฆฌ๋Š”์ง€๋‹ค. ๊ทธ๋ž˜์„œ ์ ์ˆ˜๋„ ๋ฐœํ–‰๋„ ํ”๋“ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.\n */\n private catalog(patch: { category?: string; tags?: string[] }) {\n if (!this.figure) return\n\n this.figure = { ...this.figure, ...patch }\n this.dirty = true\n }\n\n private snapshotState(): { draft?: FigureDraft; parts: PartModel[]; selected: number } {\n return {\n draft: this.draft ? structuredClone(this.draft) : undefined,\n parts: structuredClone(this.parts),\n selected: this.selected\n }\n }\n\n private recordHistory(coalesceKey?: string) {\n const now = Date.now()\n if (coalesceKey && this.lastCoalesceKey === coalesceKey && now - this.lastHistoryTime < 600) {\n this.lastHistoryTime = now\n return\n }\n\n this.lastCoalesceKey = coalesceKey\n this.lastHistoryTime = now\n\n this.undoStack = [...this.undoStack.slice(-49), this.snapshotState()]\n this.redoStack = []\n }\n\n private undo() {\n if (this.undoStack.length === 0) return\n const prev = this.undoStack[this.undoStack.length - 1]\n this.undoStack = this.undoStack.slice(0, -1)\n this.redoStack = [this.snapshotState(), ...this.redoStack.slice(0, 49)]\n\n this.draft = prev.draft ? structuredClone(prev.draft) : undefined\n this.parts = structuredClone(prev.parts)\n this.selected = this.parts.length ? Math.max(-1, Math.min(prev.selected, this.parts.length - 1)) : -1\n this.dirty = true\n this.lastCoalesceKey = undefined\n }\n\n private redo() {\n if (this.redoStack.length === 0) return\n const next = this.redoStack[0]\n this.redoStack = this.redoStack.slice(1)\n this.undoStack = [...this.undoStack.slice(-49), this.snapshotState()]\n\n this.draft = next.draft ? structuredClone(next.draft) : undefined\n this.parts = structuredClone(next.parts)\n this.selected = this.parts.length ? Math.max(-1, Math.min(next.selected, this.parts.length - 1)) : -1\n this.dirty = true\n this.lastCoalesceKey = undefined\n }\n\n /**\n * ๋ถ€ํ’ˆ ๋ชฉ๋ก์„ ๊ฐˆ์•„ ๋ผ์šด๋‹ค.\n *\n * ํŽธ์ง‘ ๊ทœ์น™์€ `part-edits` ๊ฐ€ ๊ฐ–๋Š”๋‹ค โ€” ํ™”๋ฉด๊ณผ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ™์€ ๊ฒƒ์„ ์“ฐ๊ฒŒ ํ•˜๋ ค๊ณ  ๋–ผ์–ด\n * ๋†“์•˜๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋‹ด๊ณ  ์„ ํƒ ์ธ๋ฑ์Šค๋ฅผ ์ •๋ฆฌํ•œ๋‹ค.\n */\n private put(parts: PartModel[], selected = this.selected, coalesceKey?: string) {\n this.recordHistory(coalesceKey)\n this.parts = parts\n this.selected = Math.min(selected, parts.length - 1)\n this.dirty = true\n }\n\n private addPart(primitive: PrimitiveKind) {\n if (!this.draft) return\n /*\n ๊ณ ๋ฅธ ๋ถ€ํ’ˆ ์œ„์— ์–น๋Š”๋‹ค โ€” ๊ทธ ์ž๋ฆฌ์—์„œ ๋ณด์ด๊ณ , ๊ด€์ ˆ์„ ๊ฑธ๋ฉด ๋งž๋‹ฟ๋Š” ๋ฉด์ด ํšŒ์ „ ์ค‘์‹ฌ์ด ๋œ๋‹ค.\n ๊ณ ๋ฅธ ๊ฒƒ์ด ์—†์œผ๋ฉด `newPart` ๊ฐ€ ๋ฐ”๋‹ฅ ํ•œ๊ฐ€์šด๋ฐ๋กœ ๋†“๋Š”๋‹ค.\n */\n const onTopOf = this.parts[this.selected]\n // ์ถ”๊ฐ€ํ•œ ๊ฒƒ์„ ๋ฐ”๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์„ ํƒ ์ƒํƒœ๋กœ ๋‘”๋‹ค.\n this.put(edits.addPart(this.draft, this.parts, primitive, onTopOf), this.parts.length)\n }\n\n private removePart(index: number) {\n this.put(edits.removePart(this.parts, index), Math.max(0, index - 1))\n }\n\n private duplicatePart(index: number) {\n this.put(edits.duplicatePart(this.parts, index), index + 1)\n }\n\n private movePart(index: number, to: number) {\n this.put(edits.movePart(this.parts, index, to), to)\n }\n\n private updatePart(index: number, part: PartModel) {\n this.put(edits.updatePart(this.parts, index, part), this.selected, `part:${part.name || index}`)\n }\n\n /**\n * ํŒ ์ž์ฒด์˜ ๊ฐ’์„ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ๋Šฅ๋ ฅ์ฒ˜๋Ÿผ ๋ถ€ํ’ˆ๋ณด๋‹ค ์œ„์— ์žˆ๋Š” ๊ฒƒ๋“ค.\n *\n * ๋ถ€ํ’ˆ ๋ชฉ๋ก์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค. `put` ์€ ๋ถ€ํ’ˆ์„ ํ•จ๊ป˜ ๋ฐ›์œผ๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ์“ฐ๋ฉด ๋Šฅ๋ ฅ ํ•˜๋‚˜๋ฅผ\n * ๊ณ ์น  ๋•Œ๋งˆ๋‹ค ๋ถ€ํ’ˆ ๋ชฉ๋ก์ด ์ƒˆ ๋ฐฐ์—ด์ด ๋˜๊ณ , ๊ทธ๋ฆฌ๋Š” ์ชฝ์ด ์ „๋ถ€ ๋‹ค์‹œ ๊ทธ๋ฆฐ๋‹ค.\n */\n /**\n * ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ์„ธ์šด ์ž์„ธ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์•‰ํžŒ๋‹ค.\n *\n * ๊ด€์ ˆ์ด ์žˆ๋Š” ๋„ํ˜•์€ ๊ฐ’์ด 0 ์ธ ์ž์„ธ(ํœด์ง€ ์ž์„ธ)๊ฐ€ ๊ณง๊ฒŒ ์„  ๊ธฐ๋‘ฅ์ด๋ผ, ๊ธฐ๋ณธ๊ฐ’์ด ์—†์œผ๋ฉด ๋ชฉ๋ก๊ณผ\n * ๋„๋ฉด์—์„œ ๊ทธ ๋ชจ์–‘์œผ๋กœ ์„ ๋‹ค. ์ €์ž‘์ž๊ฐ€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ๋งž์ถ˜ ์ž์„ธ๊ฐ€ ๊ณง ๊ทธ ๋‹ต์ด๋‹ค.\n */\n private poseAsDefault(values: Record<string, number>) {\n if (!this.draft) return\n\n const next = poseAsDefaults(this.draft, values)\n /* ์•‰ํž ๊ฒƒ์ด ์—†์œผ๋ฉด ์ดˆ์•ˆ์ด ๊ทธ๋Œ€๋กœ ์˜จ๋‹ค. ๊ทธ๋•Œ ๋˜๋Œ๋ฆฌ๊ธฐ ์นธ๊ณผ ใ€Œ์ €์žฅ ์•ˆ ๋จใ€์„ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค. */\n if (next !== this.draft) this.updateDraft(next)\n }\n\n /**\n * ๊ธฐ์ค€ ์ƒ์ž๋ฅผ ๊ณ ์นœ๋‹ค โ€” ์ดˆ์•ˆ๊ณผ ๋ถ€ํ’ˆ์ด **ํ•œ ๊ฑธ์Œ์œผ๋กœ** ๋ฐ”๋€๋‹ค.\n *\n * ์ƒ์ž๊ฐ€ ์ปค์ง€๋ฉด ํ•œ๊ฐ€์šด๋ฐ๊ฐ€ ์˜ฎ๊ฒจ ๊ฐ€๋ฏ€๋กœ ๋ถ€ํ’ˆ์„ ๊ทธ๋งŒํผ ๋˜๋ฐ€์–ด์•ผ ์ €์žฅ๋˜๋Š” ์ž๋ฆฌ๊ฐ€ ์œ ์ง€๋œ๋‹ค\n * (`base-box.ts`). ๊ทธ ๋‘˜์„ ๋”ฐ๋กœ ๊ธฐ๋กํ•˜๋ฉด ํ•œ ๋ฒˆ ๋˜๋Œ๋ ธ์„ ๋•Œ ๋ถ€ํ’ˆ์ด ๋ฐ˜์ฏค ๋ฐ€๋ฆฐ ํ™”๋ฉด์ด ๋‚จ๋Š”๋‹ค.\n */\n private resizeBase(draft: FigureDraft, parts: PartModel[]) {\n this.recordHistory('base')\n this.draft = draft\n this.parts = parts\n this.dirty = true\n }\n\n private updateDraft(draft: FigureDraft) {\n this.recordHistory('draft')\n this.draft = draft\n this.dirty = true\n }\n\n /**\n * ๋””ํ…Œ์ผ ๋“ฑ๊ธ‰์„ ๋ฐ”๊พผ๋‹ค.\n *\n * ์ด ๊ฐ’์€ ๋ถ€ํ’ˆ ์ˆ˜ ํ•œ๋„๋ฅผ ์ •ํ•˜๊ณ  ์„ฑ์ˆ™๋„์˜ ํ•œ ์ถ•์ด ๋œ๋‹ค. ๋ถ€ํ’ˆ๋ณด๋‹ค ์œ„์— ์žˆ๋Š” ๊ฐ’์ด๋ผ\n * ์ดˆ์•ˆ์— ๋‹ด๋Š”๋‹ค.\n *\n * ํ•œ๋„๋ฅผ ๋‚ด๋ ค์„œ ์ด๋ฏธ ๋„ฃ์€ ๋ถ€ํ’ˆ ์ˆ˜๋ณด๋‹ค ์ž‘์•„์งˆ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋•Œ ๋ถ€ํ’ˆ์„ ์ง€์šฐ์ง€ ์•Š๋Š”๋‹ค.\n * ๊ฒ€์ฆ์ด ์œ„๋ฐ˜์œผ๋กœ ์•Œ๋ฆฌ๊ณ , ์„ฑ์ˆ™๋„๊ฐ€ ๋“ฑ๊ธ‰์— ์•ˆ ๋งž๋Š”๋‹ค๊ณ  ๋งํ•œ๋‹ค. ์ €์ž‘์ž๊ฐ€ ๋ฌด์—‡์„ ๋บ„์ง€\n * ์ •ํ•  ์ผ์ด์ง€ ๋„๊ตฌ๊ฐ€ ์ •ํ•  ์ผ์ด ์•„๋‹ˆ๋‹ค.\n */\n private changeDetailLevel(level: DetailLevel) {\n if (!this.draft) return\n\n this.recordHistory('detailLevel')\n this.draft = { ...this.draft, detailLevel: level }\n this.dirty = true\n }\n\n /**\n * ๋ถ€ํ’ˆ์„ ์ž ๊น ๊ฐ€๋ฆฐ๋‹ค.\n *\n * ์”ฌ์ด ์ด๋ฏธ ๊ฐ–๊ณ  ์žˆ๋Š” ์ƒํƒœ๋ฅผ ์ผœ๊ณ  ๋„๋Š” ๊ฒƒ๋ฟ์ด๋‹ค โ€” ํŽธ์ง‘ ๋ชจ๋“œ์—์„œ๋Š” faint ๋กœ ๋ณด์ด๊ณ \n * ๋ทฐ ๋ชจ๋“œ์—์„œ๋Š” ์•ˆ ๊ทธ๋ ค์ง„๋‹ค. ์ €์žฅ ํ˜•์‹์—๋Š” ์‹ค๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์‹œ ์—ด๋ฉด ์ „๋ถ€ ๋ณด์ธ๋‹ค.\n *\n * `dirty` ๋กœ ์น˜์ง€ ์•Š๋Š”๋‹ค. ์ €์žฅ๋  ๊ฒƒ์ด ํ•˜๋‚˜๋„ ๋ฐ”๋€Œ์ง€ ์•Š์•˜๋Š”๋ฐ \"์ €์žฅํ•˜์ง€ ์•Š์€ ๋ณ€๊ฒฝ\"์ด\n * ๋œจ๋ฉด, ์ €์žฅ ๋ฒ„ํŠผ์ด ๋ฌด์—‡์„ ์ €์žฅํ•˜๊ฒ ๋‹ค๋Š” ๊ฒƒ์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.\n */\n private toggleHidden(index: number) {\n const part = this.parts[index]\n if (!part) return\n\n this.parts = edits.updatePart(this.parts, index, { ...part, hidden: !part.hidden })\n }\n\n /**\n * ํ•œ๊บผ๋ฒˆ์— ๊ฐ์ถ”๊ฑฐ๋‚˜ ๋˜๋Œ๋ฆฐ๋‹ค.\n *\n * ๋ถ€ํ’ˆ ์Šค๋ฌผ์งœ๋ฆฌ์—์„œ ์•ˆ์ชฝ ํ•˜๋‚˜๋ฅผ ๋ณด๋ ค๊ณ  ์—ด์•„ํ™‰์„ ๋ˆ„๋ฅด๋˜ ๊ฒƒ์„ ์—†์•ค๋‹ค. ํ•œ ๋ฒˆ์— ๋ฐ”๊พธ๋ฏ€๋กœ\n * ๋ชฉ๋ก์„ ํ•œ ๋ฒˆ๋งŒ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ํ•˜๋‚˜์”ฉ ์Šค๋ฌด ๋ฒˆ ๊ฐˆ๋ฉด ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ์Šค๋ฌด ๋ฒˆ ๋‹ค์‹œ ์„ ๋‹ค.\n */\n private setAllHidden(hidden: boolean) {\n this.parts = this.parts.map(part => (part.hidden === hidden ? part : { ...part, hidden }))\n }\n\n /**\n * ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ๊ธฐ์ฆˆ๋ชจ๋กœ ์˜ฎ๊ธฐ๊ฑฐ๋‚˜ 2D ์—์„œ ๋Œ์—ˆ๋‹ค.\n *\n * ์•Œ๋ ค ์˜ค๋Š” ๊ฒƒ์€ ๋ถ€ํ’ˆ **์ด๋ฆ„**์ด๋‹ค. ๋ชฉ๋ก์˜ ๋ช‡ ๋ฒˆ์งธ์ธ์ง€๋Š” ์—ฌ๊ธฐ์„œ ์ฐพ๋Š”๋‹ค โ€” ์ด๋ฒคํŠธ๊ฐ€\n * ์˜ค๋Š” ์‚ฌ์ด์— ๋ชฉ๋ก์ด ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด ์ฐพ์ง€ ๋ชปํ•˜๊ณ , ๊ทธ๋•Œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n */\n private partChanged(name: string, part: PartModel) {\n const at = this.parts.findIndex(item => item.name === name)\n if (at >= 0) this.put(edits.updatePart(this.parts, at, part), this.selected, `drag:${name}`)\n }\n\n /**\n * ์ •๋ฐ€ ๋ฐฐ์น˜ ๋„๊ตฌ.\n *\n * ๊ทœ์น™์€ `part-edits` ๊ฐ€ ๊ฐ–๋Š”๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์–ด๋А ๊ฒƒ์„ ๋ถ€๋ฅผ์ง€๋งŒ ์ •ํ•œ๋‹ค.\n */\n private runTool(index: number, tool: string, axis: PlaneAxis) {\n const draft = this.draft\n if (!draft) return\n\n if (tool === 'center') this.put(edits.centerPart(draft, this.parts, index, axis))\n else if (tool === 'floor') this.put(edits.alignPartToStart(this.parts, index, axis))\n else if (tool === 'snap') this.put(edits.snapPart(draft, this.parts, index))\n // ๋Œ€์นญ ๋ณต์ œ๋Š” ๋ถ€ํ’ˆ์ด ํ•˜๋‚˜ ๋Š˜์–ด๋‚œ๋‹ค โ€” ์ƒˆ๋กœ ์ƒ๊ธด ๊ฒƒ์„ ์„ ํƒ ์ƒํƒœ๋กœ ๋‘”๋‹ค\n else if (tool === 'mirror') this.put(edits.mirrorPart(draft, this.parts, index, axis), index + 1)\n }\n\n /**\n * ์บ”๋ฒ„์Šค์—์„œ ๋ถ€ํ’ˆ์„ ์„ ํƒํ–ˆ๋‹ค.\n *\n * ์บ”๋ฒ„์Šค๊ฐ€ ์•Œ๋ฆฌ๋Š” ๊ฒƒ์€ **์ด๋ฆ„**์ด๋‹ค. ๋ชฉ๋ก์˜ ๋ช‡ ๋ฒˆ์งธ์ธ์ง€๋Š” ์—ฌ๊ธฐ์„œ ์ฐพ๋Š”๋‹ค โ€” ์•Œ๋ฆผ์ด\n * ์˜ค๋Š” ์‚ฌ์ด์— ๋ชฉ๋ก์ด ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด ๋ชป ์ฐพ๊ณ , ๊ทธ๋•Œ๋Š” ๊ณ ๋ฅธ ๊ฒƒ์„ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.\n * ์—†๋Š” ์ž๋ฆฌ๋ฅผ ๊ณ ๋ฅด๋ฉด ์†์„ฑ ์นธ์ด ๋นˆ ์ฑ„๋กœ ์—ด๋ ค ใ€Œ์‚ฌ๋ผ์กŒ๋‚˜ใ€๋กœ ์ฝํžŒ๋‹ค.\n */\n /** ์ง€๊ธˆ ๊ณ ๋ฅธ ๋ถ€ํ’ˆ ์ด๋ฆ„. ์บ”๋ฒ„์Šค๊ฐ€ ์ธ๋ฑ์Šค๊ฐ€ ์•„๋‹ˆ๋ผ ์ด๋ฆ„์„ ๋ฐ›๋Š”๋‹ค. */\n private get pickedName(): string | undefined {\n return this.parts[this.selected]?.name\n }\n\n /**\n * ๊ณ ๋ฅธ ๋ถ€ํ’ˆ์— ๊ด€์ ˆ์ด ์žˆ์œผ๋ฉด **๊ทธ ๊ด€์ ˆ์ด ๋„๋Š” ์ž๋ฆฌ**๋ฅผ ์ €์ž‘๋ฉด์— ํ•จ๊ป˜ ๊ทธ๋ฆฐ๋‹ค(`joint-gizmo.ts`).\n *\n * ์ถ•์„ ๊ณ ๋ฅด๋Š” ์นธ์€ ์ด ํ™”๋ฉด์— ์žˆ๋Š”๋ฐ ๋„๋Š” ๋ชจ์Šต์€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—๋งŒ ์žˆ์—ˆ๋‹ค. ๊ณ ๋ฅด๋Š” ์ž๋ฆฌ์™€ ๋ณด์ด๋Š”\n * ์ž๋ฆฌ๊ฐ€ ๊ฐˆ๋ฆฌ๋ฉด ์‚ฌ๋žŒ์€ ์™•๋ณตํ•˜๋ฉฐ ๋งž์ถ˜๋‹ค โ€” ์—ฌ์„ฏ ์ถ•์งœ๋ฆฌ ๋กœ๋ด‡์ด๋ฉด ์—ฌ์„ฏ ๋ฒˆ์ด๋‹ค.\n */\n private get jointGizmo(): PartModel | undefined {\n const name = this.pickedName\n if (!name || !this.draft) return undefined\n\n const joint = jointOf(this.draft, name)\n if (!joint) return undefined\n\n return gizmoPart(joint, this.parts.find(part => part.name === name), this.draft)\n }\n\n private pickPart(name: string) {\n const at = this.parts.findIndex(part => part.name === name)\n if (at >= 0) this.selected = at\n }\n\n /**\n * ๊ทธ๋ฆผ์ด ์—†๋Š” ๋„ํ˜•์„ **์—ด์—ˆ์„ ๋•Œ ํ•œ ๋ฒˆ** ์ฐ์–ด ์ฑ„์šด๋‹ค.\n *\n * ## ์™œ ์—ฌ๊ธฐ์ธ๊ฐ€\n *\n * ๋„๋ฉด์€ ์ €์žฅํ•  ๋•Œ ์„œ๋ฒ„๊ฐ€ ์ฐ๋Š”๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐฑ์—”๋“œ์— GPU ๊ฐ€ ์—†๋Š” ๋ฐฐํฌ๊ฐ€ ๋งŽ์•„ ํ—ค๋“œ๋ฆฌ์Šค๋กœ 3D ๋ฅผ ์ฐ๋Š”\n * ๊ฒƒ์ด ์ž์ฃผ ์‹คํŒจํ•œ๋‹ค. ๋„ํ˜•์„ ๊ทธ๋ฆฌ๋Š” GPU ๋Š” ์ด๋ฏธ ๋ณด๋Š” ์‚ฌ๋žŒ ์†์— ์žˆ๊ณ , ์”ฌ์€ ์ง€๊ธˆ ์—ฌ๊ธฐ ์„œ ์žˆ๋‹ค.\n *\n * ์ €์ž‘๋ฉด์„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ์ƒ๊ธด ๊ฒƒ๋“ค์ด ๊ทธ๋ฆผ์ด ์—†๋‹ค โ€” ์”จ์•—์œผ๋กœ ๋„ฃ์€ ํ‘œ๋ณธ, AI ๊ฐ€ ๋‚ธ ํ›„๋ณด, ๋ฐ˜์ž…ํ•œ ์ž์‚ฐ.\n * ๊ทธ๊ฒƒ๋“ค์„ ํ•œ ๋ฒˆ ์—ด์–ด ๋ณด๋ฉด ๊ทธ๋•Œ ์ฑ„์›Œ์ง„๋‹ค.\n *\n * ## ์ €์žฅ์ด ์•„๋‹ˆ๋‹ค\n *\n * ๊ทธ๋ฆผ๋งŒ ๋ณด๋‚ธ๋‹ค. ์ •๋ณธ์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฏธ์ €์žฅ ํ‘œ์‹œ๋„ ์ผœ์ง€ ์•Š๋Š”๋‹ค โ€” ์—ด์–ด ๋ดค๋‹ค๋Š” ๊ฒƒ์ด ๊ณ ์ณค๋‹ค๋Š”\n * ๋œป์ด ๋˜๋ฉด ์ €์ž‘์ž๊ฐ€ ์ €์žฅํ• ์ง€ ๋ง์ง€๋ฅผ ๋งค๋ฒˆ ๊ณ ๋ฏผํ•œ๋‹ค.\n */\n /**\n * ๋ฐœํ–‰๋˜์—ˆ๋‹ค โ€” **์ •๋ณธ์„ ๋“  ์ž๋ฆฌ๊ฐ€ ๊ทธ ์‚ฌ์‹ค์„ ๋ฐ›๋Š”๋‹ค.**\n *\n * ์ด๋ ฅ ํŒจ๋„์€ ๋ฐœํ–‰์„ ์‹œํ‚ค๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์•Œ๋ฆด ๋ฟ, ์ด ํŽ˜์ด์ง€์˜ ์ƒํƒœ๋ฅผ ๊ณ ์ณ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. ํŒ ๋ฒˆํ˜ธ์™€ ์ƒํƒœ๊ฐ€\n * ์—ฌ๊ธฐ์„œ ๊ฐฑ์‹ ๋˜์–ด์•ผ ๋จธ๋ฆฌ์ค„ยท์ด๋ ฅ ํƒญ์ด ๊ฐ™์€ ๊ฒƒ์„ ๋ณธ๋‹ค.\n *\n * ์ •๋ณธ(source)์€ ๋ฐœํ–‰์ด ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š๋Š”๋‹ค โ€” ์ €์ž‘ ์ค‘์ธ ํ™”๋ฉด์„ ๋˜๊ฐ์ง€ ์•Š๋Š”๋‹ค.\n */\n private released(figure: Figure): void {\n if (!this.figure || figure?.id !== this.figure.id) {\n return\n }\n\n this.figure = { ...this.figure, version: figure.version, state: figure.state, updatedAt: figure.updatedAt }\n }\n\n private async fillThumbnail(id: string): Promise<void> {\n await this.updateComplete\n\n const thumbnail = await captureThumbnail(this.draft, this.parts)\n if (!thumbnail || this.figure?.id !== id) {\n return\n }\n\n try {\n const { figure } = await updateFigure(id, { thumbnail })\n /* ์„œ๋ฒ„๊ฐ€ ์ฐ์€ ์‹œ๊ฐ์„ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค โ€” ์ด ๊ฐ’์ด ๊ทธ๋ฆผ์„ ๋ถ€๋ฅด๋Š” ์ฃผ์†Œ์˜ ํŒ ๋ฒˆํ˜ธ๊ฐ€ ๋œ๋‹ค. */\n this.figure = { ...this.figure, thumbnailUpdatedAt: figure?.thumbnailUpdatedAt }\n } catch (e) {\n /* ๋ณผ ๊ถŒํ•œ๋งŒ ์žˆ๋Š” ์‚ฌ๋žŒ์ผ ์ˆ˜ ์žˆ๋‹ค. ํ™”๋ฉด์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์กฐ์šฉํžˆ ๋„˜์–ด๊ฐ„๋‹ค. */\n console.warn(`[thumbnail] ${id} ๋ฅผ ์ฑ„์šฐ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ${(e as Error).message}`)\n }\n }\n\n private save() {\n if (!this.canSave || !this.folded || !this.figure) {\n return\n }\n\n if (!this.figure.id) {\n // ์‹ ๊ทœ ์ €์žฅ: ํƒ€์ž… ์ฝ”๋“œ์™€ ํ‘œ์‹œ ์ด๋ฆ„์„ ๋ช…ํ™•ํžˆ ํŒ์—…์œผ๋กœ ํ™•์ธ/์ž…๋ ฅ๋ฐ›์Œ\n let defaultType = this.draft?.figureType || this.figure.type || ''\n /*\n A placeholder is not prefilled. This used to prefill 'FIGURE', inviting the author to confirm a\n meaningless name that cannot change later and that the release gate refuses.\n */\n if (!defaultType || isPlaceholderType(defaultType)) {\n defaultType = ''\n }\n this.saveModalType = defaultType\n this.saveModalName = this.figure.name || ''\n this.saveModalError = ''\n this.showSaveModal = true\n return\n }\n\n void this.doSave()\n }\n\n private async doSave() {\n if (!this.folded || !this.figure) return\n\n this.saving = true\n this.saveFailure = ''\n this.saveModalError = ''\n\n try {\n if (!this.figure.id) {\n const typeToSave = (this.saveModalType || this.draft?.figureType || this.figure.type || 'FIGURE').trim().toUpperCase()\n const nameToSave = (this.saveModalName || this.figure.name || typeToSave).trim()\n\n if (!typeToSave) {\n throw new Error(i18next.t('figure.text.enter-a-type-name'))\n }\n\n const taken = await fetchFigureTypeNames()\n if (taken.includes(typeToSave)) {\n throw new Error(i18next.t('figure.text.type-name-already-taken', { type: typeToSave }))\n }\n\n if (this.draft) this.draft.figureType = typeToSave\n this.figure = { ...this.figure, type: typeToSave, name: nameToSave }\n this.folded = { ...this.folded, type: typeToSave }\n const sourceJson = JSON.stringify(this.folded)\n const thumbnail = await captureThumbnail(this.draft, this.parts)\n\n const result = await createFigure({\n type: typeToSave,\n name: nameToSave,\n category: this.figure.category,\n tags: this.figure.tags,\n source: sourceJson,\n thumbnail\n })\n\n this.figure = result.figure\n this.violations = result.violations\n this.showSaveModal = false\n this.dirty = false\n navigate(`figure-modeller/${result.figure.id}`)\n return\n }\n\n // ๊ธฐ์กด ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ: folded.type์ด figure.type๊ณผ ์ผ์น˜ํ•˜๋„๋ก ๋ณด์žฅ\n if (this.figure.type && this.folded.type !== this.figure.type) {\n this.folded = { ...this.folded, type: this.figure.type }\n if (this.draft) this.draft.figureType = this.figure.type\n }\n const sourceJson = JSON.stringify(this.folded)\n const thumbnail = await captureThumbnail(this.draft, this.parts)\n\n const result = await updateFigure(this.figure.id, {\n /*\n ํƒ€์ž…์„ ํ•จ๊ป˜ ๋ณด๋‚ธ๋‹ค. ์ •๋ณธ ์•ˆ์˜ `type` ๋งŒ ๊ณ ์น˜๊ณ  ํ–‰์„ ์•ˆ ๊ณ ์น˜๋ฉด ์ €์žฅ๋œ JSON ๊ณผ ํ–‰์ด\n ๋‹ค๋ฅธ ์ด๋ฆ„์„ ๋งํ•˜๊ณ , ๋ฐœํ–‰ํ•  ๋•Œ ์–ด๋А ์ชฝ์ด ๋„๋ฉด์— ์ ํžˆ๋Š”์ง€๊ฐ€ ๊ฐˆ๋ฆฐ๋‹ค. ๋ฐœํ–‰๋œ ๋’ค๋ผ๋ฉด\n ๋ฌธ์ด ๊ฑฐ์ ˆํ•˜๊ณ  ๊ทธ ์‚ฌ์œ ๊ฐ€ ํ™”๋ฉด์— ๊ทธ๋Œ€๋กœ ๋œฌ๋‹ค.\n */\n type: this.figure.type,\n name: this.figure.name,\n category: this.figure.category,\n tags: this.figure.tags,\n source: sourceJson,\n thumbnail\n })\n this.figure = result.figure\n this.violations = result.violations\n this.dirty = false\n } catch (e) {\n const msg = (e as Error).message\n this.saveFailure = msg\n this.saveModalError = msg\n } finally {\n this.saving = false\n }\n }\n\n /** Title of the candidate bar. A candidate opened from a release-check fix is described by translating that fix here. */\n private proposalTitle(session: proposalSessions.ProposalSession): string {\n const fix = session.fix\n if (fix) {\n return i18next.t('figure.text.release-check-fix', {\n part: fix.part,\n axis: fix.axis.toUpperCase(),\n from: fix.from ?? 'auto',\n to: fix.to\n })\n }\n return session.title || i18next.t('figure.text.assistant-proposed-a-figure')\n }\n\n private renderSaveModal() {\n if (!this.showSaveModal) return nothing\n\n return html`\n <div class=\"save-modal-backdrop\" @click=${(e: Event) => { if (e.target === e.currentTarget) this.showSaveModal = false }}>\n <div class=\"save-modal-card\" role=\"dialog\" aria-modal=\"true\">\n <h3 class=\"save-modal-title\">${i18next.t('figure.title.save-new-figure')}</h3>\n <p class=\"save-modal-desc\">${i18next.t('figure.text.save-new-figure-desc')}</p>\n\n <div class=\"save-modal-field\">\n <label>${i18next.t('figure.label.figure-type')}</label>\n <input\n .value=${this.saveModalType}\n placeholder=${i18next.t('figure.text.figure-type-example')}\n @input=${(e: Event) => (this.saveModalType = (e.target as HTMLInputElement).value.trim().toUpperCase())}\n />\n ${this.saveModalType && !releasableType(this.saveModalType)\n ? html`<div class=\"save-modal-error\">${i18next.t('figure.text.type-name-will-not-release')}</div>`\n : nothing}\n </div>\n\n <div class=\"save-modal-field\">\n <label>${i18next.t('figure.label.name')}</label>\n <input\n .value=${this.saveModalName}\n placeholder=${i18next.t('figure.text.figure-name-example')}\n @input=${(e: Event) => (this.saveModalName = (e.target as HTMLInputElement).value)}\n />\n </div>\n\n ${this.saveModalError ? html`<div class=\"save-modal-error\">${this.saveModalError}</div>` : nothing}\n\n <div class=\"save-modal-actions\">\n <button class=\"btn-cancel\" @click=${() => (this.showSaveModal = false)}>\n ${i18next.t('figure.button.cancel')}\n </button>\n <button class=\"btn-confirm\" ?disabled=${this.saving || !this.saveModalType} @click=${() => this.doSave()}>\n ${this.saving ? i18next.t('figure.text.saving-figure') : i18next.t('figure.button.save')}\n </button>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}