@things-factory/figure-ui 10.1.75 → 10.1.76
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.
- package/client/figure-assistant.ts +3 -3
- package/client/graphql/{v3-store.ts → store.ts} +6 -6
- package/client/modeller/{v3-ai-proposal.ts → ai-proposal.ts} +8 -8
- package/client/modeller/{v3-align.ts → align.ts} +14 -14
- package/client/modeller/{v3-canvas.ts → canvas.ts} +13 -13
- package/client/modeller/{v3-clips.ts → clips.ts} +5 -5
- package/client/modeller/{v3-controls.ts → controls.ts} +2 -2
- package/client/modeller/{v3-editor.ts → editor.ts} +117 -117
- package/client/modeller/{figure-v3-capability.ts → figure-capability-panel.ts} +7 -7
- package/client/modeller/{figure-v3-clips.ts → figure-clips.ts} +10 -10
- package/client/modeller/{figure-v3-controls.ts → figure-controls.ts} +9 -9
- package/client/modeller/{v3-figure-facts.ts → figure-facts.ts} +11 -11
- package/client/modeller/{figure-v3-first-save.ts → figure-first-save.ts} +3 -3
- package/client/modeller/figure-history-panel.ts +1 -1
- package/client/modeller/{figure-v3-links.ts → figure-links.ts} +35 -35
- package/client/modeller/{figure-v3-material.ts → figure-material-panel.ts} +20 -20
- package/client/modeller/{figure-v3-parts.ts → figure-parts.ts} +15 -15
- package/client/modeller/{figure-v3-preview.ts → figure-preview.ts} +14 -14
- package/client/modeller/{figure-v3-properties.ts → figure-properties.ts} +25 -25
- package/client/modeller/{figure-v3-recipe-catalog.ts → figure-recipe-catalog.ts} +10 -10
- package/client/modeller/{figure-v3-verdict.ts → figure-verdict.ts} +24 -24
- package/client/modeller/gate-findings.ts +2 -2
- package/client/modeller/{v3-gizmo.ts → gizmo.ts} +11 -11
- package/client/modeller/{v3-joint-drag.ts → joint-drag.ts} +11 -11
- package/client/modeller/{v3-joint-fan.ts → joint-fan.ts} +13 -13
- package/client/modeller/{v3-links.ts → links.ts} +59 -59
- package/client/modeller/{v3-live.ts → live.ts} +9 -9
- package/client/modeller/{v3-maturity.ts → maturity.ts} +65 -65
- package/client/modeller/modeller-route.ts +1 -1
- package/client/modeller/{v3-occupancy.ts → occupancy.ts} +6 -6
- package/client/modeller/{v3-palette.ts → palette.ts} +3 -3
- package/client/modeller/{v3-preview-model.ts → preview-model.ts} +8 -8
- package/client/modeller/{v3-product-session.ts → product-session.ts} +25 -25
- package/client/modeller/{v3-recipe-source.ts → recipe-source.ts} +2 -2
- package/client/modeller/{v3-recipes.ts → recipes.ts} +9 -9
- package/client/modeller/{v3-renderer.ts → renderer.ts} +16 -16
- package/client/modeller/{v3-stage-model.ts → stage-model.ts} +15 -15
- package/client/modeller/{v3-thumbnail.ts → thumbnail.ts} +8 -8
- package/client/pages/figure-list-page.ts +4 -4
- package/client/pages/{figure-v3-modeller-page.ts → figure-modeller-page.ts} +37 -37
- package/client/route.ts +1 -5
- package/dist-client/figure-assistant.js +3 -3
- package/dist-client/figure-assistant.js.map +1 -1
- package/dist-client/graphql/store.d.ts +3 -0
- package/dist-client/graphql/store.js +39 -0
- package/dist-client/graphql/store.js.map +1 -0
- package/dist-client/modeller/ai-proposal.d.ts +51 -0
- package/dist-client/modeller/ai-proposal.js +64 -0
- package/dist-client/modeller/ai-proposal.js.map +1 -0
- package/dist-client/modeller/align.d.ts +30 -0
- package/dist-client/modeller/align.js +75 -0
- package/dist-client/modeller/align.js.map +1 -0
- package/dist-client/modeller/canvas.d.ts +72 -0
- package/dist-client/modeller/canvas.js +379 -0
- package/dist-client/modeller/canvas.js.map +1 -0
- package/dist-client/modeller/clips.d.ts +39 -0
- package/dist-client/modeller/clips.js +58 -0
- package/dist-client/modeller/clips.js.map +1 -0
- package/dist-client/modeller/controls.d.ts +28 -0
- package/dist-client/modeller/controls.js +28 -0
- package/dist-client/modeller/controls.js.map +1 -0
- package/dist-client/modeller/editor.d.ts +203 -0
- package/dist-client/modeller/editor.js +1526 -0
- package/dist-client/modeller/editor.js.map +1 -0
- package/dist-client/modeller/fields.d.ts +45 -0
- package/dist-client/modeller/fields.js +83 -0
- package/dist-client/modeller/fields.js.map +1 -0
- package/dist-client/modeller/figure-capability-panel.d.ts +22 -0
- package/dist-client/modeller/figure-capability-panel.js +141 -0
- package/dist-client/modeller/figure-capability-panel.js.map +1 -0
- package/dist-client/modeller/figure-clips.d.ts +31 -0
- package/dist-client/modeller/figure-clips.js +268 -0
- package/dist-client/modeller/figure-clips.js.map +1 -0
- package/dist-client/modeller/figure-controls.d.ts +36 -0
- package/dist-client/modeller/figure-controls.js +226 -0
- package/dist-client/modeller/figure-controls.js.map +1 -0
- package/dist-client/modeller/figure-facts.d.ts +35 -0
- package/dist-client/modeller/figure-facts.js +55 -0
- package/dist-client/modeller/figure-facts.js.map +1 -0
- package/dist-client/modeller/figure-first-save.d.ts +20 -0
- package/dist-client/modeller/figure-first-save.js +118 -0
- package/dist-client/modeller/figure-first-save.js.map +1 -0
- package/dist-client/modeller/figure-history-panel.d.ts +1 -1
- package/dist-client/modeller/figure-history-panel.js.map +1 -1
- package/dist-client/modeller/figure-links.d.ts +77 -0
- package/dist-client/modeller/figure-links.js +649 -0
- package/dist-client/modeller/figure-links.js.map +1 -0
- package/dist-client/modeller/figure-material-panel.d.ts +39 -0
- package/dist-client/modeller/figure-material-panel.js +323 -0
- package/dist-client/modeller/figure-material-panel.js.map +1 -0
- package/dist-client/modeller/figure-parts.d.ts +69 -0
- package/dist-client/modeller/figure-parts.js +559 -0
- package/dist-client/modeller/figure-parts.js.map +1 -0
- package/dist-client/modeller/figure-preview.d.ts +34 -0
- package/dist-client/modeller/figure-preview.js +346 -0
- package/dist-client/modeller/figure-preview.js.map +1 -0
- package/dist-client/modeller/figure-properties.d.ts +45 -0
- package/dist-client/modeller/figure-properties.js +239 -0
- package/dist-client/modeller/figure-properties.js.map +1 -0
- package/dist-client/modeller/figure-recipe-catalog.d.ts +38 -0
- package/dist-client/modeller/figure-recipe-catalog.js +399 -0
- package/dist-client/modeller/figure-recipe-catalog.js.map +1 -0
- package/dist-client/modeller/figure-verdict.d.ts +64 -0
- package/dist-client/modeller/figure-verdict.js +787 -0
- package/dist-client/modeller/figure-verdict.js.map +1 -0
- package/dist-client/modeller/first-save.d.ts +17 -0
- package/dist-client/modeller/first-save.js +26 -0
- package/dist-client/modeller/first-save.js.map +1 -0
- package/dist-client/modeller/gate-findings.d.ts +2 -2
- package/dist-client/modeller/gate-findings.js +2 -2
- package/dist-client/modeller/gate-findings.js.map +1 -1
- package/dist-client/modeller/gizmo.d.ts +72 -0
- package/dist-client/modeller/gizmo.js +111 -0
- package/dist-client/modeller/gizmo.js.map +1 -0
- package/dist-client/modeller/help.d.ts +18 -0
- package/dist-client/modeller/help.js +37 -0
- package/dist-client/modeller/help.js.map +1 -0
- package/dist-client/modeller/joint-drag.d.ts +19 -0
- package/dist-client/modeller/joint-drag.js +97 -0
- package/dist-client/modeller/joint-drag.js.map +1 -0
- package/dist-client/modeller/joint-fan.d.ts +9 -0
- package/dist-client/modeller/joint-fan.js +276 -0
- package/dist-client/modeller/joint-fan.js.map +1 -0
- package/dist-client/modeller/keys.d.ts +22 -0
- package/dist-client/modeller/keys.js +26 -0
- package/dist-client/modeller/keys.js.map +1 -0
- package/dist-client/modeller/links.d.ts +267 -0
- package/dist-client/modeller/links.js +480 -0
- package/dist-client/modeller/links.js.map +1 -0
- package/dist-client/modeller/live.d.ts +37 -0
- package/dist-client/modeller/live.js +68 -0
- package/dist-client/modeller/live.js.map +1 -0
- package/dist-client/modeller/maturity.d.ts +120 -0
- package/dist-client/modeller/maturity.js +313 -0
- package/dist-client/modeller/maturity.js.map +1 -0
- package/dist-client/modeller/modeller-route.d.ts +1 -1
- package/dist-client/modeller/modeller-route.js +1 -1
- package/dist-client/modeller/modeller-route.js.map +1 -1
- package/dist-client/modeller/occupancy.d.ts +18 -0
- package/dist-client/modeller/occupancy.js +24 -0
- package/dist-client/modeller/occupancy.js.map +1 -0
- package/dist-client/modeller/palette.d.ts +7 -0
- package/dist-client/modeller/palette.js +46 -0
- package/dist-client/modeller/palette.js.map +1 -0
- package/dist-client/modeller/preview-model.d.ts +47 -0
- package/dist-client/modeller/preview-model.js +68 -0
- package/dist-client/modeller/preview-model.js.map +1 -0
- package/dist-client/modeller/product-session.d.ts +510 -0
- package/dist-client/modeller/product-session.js +157 -0
- package/dist-client/modeller/product-session.js.map +1 -0
- package/dist-client/modeller/recipe-source.d.ts +8 -0
- package/dist-client/modeller/recipe-source.js +14 -0
- package/dist-client/modeller/recipe-source.js.map +1 -0
- package/dist-client/modeller/recipes.d.ts +37 -0
- package/dist-client/modeller/recipes.js +103 -0
- package/dist-client/modeller/recipes.js.map +1 -0
- package/dist-client/modeller/refusals.d.ts +68 -0
- package/dist-client/modeller/refusals.js +142 -0
- package/dist-client/modeller/refusals.js.map +1 -0
- package/dist-client/modeller/renderer.d.ts +40 -0
- package/dist-client/modeller/renderer.js +73 -0
- package/dist-client/modeller/renderer.js.map +1 -0
- package/dist-client/modeller/stage-model.d.ts +39 -0
- package/dist-client/modeller/stage-model.js +147 -0
- package/dist-client/modeller/stage-model.js.map +1 -0
- package/dist-client/modeller/thumbnail.d.ts +3 -0
- package/dist-client/modeller/thumbnail.js +75 -0
- package/dist-client/modeller/thumbnail.js.map +1 -0
- package/dist-client/pages/figure-list-page.d.ts +1 -1
- package/dist-client/pages/figure-list-page.js +4 -4
- package/dist-client/pages/figure-list-page.js.map +1 -1
- package/dist-client/pages/figure-modeller-page.d.ts +102 -0
- package/dist-client/pages/figure-modeller-page.js +548 -0
- package/dist-client/pages/figure-modeller-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +1 -5
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/docs/product-cutover.md +25 -0
- package/package.json +5 -5
- package/test/{v3-ai-proposal.test.ts → ai-proposal.test.ts} +6 -6
- package/test/{v3-align.test.ts → align.test.ts} +8 -8
- package/test/browser/modeller-page.js +30 -30
- package/test/browser/modeller.check.mjs +14 -14
- package/test/browser/serve.mjs +1 -1
- package/test/browser/thumbnail-page.js +6 -6
- package/test/{v3-clips.test.ts → clips.test.ts} +18 -18
- package/test/{v3-controls.test.ts → controls.test.ts} +15 -15
- package/test/{v3-fields.test.ts → fields.test.ts} +13 -13
- package/test/figure-assistant-surface.test.ts +3 -3
- package/test/{v3-first-save.test.ts → first-save.test.ts} +10 -10
- package/test/fixtures/joint-column.json +2 -2
- package/test/fixtures/joint-roller.json +2 -2
- package/test/fixtures/robot-arm-6dof.json +2 -2
- package/test/{v3-gizmo.test.ts → gizmo.test.ts} +33 -33
- package/test/{v3-joint-drag.test.ts → joint-drag.test.ts} +17 -17
- package/test/{v3-joint-fan.test.ts → joint-fan.test.ts} +18 -18
- package/test/joints.ts +6 -6
- package/test/{v3-keys.test.ts → keys.test.ts} +2 -2
- package/test/{v3-layout.test.ts → layout.test.ts} +8 -8
- package/test/{v3-links.test.ts → links.test.ts} +201 -201
- package/test/{v3-live.test.ts → live.test.ts} +18 -18
- package/test/{v3-material.test.ts → material.test.ts} +12 -12
- package/test/{v3-maturity.test.ts → maturity.test.ts} +74 -74
- package/test/modeller-route.test.ts +5 -6
- package/test/{v3-palette-icons.test.ts → palette-icons.test.ts} +1 -1
- package/test/{v3-parts-wiring.test.ts → parts-wiring.test.ts} +5 -5
- package/test/{v3-preview.test.ts → preview.test.ts} +12 -12
- package/test/{v3-product-session.test.ts → product-session.test.ts} +17 -17
- package/test/{v3-product-write.test.ts → product-write.test.ts} +20 -20
- package/test/{v3-properties.test.ts → properties.test.ts} +24 -24
- package/test/{v3-recipes.test.ts → recipes.test.ts} +9 -9
- package/test/{v3-refusals.test.ts → refusals.test.ts} +43 -43
- package/test/{v3-release-panel.test.ts → release-panel.test.ts} +21 -21
- package/test/renderer.test.ts +24 -0
- package/test/resolve-ts-siblings.mjs +1 -1
- package/test/{v3-scene-canvas.test.ts → scene-canvas.test.ts} +6 -6
- package/test/{v3-stage-model.test.ts → stage-model.test.ts} +5 -5
- package/test/{v3-theme.test.ts → theme.test.ts} +4 -4
- package/test/{v3-thumbnail.test.ts → thumbnail.test.ts} +4 -4
- package/test/{v3-verdict.test.ts → verdict.test.ts} +17 -17
- package/test/{v3-view.test.ts → view.test.ts} +2 -2
- package/things-factory.config.js +2 -3
- package/translations/en.json +2 -2
- package/translations/ja.json +2 -2
- package/translations/ko.json +2 -2
- package/translations/ms.json +2 -2
- package/translations/zh.json +2 -2
- package/docs/v3-product-cutover.md +0 -25
- package/test/v3-renderer.test.ts +0 -24
- /package/client/modeller/{v3-fields.ts → fields.ts} +0 -0
- /package/client/modeller/{v3-first-save.ts → first-save.ts} +0 -0
- /package/client/modeller/{v3-help.ts → help.ts} +0 -0
- /package/client/modeller/{v3-keys.ts → keys.ts} +0 -0
- /package/client/modeller/{v3-refusals.ts → refusals.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../client/modeller/canvas.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css, nothing, unsafeCSS, type PropertyValues } from 'lit'\nimport { customElement, property, query } from 'lit/decorators.js'\nimport { i18next } from '@operato/i18n'\nimport { create, Model, niceGridStep } from '@hatiolab/things-scene'\n\nimport type { GizmoReach } from './gizmo.js'\nimport type { FigureRenderPart } from './renderer.js'\nimport { DEFAULT_VIEW, sceneLook, WORKBENCH, type ViewSettings } from './figure-view.js'\nimport { boardFor, changedKeys, componentOf, gizmoResultOf, type Board } from './stage-model.js'\nimport { JOINT_FAN } from './joint-fan.js'\nimport { ownerOf } from './fields.js'\n\n/*\n * The modeller's canvas -- a things-scene board in editing mode.\n *\n * Each body figure-model evaluates stands on the board as a `figure-part` component, so everything the canvas\n * has comes from the scene, not from code of our own: picking and the orange outline, the gizmo with its snapping and\n * live readout, box and shift selection, the camera (damping, zoom toward the cursor, double-click to fit, the six\n * view presets and their keys), the studio environment, the coordinate ground, lights and shadows, supersampling.\n * An earlier canvas drew the bodies with three.js directly and lost all of that (user, 2026-09-24: \"why nerf it?\").\n *\n * What the gizmo writes back to a component comes out as `part-transformed` in the inputs' terms (`stage-model`),\n * and the editor turns it into one step of the history; the next evaluation puts the component where the inputs say,\n * so a change a part cannot take (a fastened part moved) springs back.\n */\n\ninterface SceneComponent {\n state: Record<string, unknown>\n set: (props: Record<string, unknown>) => void\n dispose?: () => void\n}\ninterface SceneLayer extends SceneComponent {\n components?: SceneComponent[]\n root?: { on?: (event: string, handler: (...args: unknown[]) => void) => void; selected?: SceneComponent[] }\n on?: (event: string, handler: (...args: unknown[]) => void) => void\n addComponent?: (component: SceneComponent) => void\n removeComponent?: (component: SceneComponent) => void\n invalidate?: () => void\n fitCameraToModel?: () => void\n setGizmoMode?: (mode: 'translate' | 'rotate' | 'scale') => void\n app?: unknown\n}\ninterface SceneHandle {\n dispose?: () => void\n resize?: () => void\n fit?: (type?: string) => void\n root?: SceneLayer\n}\n\n/* The fan's colour when the figure's palette names no accent. */\nconst FAN_COLOUR = '#ff8a00'\n\n/* The 2D side of the board, which the author never sees on this page but the scene paints. */\nconst PAPER = '#ffffff'\n\n@customElement('figure-canvas')\nexport class FigureCanvas extends LitElement {\n static styles = css`\n :host {\n position: relative;\n display: flex;\n min-height: 0;\n overflow: hidden;\n background-color: ${unsafeCSS(WORKBENCH)};\n }\n /* The scene sets position: relative inline on its target; the size is given so it still fills the host. */\n div[stage] {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n }\n p {\n position: absolute;\n inset: 45% 1rem auto;\n margin: 0;\n text-align: center;\n pointer-events: none;\n color: #f4f4f4;\n }\n `\n @property({ attribute: false }) parts: FigureRenderPart[] = []\n @property({ attribute: false }) palette: Record<string, string> = {}\n /** Which figure is open. Another one stands the board up again. */\n @property() context = ''\n @property() selected = ''\n /** What the gizmo may write for the selected part; a change beyond it is not applied and the part springs back. */\n @property({ attribute: false }) reach: GizmoReach = { translate: false, rotate: false, scale: false }\n @property({ attribute: false }) view: ViewSettings = DEFAULT_VIEW\n /**\n * View only: nothing that happens on the board is told out. The card's picture is taken from a second canvas stood up\n * off screen (`thumbnail.ts`); the scene settles its components as it builds them, and a change told from there\n * would reach the editor as an edit.\n */\n @property({ type: Boolean }) viewOnly = false\n /** Where the selected part turns and how far (`joint-fan`): shown, never picked, never saved. */\n @property({ attribute: false }) fan: FigureRenderPart | null = null\n\n @query('div[stage]') private stage!: HTMLDivElement\n\n private scene?: SceneHandle\n private board?: Board & { depth: number }\n private standingFor?: string\n private watcher?: ResizeObserver\n /** Set while this canvas writes to components, so the scene's echo of it is not taken for a drag. */\n private writing = false\n\n render() {\n return html`<div stage></div>${this.parts.length ? nothing : html`<p>${i18next.t('figure.text.add-a-shape-to-start')}</p>`}`\n }\n\n firstUpdated() {\n this.standUp()\n this.watcher = new ResizeObserver(() => this.scene?.resize?.())\n this.watcher.observe(this)\n }\n\n protected updated(changed: PropertyValues) {\n if (!this.hasUpdated) return\n if (changed.has('parts') || changed.has('palette') || changed.has('context') || changed.has('fan')) this.standUp()\n if (changed.has('view')) this.applyView()\n if (changed.has('selected')) this.showSelected()\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n this.watcher?.disconnect()\n this.watcher = undefined\n this.teardown()\n }\n\n /**\n * The board as a picture, for the card: the 3D view at this size, or nothing when it cannot be taken. The scene's\n * `toDataURL` renders the 2D board (a white sheet), and the 3D renderer keeps no drawing buffer, so the\n * scene's own `snapshot3d` draws and reads in one task. A picture that cannot be taken is not an error and is not\n * faked (user, 2026-09-24: no card had a picture after this canvas moved onto the scene).\n */\n async snapshot(width = 480, height = 360): Promise<string | undefined> {\n const layer = this.scene?.root as (SceneLayer & { snapshot3d?: (w?: number, h?: number, type?: string, quality?: number) => Promise<string | undefined> }) | undefined\n if (!layer?.snapshot3d) return undefined\n try {\n /* webp: a card needs no alpha, and png is three or four times heavier in every list response. */\n const url = await layer.snapshot3d(width, height, 'image/webp', 0.85)\n return typeof url === 'string' && url.startsWith('data:') ? url : undefined\n } catch (e) {\n console.warn(`[thumbnail] could not take the picture -- ${(e as Error).message}`)\n return undefined\n }\n }\n\n /** The whole figure in view: the board's fit, then the 3D camera's own. */\n fit() {\n this.scene?.fit?.('ratio')\n this.scene?.root?.fitCameraToModel?.()\n }\n\n /** The gizmo in a mode on the selected part, as W, E and R do with the canvas focused -- the scene's toggle. */\n gizmo(mode: 'translate' | 'rotate' | 'scale') {\n this.scene?.root?.setGizmoMode?.(mode)\n }\n\n /**\n * Stand the board up for this figure, or bring the one standing up to date. A new board only for another figure or\n * one that has outgrown it -- standing up again loses the selection and the camera.\n */\n private standUp() {\n if (!this.stage) return\n const needed = boardFor(this.parts, this.board)\n const outgrown = !this.board || needed.width > this.board.width || needed.height > this.board.height || needed.depth > this.board.depth\n if (this.scene && this.standingFor === this.context) {\n /*\n A part dragged past the board's edge grows the board where it stands. Standing it up again lost the selection,\n the gizmo and the camera after every such drag -- the gizmo seemed not to work at all (user, 2026-09-24).\n */\n if (outgrown) this.grow(needed)\n this.sync()\n return\n }\n this.teardown()\n if (!this.parts.length) return\n this.board = needed\n try {\n this.scene = create({\n target: this.stage,\n model: {\n type: 'model-layer',\n width: needed.width,\n height: needed.height,\n depth: needed.depth,\n threed: true,\n /* Two samples a pixel: MSAA alone leaves steps on the silhouettes of one asset shown large. */\n pixelRatio: Math.min(3, (window.devicePixelRatio || 1) * 2),\n ...sceneLook(this.view ?? DEFAULT_VIEW),\n gridStep: niceGridStep(Math.max(needed.width, needed.height)),\n sky: 'color',\n fillStyle: PAPER,\n components: this.components()\n },\n /* Editing mode: the camera view buttons, gizmo modes, help and keys. */\n mode: 1\n }) as SceneHandle\n this.standingFor = this.context\n this.watchScene()\n this.scene.resize?.()\n this.fit()\n this.showSelected()\n } catch (error) {\n this.dispatchEvent(new CustomEvent('render-error', { detail: error, bubbles: true, composed: true }))\n }\n }\n\n private components(): Record<string, unknown>[] {\n const parts = this.parts.map(part => componentOf(part, this.palette, this.board!)).filter((c): c is Record<string, unknown> => !!c)\n /* The fan in the accent colour, translucent; `capturable: false` keeps clicks, the marquee and the gizmo off it. */\n const fan = this.fan ? componentOf(this.fan, { ...this.palette, [JOINT_FAN]: this.palette['palette.accent'] ?? FAN_COLOUR }, this.board!) : null\n return fan ? [...parts, { ...fan, capturable: false, locked: true }] : parts\n }\n\n /** The components standing now, by name. Not held between calls: the scene stands its board up again at times. */\n private standing(): Map<string, SceneComponent> {\n const found = new Map<string, SceneComponent>()\n for (const component of this.scene?.root?.components ?? []) {\n const name = component.state?.name\n if (typeof name === 'string') found.set(name, component)\n }\n return found\n }\n\n /** A larger board under the same scene: its size, and the grid that suits it. Components are re-placed by `sync`. */\n private grow(needed: Board & { depth: number }) {\n const layer = this.scene?.root\n this.board = needed\n if (!layer) return\n this.writing = true\n try {\n layer.set({ width: needed.width, height: needed.height, depth: needed.depth, gridStep: niceGridStep(Math.max(needed.width, needed.height)) })\n } finally {\n this.writing = false\n }\n }\n\n /** Bring the components to the evaluation: remove the gone, write only what differs, add the new. */\n private sync() {\n const layer = this.scene?.root\n if (!layer || !this.board) return\n const wanted = new Map(this.components().map(c => [c.name as string, c]))\n const standing = this.standing()\n this.writing = true\n try {\n for (const [name, component] of standing) {\n if (wanted.has(name)) continue\n layer.removeComponent?.(component)\n component.dispose?.()\n }\n for (const [name, state] of wanted) {\n const found = standing.get(name)\n if (found) {\n const diff = changedKeys(found.state, state)\n if (diff) found.set(diff)\n continue\n }\n const made = Model.compile({ ...state }, layer.app as never) as SceneComponent | null\n if (made) layer.addComponent?.(made)\n }\n } finally {\n this.writing = false\n }\n layer.invalidate?.()\n }\n\n /** Selection and drags, told out by the part's name -- never by its place in the list, which may change meanwhile. */\n private watchScene() {\n const layer = this.scene?.root\n const root = layer?.root\n if (!layer || !root?.on || this.viewOnly) return\n root.on('selected', (after: unknown) => {\n const name = (after as SceneComponent[] | undefined)?.[0]?.state?.name\n const id = typeof name === 'string' ? name : ''\n if (ownerOf(id) !== this.selected) this.dispatchEvent(new CustomEvent('part-selected', { detail: id, bubbles: true, composed: true }))\n })\n /*\n A change comes up to the board with the component in the last argument's `origin` (the scene appends\n { origin, name, deliverer } as it bubbles). Reading the first argument as the component does nothing.\n */\n /*\n A drag is written once, when it ends. The scene's gizmo writes the component on every move -- a size anchored on\n the opposite face, from the scale it captured when the drag began -- and writing each move back through the\n editor (and the evaluation back onto the component) fought it: the scale gizmo did not work (user, 2026-09-24).\n While a pointer is down the changes are only kept; on release the last one is told, and the board is brought to\n the evaluation. A change with no pointer down (the arrow keys) is told after a short pause.\n */\n layer.on?.('change', (...args: unknown[]) => {\n if (this.writing) return\n const origin = (args[args.length - 1] as { origin?: SceneComponent } | undefined)?.origin\n const name = origin?.state?.name\n if (typeof name !== 'string' || !this.parts.some(p => p.id === name)) return\n this.pending.set(name, origin!)\n if (!this.pointerDown) this.commitSoon(80)\n })\n this.stage.addEventListener('pointerdown', this.onPointerDown, true)\n window.addEventListener('pointerup', this.onPointerUp, true)\n window.addEventListener('pointercancel', this.onPointerUp, true)\n }\n\n /** Components the gizmo changed since the last commit, by part name. */\n private pending = new Map<string, SceneComponent>()\n private pointerDown = false\n private commitTimer?: ReturnType<typeof setTimeout>\n private onPointerDown = () => {\n this.pointerDown = true\n }\n private onPointerUp = () => {\n if (!this.pointerDown) return\n this.pointerDown = false\n /* After the scene's own drag end (it settles the depth and rebuilds the geometry on release). */\n this.commitSoon(0)\n }\n private commitSoon(delay: number) {\n clearTimeout(this.commitTimer)\n this.commitTimer = setTimeout(() => this.commit(), delay)\n }\n /** Tell each changed part once, in the inputs' terms; then put the board where the inputs say (a refusal springs back). */\n private commit() {\n /*\n Not while a pointer is down: a pause set by a change just before the press (the arrow keys, the scene settling\n the pick) fired in the middle of the drag and wrote where the part was then -- a drag on a part placed by a rule\n became two steps, the first one a few millimetres in (browser check, 2026-09-25). What is pending waits for the release.\n */\n if (this.pointerDown || !this.board || !this.pending.size) return\n const changed = [...this.pending]\n this.pending.clear()\n for (const [name, component] of changed) {\n const part = this.parts.find(p => p.id === name)\n if (!part) continue\n const result = gizmoResultOf(part, component.state, this.board)\n if (!result.position && !result.rotation && !result.scale) continue\n this.dispatchEvent(new CustomEvent('part-transformed', { detail: { id: part.id, result }, bubbles: true, composed: true }))\n }\n setTimeout(() => this.sync())\n }\n\n private showSelected() {\n const layer = this.scene?.root\n const root = layer?.root\n if (!layer || !root) return\n /* A repeat is selected whole: every copy of it (the editor selects the part the author made). */\n const wanted = this.selected ? [...this.standing()].filter(([name]) => ownerOf(name) === this.selected).map(([, component]) => component) : []\n const now = root.selected ?? []\n if (now.length === wanted.length && wanted.every((one, i) => now[i] === one)) return\n root.selected = wanted\n layer.invalidate?.()\n }\n\n /** View settings go into the scene as they are: they are the board's own state names. Nothing is stood up again. */\n private applyView() {\n const layer = this.scene?.root\n if (!layer) return\n layer.set(sceneLook(this.view ?? DEFAULT_VIEW))\n layer.invalidate?.()\n }\n\n private teardown() {\n clearTimeout(this.commitTimer)\n this.pending.clear()\n this.stage?.removeEventListener('pointerdown', this.onPointerDown, true)\n window.removeEventListener('pointerup', this.onPointerUp, true)\n window.removeEventListener('pointercancel', this.onPointerUp, true)\n this.scene?.dispose?.()\n this.scene = undefined\n this.standingFor = undefined\n this.board = undefined\n }\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { FigureAsset, FigureInputChange } from '@hatiolab/figure-model';
|
|
2
|
+
export interface ClipKey {
|
|
3
|
+
seconds: number;
|
|
4
|
+
value: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ClipTrack {
|
|
7
|
+
state: string;
|
|
8
|
+
label: string;
|
|
9
|
+
unit: string;
|
|
10
|
+
min: number;
|
|
11
|
+
max: number;
|
|
12
|
+
keys: ClipKey[];
|
|
13
|
+
interpolation: 'linear' | 'step';
|
|
14
|
+
loop: 'wrap' | 'hold';
|
|
15
|
+
accumulates: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface Clip {
|
|
18
|
+
name: string;
|
|
19
|
+
duration: number;
|
|
20
|
+
tracks: ClipTrack[];
|
|
21
|
+
}
|
|
22
|
+
export declare function clipsOf(asset: FigureAsset): Clip[];
|
|
23
|
+
/** The controls no clip plays yet -- the ones a new track can take. One driver per control. */
|
|
24
|
+
export declare function undrivenOf(asset: FigureAsset): string[];
|
|
25
|
+
/** The pose a clip gives at a moment, as one batch of state changes -- one step of the history. */
|
|
26
|
+
export declare function poseAt(asset: FigureAsset, clip: string, seconds: number): FigureInputChange[];
|
|
27
|
+
/** A key added at a moment, replacing one already there; the keys stay in time order. */
|
|
28
|
+
export declare function withKey(keys: readonly ClipKey[], key: ClipKey): ClipKey[];
|
|
29
|
+
/** A track's keys without one; a track keeps at least its two ends, as the format asks. */
|
|
30
|
+
export declare function withoutKey(keys: readonly ClipKey[], index: number): ClipKey[] | null;
|
|
31
|
+
/** The edit that writes a track back. */
|
|
32
|
+
export declare const trackEdit: (clip: Clip, track: ClipTrack, patch?: Partial<Pick<ClipTrack, "keys" | "loop" | "interpolation">>) => {
|
|
33
|
+
kind: "set-driver";
|
|
34
|
+
state: string;
|
|
35
|
+
clip: string;
|
|
36
|
+
keys: ClipKey[];
|
|
37
|
+
loop: "wrap" | "hold";
|
|
38
|
+
interpolation: "linear" | "step";
|
|
39
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { driverValue } from '@hatiolab/figure-model';
|
|
2
|
+
const round = (n) => Math.round(n * 1000) / 1000;
|
|
3
|
+
export function clipsOf(asset) {
|
|
4
|
+
const inputs = new Map(asset.document.model.inputs.map(i => [i.id, i]));
|
|
5
|
+
const clips = new Map();
|
|
6
|
+
for (const d of asset.drivers ?? []) {
|
|
7
|
+
const input = inputs.get(d.state);
|
|
8
|
+
const clip = clips.get(d.clip) ?? { name: d.clip, duration: d.time.duration, tracks: [] };
|
|
9
|
+
clip.tracks.push({
|
|
10
|
+
state: d.state,
|
|
11
|
+
label: asset.stateInputs?.[d.state]?.label ?? d.state,
|
|
12
|
+
unit: input?.unit ?? '',
|
|
13
|
+
min: input?.min ?? 0,
|
|
14
|
+
max: input?.max ?? 0,
|
|
15
|
+
keys: d.keys.map(k => ({ seconds: round(k.at * d.time.duration), value: k.value })),
|
|
16
|
+
interpolation: d.interpolation,
|
|
17
|
+
loop: d.loop,
|
|
18
|
+
accumulates: d.accumulates
|
|
19
|
+
});
|
|
20
|
+
clips.set(d.clip, clip);
|
|
21
|
+
}
|
|
22
|
+
return [...clips.values()];
|
|
23
|
+
}
|
|
24
|
+
/** The controls no clip plays yet -- the ones a new track can take. One driver per control. */
|
|
25
|
+
export function undrivenOf(asset) {
|
|
26
|
+
const driven = new Set((asset.drivers ?? []).map(d => d.state));
|
|
27
|
+
return asset.document.model.inputs.filter(i => i.role === 'state' && !driven.has(i.id)).map(i => i.id);
|
|
28
|
+
}
|
|
29
|
+
/** The pose a clip gives at a moment, as one batch of state changes -- one step of the history. */
|
|
30
|
+
export function poseAt(asset, clip, seconds) {
|
|
31
|
+
const units = new Map(asset.document.model.inputs.map(i => [i.id, i.unit]));
|
|
32
|
+
return (asset.drivers ?? [])
|
|
33
|
+
.filter((d) => d.clip === clip)
|
|
34
|
+
.map(d => {
|
|
35
|
+
const unit = units.get(d.state);
|
|
36
|
+
return { kind: 'set-state', input: d.state, unit, value: driverValue(d, seconds, unit === 'deg' || unit === 'rad' ? unit : 'other') };
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** A key added at a moment, replacing one already there; the keys stay in time order. */
|
|
40
|
+
export function withKey(keys, key) {
|
|
41
|
+
return [...keys.filter(k => k.seconds !== key.seconds), key].sort((a, b) => a.seconds - b.seconds);
|
|
42
|
+
}
|
|
43
|
+
/** A track's keys without one; a track keeps at least its two ends, as the format asks. */
|
|
44
|
+
export function withoutKey(keys, index) {
|
|
45
|
+
if (keys.length <= 2 || index === 0)
|
|
46
|
+
return null;
|
|
47
|
+
return keys.filter((_, i) => i !== index);
|
|
48
|
+
}
|
|
49
|
+
/** The edit that writes a track back. */
|
|
50
|
+
export const trackEdit = (clip, track, patch = {}) => ({
|
|
51
|
+
kind: 'set-driver',
|
|
52
|
+
state: track.state,
|
|
53
|
+
clip: clip.name,
|
|
54
|
+
keys: patch.keys ?? track.keys,
|
|
55
|
+
loop: patch.loop ?? track.loop,
|
|
56
|
+
interpolation: patch.interpolation ?? track.interpolation
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=clips.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clips.js","sourceRoot":"","sources":["../../client/modeller/clips.ts"],"names":[],"mappings":"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","sourcesContent":["import { driverValue } from '@hatiolab/figure-model'\nimport type { FigureAsset, FigureDriver, FigureInputChange } from '@hatiolab/figure-model'\n\n/*\n * The figure's clips: controls played by keys over time, grouped into clips that each run on one\n * clock. Kept apart from the element so the reading is tested without a browser.\n */\nexport interface ClipKey {\n seconds: number\n value: number\n}\nexport interface ClipTrack {\n state: string\n label: string\n unit: string\n min: number\n max: number\n keys: ClipKey[]\n interpolation: 'linear' | 'step'\n loop: 'wrap' | 'hold'\n accumulates: boolean\n}\nexport interface Clip {\n name: string\n duration: number\n tracks: ClipTrack[]\n}\n\nconst round = (n: number) => Math.round(n * 1000) / 1000\n\nexport function clipsOf(asset: FigureAsset): Clip[] {\n const inputs = new Map(asset.document.model.inputs.map(i => [i.id, i]))\n const clips = new Map<string, Clip>()\n for (const d of asset.drivers ?? []) {\n const input = inputs.get(d.state)\n const clip = clips.get(d.clip) ?? { name: d.clip, duration: d.time.duration, tracks: [] }\n clip.tracks.push({\n state: d.state,\n label: asset.stateInputs?.[d.state]?.label ?? d.state,\n unit: input?.unit ?? '',\n min: input?.min ?? 0,\n max: input?.max ?? 0,\n keys: d.keys.map(k => ({ seconds: round(k.at * d.time.duration), value: k.value })),\n interpolation: d.interpolation,\n loop: d.loop,\n accumulates: d.accumulates\n })\n clips.set(d.clip, clip)\n }\n return [...clips.values()]\n}\n\n/** The controls no clip plays yet -- the ones a new track can take. One driver per control. */\nexport function undrivenOf(asset: FigureAsset): string[] {\n const driven = new Set((asset.drivers ?? []).map(d => d.state))\n return asset.document.model.inputs.filter(i => i.role === 'state' && !driven.has(i.id)).map(i => i.id)\n}\n\n/** The pose a clip gives at a moment, as one batch of state changes -- one step of the history. */\nexport function poseAt(asset: FigureAsset, clip: string, seconds: number): FigureInputChange[] {\n const units = new Map(asset.document.model.inputs.map(i => [i.id, i.unit]))\n return (asset.drivers ?? [])\n .filter((d: FigureDriver) => d.clip === clip)\n .map(d => {\n const unit = units.get(d.state)!\n return { kind: 'set-state' as const, input: d.state, unit, value: driverValue(d, seconds, unit === 'deg' || unit === 'rad' ? unit : 'other') }\n })\n}\n\n/** A key added at a moment, replacing one already there; the keys stay in time order. */\nexport function withKey(keys: readonly ClipKey[], key: ClipKey): ClipKey[] {\n return [...keys.filter(k => k.seconds !== key.seconds), key].sort((a, b) => a.seconds - b.seconds)\n}\n\n/** A track's keys without one; a track keeps at least its two ends, as the format asks. */\nexport function withoutKey(keys: readonly ClipKey[], index: number): ClipKey[] | null {\n if (keys.length <= 2 || index === 0) return null\n return keys.filter((_, i) => i !== index)\n}\n\n/** The edit that writes a track back. */\nexport const trackEdit = (clip: Clip, track: ClipTrack, patch: Partial<Pick<ClipTrack, 'keys' | 'loop' | 'interpolation'>> = {}) => ({\n kind: 'set-driver' as const,\n state: track.state,\n clip: clip.name,\n keys: patch.keys ?? track.keys,\n loop: patch.loop ?? track.loop,\n interpolation: patch.interpolation ?? track.interpolation\n})\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FigureAsset } from '@hatiolab/figure-model';
|
|
2
|
+
export interface Control {
|
|
3
|
+
id: string;
|
|
4
|
+
/** The name a person reads, or the id when none is given. */
|
|
5
|
+
label: string;
|
|
6
|
+
named: boolean;
|
|
7
|
+
unit: string;
|
|
8
|
+
min: number;
|
|
9
|
+
max: number;
|
|
10
|
+
/** Where it stands in the editor now -- the pose being looked at, not yet the default. */
|
|
11
|
+
value: number;
|
|
12
|
+
/** Where it starts when the figure is placed: the default pose. */
|
|
13
|
+
start: number;
|
|
14
|
+
/** Seconds to travel the whole range, 0 when it moves at once. */
|
|
15
|
+
sweep: number;
|
|
16
|
+
/** A slider step: one degree for an angle, else a hundredth of the range. */
|
|
17
|
+
step: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function controlsOf(asset: FigureAsset, values?: Record<string, number>): Control[];
|
|
20
|
+
/** Whether the pose being looked at differs from the default pose -- only then is there a pose to keep. */
|
|
21
|
+
export declare const poseDiffers: (controls: readonly Control[]) => boolean;
|
|
22
|
+
/** The current pose as the defaults: one `set-state-defaults` edit. */
|
|
23
|
+
export declare const poseAsDefault: (controls: readonly Control[]) => {
|
|
24
|
+
kind: "set-state-defaults";
|
|
25
|
+
values: {
|
|
26
|
+
[k: string]: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function controlsOf(asset, values = {}) {
|
|
2
|
+
return asset.document.model.inputs
|
|
3
|
+
.filter(input => input.role === 'state')
|
|
4
|
+
.map(input => {
|
|
5
|
+
const policy = asset.stateInputs?.[input.id];
|
|
6
|
+
const start = asset.stateDefaults[input.id] ?? input.min;
|
|
7
|
+
return {
|
|
8
|
+
id: input.id,
|
|
9
|
+
label: policy?.label ?? input.id,
|
|
10
|
+
named: !!policy?.label,
|
|
11
|
+
unit: input.unit,
|
|
12
|
+
min: input.min,
|
|
13
|
+
max: input.max,
|
|
14
|
+
value: values[input.id] ?? start,
|
|
15
|
+
start,
|
|
16
|
+
sweep: policy?.sweep ?? 0,
|
|
17
|
+
step: input.unit === 'deg' ? 1 : (input.max - input.min) / 100
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Whether the pose being looked at differs from the default pose -- only then is there a pose to keep. */
|
|
22
|
+
export const poseDiffers = (controls) => controls.some(c => c.value !== c.start);
|
|
23
|
+
/** The current pose as the defaults: one `set-state-defaults` edit. */
|
|
24
|
+
export const poseAsDefault = (controls) => ({
|
|
25
|
+
kind: 'set-state-defaults',
|
|
26
|
+
values: Object.fromEntries(controls.map(c => [c.id, c.value]))
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controls.js","sourceRoot":"","sources":["../../client/modeller/controls.ts"],"names":[],"mappings":"AAyBA,MAAM,UAAU,UAAU,CAAC,KAAkB,EAAE,SAAiC,EAAE;IAChF,OAAO,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM;SAC/B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;SACvC,GAAG,CAAC,KAAK,CAAC,EAAE;QACX,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,GAAG,CAAA;QACxD,OAAO;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK,CAAC,EAAE;YAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK;YAChC,KAAK;YACL,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;YACzB,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG;SAC/D,CAAA;IACH,CAAC,CAAC,CAAA;AACN,CAAC;AAED,2GAA2G;AAC3G,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAA4B,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAA;AAEpG,uEAAuE;AACvE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAA4B,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,EAAE,oBAA6B;IACnC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;CAC/D,CAAC,CAAA","sourcesContent":["import type { FigureAsset } from '@hatiolab/figure-model'\n\n/*\n * The figure's controls -- its state inputs: the values a board or a connector sets to pose\n * the figure (a lift height, a joint angle, a gripper opening). Kept apart from the element so the reading is tested\n * without a browser.\n */\nexport interface Control {\n id: string\n /** The name a person reads, or the id when none is given. */\n label: string\n named: boolean\n unit: string\n min: number\n max: number\n /** Where it stands in the editor now -- the pose being looked at, not yet the default. */\n value: number\n /** Where it starts when the figure is placed: the default pose. */\n start: number\n /** Seconds to travel the whole range, 0 when it moves at once. */\n sweep: number\n /** A slider step: one degree for an angle, else a hundredth of the range. */\n step: number\n}\n\nexport function controlsOf(asset: FigureAsset, values: Record<string, number> = {}): Control[] {\n return asset.document.model.inputs\n .filter(input => input.role === 'state')\n .map(input => {\n const policy = asset.stateInputs?.[input.id]\n const start = asset.stateDefaults[input.id] ?? input.min\n return {\n id: input.id,\n label: policy?.label ?? input.id,\n named: !!policy?.label,\n unit: input.unit,\n min: input.min,\n max: input.max,\n value: values[input.id] ?? start,\n start,\n sweep: policy?.sweep ?? 0,\n step: input.unit === 'deg' ? 1 : (input.max - input.min) / 100\n }\n })\n}\n\n/** Whether the pose being looked at differs from the default pose -- only then is there a pose to keep. */\nexport const poseDiffers = (controls: readonly Control[]) => controls.some(c => c.value !== c.start)\n\n/** The current pose as the defaults: one `set-state-defaults` edit. */\nexport const poseAsDefault = (controls: readonly Control[]) => ({\n kind: 'set-state-defaults' as const,\n values: Object.fromEntries(controls.map(c => [c.id, c.value]))\n})\n"]}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { LitElement, type PropertyValues } from 'lit';
|
|
2
|
+
import '@operato/layout';
|
|
3
|
+
import type { RecipeTemplate } from './recipes.js';
|
|
4
|
+
import './figure-recipe-catalog.js';
|
|
5
|
+
import type { FigureProductSession } from './product-session.js';
|
|
6
|
+
import './canvas.js';
|
|
7
|
+
import './figure-parts.js';
|
|
8
|
+
import './figure-material-panel.js';
|
|
9
|
+
import './figure-links.js';
|
|
10
|
+
import './figure-controls.js';
|
|
11
|
+
import './figure-clips.js';
|
|
12
|
+
import './figure-preview.js';
|
|
13
|
+
import './figure-settings.js';
|
|
14
|
+
import './figure-capability-panel.js';
|
|
15
|
+
import type { FigureBudgetLine } from './maturity.js';
|
|
16
|
+
import type { FirstSave } from './first-save.js';
|
|
17
|
+
/** Product editor surface. The injected session is shared by page persistence and AI commands. */
|
|
18
|
+
export declare class FigureEditor extends LitElement {
|
|
19
|
+
static styles: import("lit").CSSResult[];
|
|
20
|
+
session?: FigureProductSession;
|
|
21
|
+
/** The recipe templates (ADR-0095), read by the page from the server. */
|
|
22
|
+
templates: RecipeTemplate[];
|
|
23
|
+
/** Reads a template's guide; given by the page. */
|
|
24
|
+
guideOf?: (name: string) => Promise<string>;
|
|
25
|
+
thumbnailOf?: (name: string) => string;
|
|
26
|
+
/** The 「보드에서 바꿀 수 있음」 form being filled for a row, before it is published (ADR-0096). */
|
|
27
|
+
private publishing?;
|
|
28
|
+
private catalogOpen;
|
|
29
|
+
/** Why the last recipe was refused, in the kernel's words. */
|
|
30
|
+
private recipeError;
|
|
31
|
+
revision: number;
|
|
32
|
+
/** The verdict's count, drawn on its tab so it stays in sight while another tab is open. */
|
|
33
|
+
instances?: number;
|
|
34
|
+
/** The cost line the parts list shows, from the maturity report. */
|
|
35
|
+
budget?: FigureBudgetLine;
|
|
36
|
+
/**
|
|
37
|
+
* Asks the type and name of a figure saved for the first time (the host has the catalogue's names); undefined
|
|
38
|
+
* when the author cancels, and then nothing is saved. Without it a new figure is saved under a temporary type.
|
|
39
|
+
*/
|
|
40
|
+
firstSave?: (name: string) => Promise<FirstSave | undefined>;
|
|
41
|
+
private selected;
|
|
42
|
+
private error;
|
|
43
|
+
/** The same link offered again with the size range narrowed, after a link the declared range could not hold. */
|
|
44
|
+
private retry?;
|
|
45
|
+
private tab;
|
|
46
|
+
/** The frame the bar folds - found after the first render, since the bar is drawn before it. */
|
|
47
|
+
private panes?;
|
|
48
|
+
/** What the middle shows: the parts being edited, or the figure placed on a board at eight sizes. */
|
|
49
|
+
private middle;
|
|
50
|
+
/** The gizmo shown on the selected part, as the tools in the bar and W · E · R choose it; none once the part changes. */
|
|
51
|
+
private gizmoMode?;
|
|
52
|
+
/** A line saying what a command did besides what was asked; gone at the next command. */
|
|
53
|
+
private notice;
|
|
54
|
+
/**
|
|
55
|
+
* How the workbench shows the figure, in the board's own state names. The viewer's, kept in
|
|
56
|
+
* their browser, never in the figure.
|
|
57
|
+
*/
|
|
58
|
+
private view;
|
|
59
|
+
private static storedView;
|
|
60
|
+
private setView;
|
|
61
|
+
/**
|
|
62
|
+
* Parts hidden on the workbench — the author's view while working, not the asset's visibility. Not saved: a part
|
|
63
|
+
* hidden here is still in the figure, still checked, still drawn on a board.
|
|
64
|
+
*/
|
|
65
|
+
private hiddenParts;
|
|
66
|
+
/** A value in hand -- typed, held on a spinner, scrubbed, dragged -- drawn at once and recorded on release. */
|
|
67
|
+
private live;
|
|
68
|
+
private lastContext;
|
|
69
|
+
/**
|
|
70
|
+
* Move, turn and size on the canvas, in the bar's tools: the gizmo the W · E · R keys give, found without knowing the
|
|
71
|
+
* keys (2026-09-26 -- a person dragging the part turned the camera). Each is offered only where the part's own
|
|
72
|
+
* numbers can take it (`gizmoReachOf`).
|
|
73
|
+
*/
|
|
74
|
+
private renderGizmoTools;
|
|
75
|
+
/** The modeller's keys (`keys.ts`): undo, redo, duplicate, remove, back to the whole figure, the gizmo's modes. */
|
|
76
|
+
private onKey;
|
|
77
|
+
connectedCallback(): void;
|
|
78
|
+
disconnectedCallback(): void;
|
|
79
|
+
protected willUpdate(_changed: PropertyValues): void;
|
|
80
|
+
/** The scene toggles: the mode already shown takes the gizmo away. */
|
|
81
|
+
private useGizmo;
|
|
82
|
+
/**
|
|
83
|
+
* A refusal no particular sentence knows: figure-model's code said as a refusal, never its English; any other error
|
|
84
|
+
* (the network, the server) as it came.
|
|
85
|
+
*/
|
|
86
|
+
private said;
|
|
87
|
+
private run;
|
|
88
|
+
/** What the canvas draws: the figure with the value in hand when there is one, and without the parts hidden on the workbench. */
|
|
89
|
+
private drawnParts;
|
|
90
|
+
/**
|
|
91
|
+
* Where the selected part turns and how far, with the pose as it is drawn now -- a value in hand included, so the
|
|
92
|
+
* fan follows the arm while a control is dragged. Its size comes from what the joint turns (`joint-fan`).
|
|
93
|
+
*/
|
|
94
|
+
private fanOf;
|
|
95
|
+
/**
|
|
96
|
+
* Every input as the session holds it now -- the pose the author has set with the controls, and design values. The
|
|
97
|
+
* asset's source carries only the saved defaults, so drawing a value in hand over it put every other joint back to its
|
|
98
|
+
* saved angle while one slider moved, and the arm jumped when released (user, 2026-09-24).
|
|
99
|
+
*/
|
|
100
|
+
private currentInputs;
|
|
101
|
+
/** A value in hand: an input's value (design or pose) or a part edit. Nothing is recorded. */
|
|
102
|
+
private hold;
|
|
103
|
+
private changed;
|
|
104
|
+
private save;
|
|
105
|
+
private add;
|
|
106
|
+
/**
|
|
107
|
+
* The list's own operations, as part edits — each one step of the session's history. The copy and the moved
|
|
108
|
+
* part stay selected.
|
|
109
|
+
*/
|
|
110
|
+
private duplicatePart;
|
|
111
|
+
private movePart;
|
|
112
|
+
private removePart;
|
|
113
|
+
private toggleHiddenPart;
|
|
114
|
+
/** Whether the figure declares its size (the three size inputs). */
|
|
115
|
+
private hasSize;
|
|
116
|
+
/**
|
|
117
|
+
* A recipe put in (ADR-0095): resolved on the figure as it stands -- its parts take the next free name, onto the part
|
|
118
|
+
* chosen -- and applied as one step. Refused, the kernel's sentence goes to the form as it is (condition 3).
|
|
119
|
+
*/
|
|
120
|
+
private insertRecipe;
|
|
121
|
+
/** The design values of the asset being edited. */
|
|
122
|
+
private designValues;
|
|
123
|
+
/** What the gizmo may write for the selected part — read off the asset being edited. */
|
|
124
|
+
private reachOf;
|
|
125
|
+
/** A drag, written back as the part's own inputs — one step of the history, like typing the numbers. */
|
|
126
|
+
private transformed;
|
|
127
|
+
/** 맞추기 — one tool, one step of the history, through the same inputs as the gizmo. */
|
|
128
|
+
private align;
|
|
129
|
+
/** A mirrored copy: duplicate, then place the copy on the other side — two steps. */
|
|
130
|
+
private mirror;
|
|
131
|
+
private renderTools;
|
|
132
|
+
/**
|
|
133
|
+
* A part's name. It renames every value the part owns, and what refers to them follows
|
|
134
|
+
* (figure-model's rename-part), as one step of the history.
|
|
135
|
+
*/
|
|
136
|
+
/**
|
|
137
|
+
* A repeated body is one of its repeat's copies and is named after it -- the list and this field read `roller 15`, not
|
|
138
|
+
* its path through the repeat (user, 2026-09-24: 「부품 이름 ???」). A copy has no name of its own to change.
|
|
139
|
+
*/
|
|
140
|
+
private renderRepeatNote;
|
|
141
|
+
private renamePart;
|
|
142
|
+
/** Size response, fastening and motion for the selected part, as authoring commands. */
|
|
143
|
+
private renderLinks;
|
|
144
|
+
/** The figure's controls: try a value, name it, set its range and travel time, keep the pose. */
|
|
145
|
+
private renderControls;
|
|
146
|
+
/** One command, one step of the history. A refusal a person can meet here is said in their words. */
|
|
147
|
+
private linkCommand;
|
|
148
|
+
private changeField;
|
|
149
|
+
/** Typing or a held spinner: each number on the way is drawn; the field's change records it. */
|
|
150
|
+
private typing;
|
|
151
|
+
/**
|
|
152
|
+
* Drag a field's label (or an axis letter) sideways to scrub its value, as vector tools do: the figure follows the
|
|
153
|
+
* pointer, and the release records one step. The field's number follows too, so the value in hand is always read.
|
|
154
|
+
*/
|
|
155
|
+
private scrub;
|
|
156
|
+
/** The measured range, kept for the revision it was measured at: measuring evaluates the figure at its edges. */
|
|
157
|
+
private range?;
|
|
158
|
+
/** The sizes a board may give the figure, and the way to measure them again after its parts changed. */
|
|
159
|
+
private renderInstanceRange;
|
|
160
|
+
/**
|
|
161
|
+
* How the figure grows. One ratio (`sizing: 'uniform'`) is said as it is; a figure with joints that has not declared
|
|
162
|
+
* it is offered it -- a mechanism does not shear -- and the author decides (chief architect's ruling 2026-09-24).
|
|
163
|
+
*/
|
|
164
|
+
private renderSizingRule;
|
|
165
|
+
/**
|
|
166
|
+
* The parts list, one row per part the author made: a repeat is one row with how many copies it makes now
|
|
167
|
+
* (user, 2026-09-24: the conveyor's sixteen rollers were sixteen rows).
|
|
168
|
+
*/
|
|
169
|
+
private partRows;
|
|
170
|
+
/** The detail level's part budget for the list's head, from figure-model's count (a repeat once). */
|
|
171
|
+
private partBudgetOf;
|
|
172
|
+
/**
|
|
173
|
+
* A repeat: which way its copies run, how many there are now, and how far apart.
|
|
174
|
+
* The pitch of a row that fills its length is edited as a number (`set-repeat` makes it the part's own the first
|
|
175
|
+
* time); a row of a fixed count says its pitch cannot be set yet (figure-model refuses it the same way).
|
|
176
|
+
*/
|
|
177
|
+
private renderRepeat;
|
|
178
|
+
/** The design input a row's pitch is read from, when it is read from one (ADR-0096). */
|
|
179
|
+
private pitchInputOf;
|
|
180
|
+
/**
|
|
181
|
+
* 「보드에서 바꿀 수 있음」 (ADR-0096): the row's pitch made a design input the figure publishes -- a name on the board and
|
|
182
|
+
* a range the board's value is held to -- in one step; changed or withdrawn the same way. Only a row with a record.
|
|
183
|
+
*/
|
|
184
|
+
private renderPublicPitch;
|
|
185
|
+
/**
|
|
186
|
+
* A pitch always goes through `set-repeat`: the command moves the longest length with it (at most `limit` copies),
|
|
187
|
+
* which a plain input edit would not. Refused only when the authored length would already hold more copies than
|
|
188
|
+
* that -- said with the numbers.
|
|
189
|
+
*/
|
|
190
|
+
private setPitch;
|
|
191
|
+
/** Choose a part, from the list, the canvas or a place row. A figure-level tab gives way to the part's properties. */
|
|
192
|
+
private choose;
|
|
193
|
+
private asset;
|
|
194
|
+
private renderTab;
|
|
195
|
+
updated(changed: PropertyValues<this>): void;
|
|
196
|
+
/** Which part the panel speaks of, with the way back to the whole figure. */
|
|
197
|
+
private renderSubject;
|
|
198
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
199
|
+
/** Position, rotation and size as rows of three cells; the shape's own measures one to a line, under their title. */
|
|
200
|
+
private renderGroups;
|
|
201
|
+
private renderVector;
|
|
202
|
+
private renderSingle;
|
|
203
|
+
}
|