@things-factory/board-ui 10.1.24 → 10.1.25
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/dist-client/board-placement-tray.js +3 -3
- package/dist-client/board-placement-tray.js.map +1 -1
- package/dist-client/board-placement-view.d.ts +1 -1
- package/dist-client/board-placement-view.js.map +1 -1
- package/dist-client/index.d.ts +0 -3
- package/dist-client/index.js +0 -3
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/board-modeller-page.d.ts +5 -5
- package/dist-client/pages/board-modeller-page.js +19 -21
- package/dist-client/pages/board-modeller-page.js.map +1 -1
- package/dist-client/pages/board-player-page.js +1 -1
- package/dist-client/pages/board-player-page.js.map +1 -1
- package/dist-client/pages/board-viewer-page.js +1 -1
- package/dist-client/pages/board-viewer-page.js.map +1 -1
- package/dist-client/utils/board-model-diff.d.ts +3 -3
- package/dist-client/utils/board-model-diff.js +0 -0
- package/dist-client/utils/board-model-diff.js.map +1 -1
- package/dist-client/utils/patch-apply-gate.js +1 -1
- package/dist-client/utils/patch-apply-gate.js.map +1 -1
- package/package.json +7 -6
- package/dist-client/board-placement-palette.d.ts +0 -39
- package/dist-client/board-placement-palette.js +0 -8
- package/dist-client/board-placement-palette.js.map +0 -1
- package/dist-client/board-provider.d.ts +0 -3
- package/dist-client/board-provider.js +0 -87
- package/dist-client/board-provider.js.map +0 -1
- package/dist-client/pages/board-action-dispatch.d.ts +0 -31
- package/dist-client/pages/board-action-dispatch.js +0 -80
- package/dist-client/pages/board-action-dispatch.js.map +0 -1
- package/dist-client/pages/board-edit-dispatch.d.ts +0 -74
- package/dist-client/pages/board-edit-dispatch.js +0 -299
- package/dist-client/pages/board-edit-dispatch.js.map +0 -1
- package/dist-client/tsconfig.tsbuildinfo +0 -1
- package/dist-server/tsconfig.tsbuildinfo +0 -1
|
@@ -2,8 +2,8 @@ import { __decorate, __metadata } from "tslib";
|
|
|
2
2
|
import { LitElement, html, css } from 'lit';
|
|
3
3
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
4
4
|
import { hasGroups, hasHierarchy, toPlacementGroups, toPlacementTree } from './board-placement-view.js';
|
|
5
|
-
import { boardPlacementProvider } from '
|
|
6
|
-
import {
|
|
5
|
+
import { boardPlacementProvider } from '@operato/board/board-placement-palette.js';
|
|
6
|
+
import { dispatchSceneEditOp } from '@operato/scene-ops';
|
|
7
7
|
/*
|
|
8
8
|
* board 배치 트레이 = 능력 컴포저 UI — 등록된 BoardPlacementProvider 의 항목을 좌측 트레이로 노출.
|
|
9
9
|
* 항목(엔티티)마다 가능한 **표현들(능력×목적, N:M)** 을 펼쳐 저작이 선택 배치. 1:1 드롭 아님 —
|
|
@@ -171,7 +171,7 @@ let BoardPlacementTray = class BoardPlacementTray extends LitElement {
|
|
|
171
171
|
if (!comp)
|
|
172
172
|
return;
|
|
173
173
|
const off = (this._dropCount++ % 6) * 22;
|
|
174
|
-
|
|
174
|
+
dispatchSceneEditOp(this.scene, { op: 'add', component: { ...comp, left: 80 + off, top: 80 + off } });
|
|
175
175
|
this.scanPlaced();
|
|
176
176
|
}
|
|
177
177
|
/* 모두 자동 배치 — 각 항목의 첫 미배치 표현을 배치(빠른 시작). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board-placement-tray.js","sourceRoot":"","sources":["../client/board-placement-tray.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { hasGroups, hasHierarchy, toPlacementGroups, toPlacementTree } from './board-placement-view.js'\nimport { boardPlacementProvider, type BoardPlacementItem, type BoardPlacementView } from './board-placement-palette.js'\nimport { dispatchBoardEditOp } from './pages/board-edit-dispatch.js'\n\n/*\n * board 배치 트레이 = 능력 컴포저 UI — 등록된 BoardPlacementProvider 의 항목을 좌측 트레이로 노출.\n * 항목(엔티티)마다 가능한 **표현들(능력×목적, N:M)** 을 펼쳐 저작이 선택 배치. 1:1 드롭 아님 —\n * 한 엔티티에 여러 표현(모니터/지표/제어/에뮬레이션)을 얹을 수 있다. 표현별 배치 여부 표시(tag::type 스캔).\n * 배치 = board-edit 'add' op(model.tag=엔티티 id) → 라이브 결합(board-provider data(tag:)).\n * board 제품 무변경 — 통합층(board-ui)이 modeller 위에 얹는 일반 오버레이.\n */\n@customElement('board-placement-tray')\nexport class BoardPlacementTray extends LitElement {\n @property({ type: String }) boardId?: string\n @property({ type: Object }) scene: any\n\n @state() private items: BoardPlacementItem[] = []\n @state() private placedKeys: Set<string> = new Set() // `${tag}::${componentType}`\n @state() private placedTags: Set<string> = new Set() // tag (fallback 단일 view 판정)\n @state() private _pos = { x: 12, y: 12 }\n @state() private _collapsed = false\n private _drag: { sx: number; sy: number; ox: number; oy: number } | null = null\n private _timer?: any\n private _dropCount = 0\n /**\n * 어떻게 볼 것인가 — **계층**(소속 트리) 또는 **종류**(같은 것끼리 묶음).\n *\n * 호스트가 소속·종류를 안 주면 둘 다 없으므로 예전처럼 평평한 목록이다. 기본은 계층 — 현장은 어디에\n * 무엇이 있는지로 인식한다.\n */\n @state() private _view: 'tree' | 'kind' = 'tree'\n\n static styles = css`\n :host { display: contents; }\n .tray { position: absolute; top: 12px; left: 12px; width: 210px; max-height: calc(100% - 24px); overflow: auto; z-index: 5; background: #fff; border: 1px solid #d5dce0; border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.18); padding: 10px 11px; display: flex; flex-direction: column; gap: 7px; }\n .tray.collapsed { overflow: visible; max-height: none; }\n /* 표준 스크롤바(앱 consoleStyles 규약: 얇은 5px·투명 트랙·은은한 thumb). */\n .tray { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,.2) transparent; }\n .tray::-webkit-scrollbar { width: 5px; height: 5px; }\n .tray::-webkit-scrollbar-track { background: transparent; }\n .tray::-webkit-scrollbar-thumb { background: rgba(0,0,0,.2); border-radius: 3px; }\n .tray::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,.38); }\n .ttl { font-family: ui-monospace, Menlo, monospace; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: #57676f; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; touch-action: none; }\n .ttl b { color: #0b8f9c; font-weight: 700; }\n .ttl .grip::before { content: '⠿'; color: #8a959c; font-size: 12px; margin-right: 5px; }\n .rt { display: inline-flex; align-items: center; gap: 6px; }\n .collapse { border: 0; background: transparent; color: #57676f; cursor: pointer; font-size: 12px; line-height: 1; padding: 2px 3px; }\n .collapse:hover { color: #0b8f9c; }\n .hint { font-size: 10.5px; color: #8a959c; font-family: ui-monospace, Menlo, monospace; margin: 0; }\n .auto { border: 1px solid #0b8f9c88; background: #0b8f9c14; color: #0b8f9c; border-radius: 7px; padding: 6px 9px; font-size: 11.5px; font-weight: 640; cursor: pointer; font-family: ui-monospace, Menlo, monospace; }\n .auto:hover { background: #0b8f9c26; }\n .node { border: 1px solid #d5dce0; border-radius: 8px; padding: 6px 8px; display: flex; flex-direction: column; gap: 5px; font-family: ui-monospace, Menlo, monospace; }\n .nhead { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; }\n .nid { font-size: 12px; font-weight: 640; color: #17242c; }\n .nty { font-size: 9.5px; color: #8a959c; }\n .views { display: flex; flex-direction: column; gap: 3px; }\n .view { display: flex; justify-content: space-between; align-items: center; gap: 6px; text-align: left; background: transparent; border: 1px solid #cfe6e9; color: #0b8f9c; border-radius: 6px; padding: 4px 7px; font-size: 11px; cursor: pointer; font-family: ui-monospace, Menlo, monospace; }\n .view:hover { background: #0b8f9c12; border-color: #0b8f9c; }\n .view.placed { color: #1a8f4c; border-color: #b7e0c5; background: #1a8f4c0e; cursor: default; }\n .purpose { font-size: 9px; color: #8a959c; text-transform: uppercase; letter-spacing: .04em; }\n .vsw { display: flex; gap: 4px; margin: 0 0 6px; }\n .vsw button { flex: 1; padding: 3px 0; font-size: 10px; border: 1px solid var(--line, #d5dce0); background: transparent; color: inherit; border-radius: 5px; cursor: pointer; }\n .vsw button.on { background: var(--accent, #17242c); color: #fff; border-color: transparent; }\n /* 맥락으로 끼운 조상 — 놓을 것이 없으니 흐리게. 길을 보여 줄 뿐이다. */\n .node.context > .nhead { opacity: 0.55; }\n .grp { margin-bottom: 6px; }\n .ghead { font-size: 10px; opacity: 0.7; margin: 4px 0 2px; }\n .done { color: #1a8f4c; font-size: 11.5px; font-family: ui-monospace, Menlo, monospace; }\n @media (prefers-color-scheme: dark) {\n .tray { background: #141b21; border-color: #26323a; scrollbar-color: rgba(255,255,255,.18) transparent; }\n .tray::-webkit-scrollbar-thumb { background: rgba(255,255,255,.18); }\n .tray::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.34); }\n .ttl { color: #8b9aa3; } .ttl b { color: #22c3cf; }\n .node { border-color: #26323a; } .nid { color: #dde7ec; } .nty { color: #8b9aa3; }\n .view { border-color: #234; color: #22c3cf; } .view:hover { background: #22c3cf16; }\n .auto { border-color: #22c3cf88; background: #22c3cf1e; color: #22c3cf; }\n }\n `\n\n connectedCallback() {\n super.connectedCallback()\n this.reload()\n this._timer = setInterval(() => this.scanPlaced(), 2000)\n }\n disconnectedCallback() {\n super.disconnectedCallback()\n clearInterval(this._timer)\n window.removeEventListener('pointermove', this._onDrag)\n window.removeEventListener('pointerup', this._endDrag)\n }\n updated(changed: Map<string, unknown>) {\n if (changed.has('boardId')) this.reload()\n else if (changed.has('scene')) this.scanPlaced()\n }\n\n /* 헤더 드래그 이동(편집 공간 가림 방지) — 델타 기반. */\n private startDrag(e: PointerEvent) {\n e.preventDefault()\n this._drag = { sx: e.clientX, sy: e.clientY, ox: this._pos.x, oy: this._pos.y }\n window.addEventListener('pointermove', this._onDrag)\n window.addEventListener('pointerup', this._endDrag)\n }\n private _onDrag = (e: PointerEvent) => {\n const d = this._drag\n if (!d) return\n this._pos = { x: Math.max(0, d.ox + (e.clientX - d.sx)), y: Math.max(0, d.oy + (e.clientY - d.sy)) }\n }\n private _endDrag = () => {\n this._drag = null\n window.removeEventListener('pointermove', this._onDrag)\n window.removeEventListener('pointerup', this._endDrag)\n }\n\n private async reload() {\n const p = boardPlacementProvider()\n if (!p || !this.boardId) { this.items = []; return }\n try { this.items = await p.items(this.boardId) } catch { this.items = [] }\n this.scanPlaced()\n }\n private scanPlaced() {\n const provider = boardPlacementProvider()\n const map = this.scene?.rootContainer?.refidIndexMap\n const keys = new Set<string>()\n const tags = new Set<string>()\n map?.forEach?.((comp: any) => {\n const t = comp?.get?.('tag')\n if (!t) return\n tags.add(String(t))\n /* 표현별 판정 = 호스트가 배치된 컴포넌트에서 되짚어 준 key. 컴포넌트에 저장된 값을 읽지 않는다\n (예전에 `viewKey` 를 심었다가 떼면서, 심는 쪽과 읽는 쪽이 어긋나 판정이 죽은 적이 있다).\n 되짚기를 제공하지 않는 호스트는 `type` 을 key 로 쓴다고 보고 비교한다. */\n const derived = provider?.viewKeyOfComponent?.(comp?.model ?? comp)\n const type = comp?.get?.('type') ?? comp?.model?.type\n if (derived) keys.add(`${t}::${derived}`)\n else if (type) keys.add(`${t}::${type}`)\n })\n this.placedKeys = keys\n this.placedTags = tags\n this.requestUpdate()\n }\n\n /* 항목의 표현들(능력×목적). provider 가 viewsFor 지원하면 그 결과(빈 배열=표현 없음)를 그대로,\n 미지원이면 단일 fallback. → 표현 없는 엔티티는 죽은 버튼 없이 자연히 트레이에서 빠짐. */\n private viewsOf(item: BoardPlacementItem): BoardPlacementView[] {\n const p = boardPlacementProvider()\n if (p?.viewsFor) return p.viewsFor(item.id)\n return [{ key: '', label: '배치' }]\n }\n private viewPlaced(id: string, key: string): boolean {\n return key ? this.placedKeys.has(`${id}::${key}`) : this.placedTags.has(id)\n }\n /* 미배치(=아직 안 놓은 표현이 하나라도 있는) 항목. */\n private get unplaced(): BoardPlacementItem[] {\n return this.items.filter(i => this.viewsOf(i).some(v => !this.viewPlaced(i.id, v.key)))\n }\n\n private place(item: BoardPlacementItem, viewKey: string) {\n const p = boardPlacementProvider()\n if (!p || !this.scene) return\n const comp = p.componentFor(item, viewKey || undefined)\n if (!comp) return\n const off = (this._dropCount++ % 6) * 22\n dispatchBoardEditOp(this.scene, { op: 'add', component: { ...comp, left: 80 + off, top: 80 + off } } as any)\n this.scanPlaced()\n }\n /* 모두 자동 배치 — 각 항목의 첫 미배치 표현을 배치(빠른 시작). */\n private placeAll() {\n for (const it of this.unplaced) {\n const v = this.viewsOf(it).find(v => !this.viewPlaced(it.id, v.key))\n if (v) this.place(it, v.key)\n }\n this.scanPlaced()\n }\n\n /** 이 항목에 아직 안 놓은 표현이 있나 — 트리에서 \"놓을 것이 있는지\" 를 이걸로 가른다. */\n private hasUnplaced(item: BoardPlacementItem): boolean {\n return this.viewsOf(item).some(v => !this.viewPlaced(item.id, v.key))\n }\n\n /** 항목 한 줄 — 이름과 그 항목에 놓을 수 있는 표현들. */\n private renderItem(it: BoardPlacementItem, depth = 0, context = false) {\n const views = this.viewsOf(it)\n return html`<div class=\"node ${context ? 'context' : ''}\" style=${depth ? `margin-left:${depth * 12}px` : ''}>\n <div class=\"nhead\"><span class=\"nid\">${it.label}</span>${it.sub ? html`<span class=\"nty\">${it.sub}</span>` : ''}</div>\n ${views.length\n ? html`<div class=\"views\">\n ${views.map(v => {\n const placed = this.viewPlaced(it.id, v.key)\n return html`<button class=\"view ${placed ? 'placed' : ''}\" ?disabled=${placed}\n @click=${() => this.place(it, v.key)} title=${placed ? '배치됨' : '이 표현으로 배치'}>\n <span>${placed ? '✓ ' : ''}${v.label}</span>${v.sub ? html`<span class=\"purpose\">${v.sub}</span>` : ''}\n </button>`\n })}\n </div>`\n : ''}\n </div>`\n }\n\n render() {\n const p = boardPlacementProvider()\n if (!p || !this.items.length) return html``\n const un = this.unplaced\n const co = this._collapsed\n /* 호스트가 소속·종류를 주지 않으면 뷰 전환이 의미 없다 — 예전처럼 평평하게 보여 준다. */\n const tree = hasHierarchy(this.items)\n const kinds = hasGroups(this.items)\n const view = tree && kinds ? this._view : tree ? 'tree' : kinds ? 'kind' : 'flat'\n\n return html`<div class=\"tray ${co ? 'collapsed' : ''}\" style=\"left:${this._pos.x}px;top:${this._pos.y}px\">\n <div class=\"ttl\" @pointerdown=${(e: PointerEvent) => this.startDrag(e)}>\n <span class=\"grip\">${p.label || '미배치'}</span>\n <span class=\"rt\"><b>${un.length}</b>\n <button class=\"collapse\" @pointerdown=${(e: Event) => e.stopPropagation()} @click=${() => (this._collapsed = !co)} title=${co ? '펼치기' : '접기'}>${co ? '▸' : '▾'}</button>\n </span>\n </div>\n ${co\n ? ''\n : un.length\n ? html`\n ${tree && kinds\n ? html`<div class=\"vsw\" @pointerdown=${(e: Event) => e.stopPropagation()}>\n <button class=\"${view === 'tree' ? 'on' : ''}\" @click=${() => (this._view = 'tree')} title=\"공간 계층으로 — 어디에 무엇이 있나\">계층</button>\n <button class=\"${view === 'kind' ? 'on' : ''}\" @click=${() => (this._view = 'kind')} title=\"종류별로 — 같은 것 여럿에 한 번에\">종류</button>\n </div>`\n : ''}\n <button class=\"auto\" @click=${() => this.placeAll()} title=\"각 항목의 첫 표현을 일괄 배치\">모두 자동 배치</button>\n <div class=\"hint\">항목의 표현을 골라 배치. 여러 표현을 얹을 수 있습니다.</div>\n ${view === 'tree'\n ? /* 놓을 것이 남은 항목만 남기되 **조상은 길로 함께** 보여 준다 — 트리가 끊기면 어디에\n 속한 것인지 알 수 없다. */\n toPlacementTree(this.items, it => this.hasUnplaced(it)).map(r => this.renderItem(r.item, r.depth, r.context))\n : view === 'kind'\n ? toPlacementGroups(un).map(\n g => html`<div class=\"grp\"><div class=\"ghead\">${g.group} <b>${g.items.length}</b></div>\n ${g.items.map(it => this.renderItem(it))}</div>`\n )\n : un.map(it => this.renderItem(it))}`\n : html`<div class=\"done\">✓ 모두 배치됨</div>`}\n </div>`\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"board-placement-tray.js","sourceRoot":"","sources":["../client/board-placement-tray.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { hasGroups, hasHierarchy, toPlacementGroups, toPlacementTree } from './board-placement-view.js'\nimport { boardPlacementProvider, type BoardPlacementItem, type BoardPlacementView } from '@operato/board/board-placement-palette.js'\nimport { dispatchSceneEditOp } from '@operato/scene-ops'\n\n/*\n * board 배치 트레이 = 능력 컴포저 UI — 등록된 BoardPlacementProvider 의 항목을 좌측 트레이로 노출.\n * 항목(엔티티)마다 가능한 **표현들(능력×목적, N:M)** 을 펼쳐 저작이 선택 배치. 1:1 드롭 아님 —\n * 한 엔티티에 여러 표현(모니터/지표/제어/에뮬레이션)을 얹을 수 있다. 표현별 배치 여부 표시(tag::type 스캔).\n * 배치 = board-edit 'add' op(model.tag=엔티티 id) → 라이브 결합(board-provider data(tag:)).\n * board 제품 무변경 — 통합층(board-ui)이 modeller 위에 얹는 일반 오버레이.\n */\n@customElement('board-placement-tray')\nexport class BoardPlacementTray extends LitElement {\n @property({ type: String }) boardId?: string\n @property({ type: Object }) scene: any\n\n @state() private items: BoardPlacementItem[] = []\n @state() private placedKeys: Set<string> = new Set() // `${tag}::${componentType}`\n @state() private placedTags: Set<string> = new Set() // tag (fallback 단일 view 판정)\n @state() private _pos = { x: 12, y: 12 }\n @state() private _collapsed = false\n private _drag: { sx: number; sy: number; ox: number; oy: number } | null = null\n private _timer?: any\n private _dropCount = 0\n /**\n * 어떻게 볼 것인가 — **계층**(소속 트리) 또는 **종류**(같은 것끼리 묶음).\n *\n * 호스트가 소속·종류를 안 주면 둘 다 없으므로 예전처럼 평평한 목록이다. 기본은 계층 — 현장은 어디에\n * 무엇이 있는지로 인식한다.\n */\n @state() private _view: 'tree' | 'kind' = 'tree'\n\n static styles = css`\n :host { display: contents; }\n .tray { position: absolute; top: 12px; left: 12px; width: 210px; max-height: calc(100% - 24px); overflow: auto; z-index: 5; background: #fff; border: 1px solid #d5dce0; border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.18); padding: 10px 11px; display: flex; flex-direction: column; gap: 7px; }\n .tray.collapsed { overflow: visible; max-height: none; }\n /* 표준 스크롤바(앱 consoleStyles 규약: 얇은 5px·투명 트랙·은은한 thumb). */\n .tray { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,.2) transparent; }\n .tray::-webkit-scrollbar { width: 5px; height: 5px; }\n .tray::-webkit-scrollbar-track { background: transparent; }\n .tray::-webkit-scrollbar-thumb { background: rgba(0,0,0,.2); border-radius: 3px; }\n .tray::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,.38); }\n .ttl { font-family: ui-monospace, Menlo, monospace; font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: #57676f; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; touch-action: none; }\n .ttl b { color: #0b8f9c; font-weight: 700; }\n .ttl .grip::before { content: '⠿'; color: #8a959c; font-size: 12px; margin-right: 5px; }\n .rt { display: inline-flex; align-items: center; gap: 6px; }\n .collapse { border: 0; background: transparent; color: #57676f; cursor: pointer; font-size: 12px; line-height: 1; padding: 2px 3px; }\n .collapse:hover { color: #0b8f9c; }\n .hint { font-size: 10.5px; color: #8a959c; font-family: ui-monospace, Menlo, monospace; margin: 0; }\n .auto { border: 1px solid #0b8f9c88; background: #0b8f9c14; color: #0b8f9c; border-radius: 7px; padding: 6px 9px; font-size: 11.5px; font-weight: 640; cursor: pointer; font-family: ui-monospace, Menlo, monospace; }\n .auto:hover { background: #0b8f9c26; }\n .node { border: 1px solid #d5dce0; border-radius: 8px; padding: 6px 8px; display: flex; flex-direction: column; gap: 5px; font-family: ui-monospace, Menlo, monospace; }\n .nhead { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; }\n .nid { font-size: 12px; font-weight: 640; color: #17242c; }\n .nty { font-size: 9.5px; color: #8a959c; }\n .views { display: flex; flex-direction: column; gap: 3px; }\n .view { display: flex; justify-content: space-between; align-items: center; gap: 6px; text-align: left; background: transparent; border: 1px solid #cfe6e9; color: #0b8f9c; border-radius: 6px; padding: 4px 7px; font-size: 11px; cursor: pointer; font-family: ui-monospace, Menlo, monospace; }\n .view:hover { background: #0b8f9c12; border-color: #0b8f9c; }\n .view.placed { color: #1a8f4c; border-color: #b7e0c5; background: #1a8f4c0e; cursor: default; }\n .purpose { font-size: 9px; color: #8a959c; text-transform: uppercase; letter-spacing: .04em; }\n .vsw { display: flex; gap: 4px; margin: 0 0 6px; }\n .vsw button { flex: 1; padding: 3px 0; font-size: 10px; border: 1px solid var(--line, #d5dce0); background: transparent; color: inherit; border-radius: 5px; cursor: pointer; }\n .vsw button.on { background: var(--accent, #17242c); color: #fff; border-color: transparent; }\n /* 맥락으로 끼운 조상 — 놓을 것이 없으니 흐리게. 길을 보여 줄 뿐이다. */\n .node.context > .nhead { opacity: 0.55; }\n .grp { margin-bottom: 6px; }\n .ghead { font-size: 10px; opacity: 0.7; margin: 4px 0 2px; }\n .done { color: #1a8f4c; font-size: 11.5px; font-family: ui-monospace, Menlo, monospace; }\n @media (prefers-color-scheme: dark) {\n .tray { background: #141b21; border-color: #26323a; scrollbar-color: rgba(255,255,255,.18) transparent; }\n .tray::-webkit-scrollbar-thumb { background: rgba(255,255,255,.18); }\n .tray::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.34); }\n .ttl { color: #8b9aa3; } .ttl b { color: #22c3cf; }\n .node { border-color: #26323a; } .nid { color: #dde7ec; } .nty { color: #8b9aa3; }\n .view { border-color: #234; color: #22c3cf; } .view:hover { background: #22c3cf16; }\n .auto { border-color: #22c3cf88; background: #22c3cf1e; color: #22c3cf; }\n }\n `\n\n connectedCallback() {\n super.connectedCallback()\n this.reload()\n this._timer = setInterval(() => this.scanPlaced(), 2000)\n }\n disconnectedCallback() {\n super.disconnectedCallback()\n clearInterval(this._timer)\n window.removeEventListener('pointermove', this._onDrag)\n window.removeEventListener('pointerup', this._endDrag)\n }\n updated(changed: Map<string, unknown>) {\n if (changed.has('boardId')) this.reload()\n else if (changed.has('scene')) this.scanPlaced()\n }\n\n /* 헤더 드래그 이동(편집 공간 가림 방지) — 델타 기반. */\n private startDrag(e: PointerEvent) {\n e.preventDefault()\n this._drag = { sx: e.clientX, sy: e.clientY, ox: this._pos.x, oy: this._pos.y }\n window.addEventListener('pointermove', this._onDrag)\n window.addEventListener('pointerup', this._endDrag)\n }\n private _onDrag = (e: PointerEvent) => {\n const d = this._drag\n if (!d) return\n this._pos = { x: Math.max(0, d.ox + (e.clientX - d.sx)), y: Math.max(0, d.oy + (e.clientY - d.sy)) }\n }\n private _endDrag = () => {\n this._drag = null\n window.removeEventListener('pointermove', this._onDrag)\n window.removeEventListener('pointerup', this._endDrag)\n }\n\n private async reload() {\n const p = boardPlacementProvider()\n if (!p || !this.boardId) { this.items = []; return }\n try { this.items = await p.items(this.boardId) } catch { this.items = [] }\n this.scanPlaced()\n }\n private scanPlaced() {\n const provider = boardPlacementProvider()\n const map = this.scene?.rootContainer?.refidIndexMap\n const keys = new Set<string>()\n const tags = new Set<string>()\n map?.forEach?.((comp: any) => {\n const t = comp?.get?.('tag')\n if (!t) return\n tags.add(String(t))\n /* 표현별 판정 = 호스트가 배치된 컴포넌트에서 되짚어 준 key. 컴포넌트에 저장된 값을 읽지 않는다\n (예전에 `viewKey` 를 심었다가 떼면서, 심는 쪽과 읽는 쪽이 어긋나 판정이 죽은 적이 있다).\n 되짚기를 제공하지 않는 호스트는 `type` 을 key 로 쓴다고 보고 비교한다. */\n const derived = provider?.viewKeyOfComponent?.(comp?.model ?? comp)\n const type = comp?.get?.('type') ?? comp?.model?.type\n if (derived) keys.add(`${t}::${derived}`)\n else if (type) keys.add(`${t}::${type}`)\n })\n this.placedKeys = keys\n this.placedTags = tags\n this.requestUpdate()\n }\n\n /* 항목의 표현들(능력×목적). provider 가 viewsFor 지원하면 그 결과(빈 배열=표현 없음)를 그대로,\n 미지원이면 단일 fallback. → 표현 없는 엔티티는 죽은 버튼 없이 자연히 트레이에서 빠짐. */\n private viewsOf(item: BoardPlacementItem): BoardPlacementView[] {\n const p = boardPlacementProvider()\n if (p?.viewsFor) return p.viewsFor(item.id)\n return [{ key: '', label: '배치' }]\n }\n private viewPlaced(id: string, key: string): boolean {\n return key ? this.placedKeys.has(`${id}::${key}`) : this.placedTags.has(id)\n }\n /* 미배치(=아직 안 놓은 표현이 하나라도 있는) 항목. */\n private get unplaced(): BoardPlacementItem[] {\n return this.items.filter(i => this.viewsOf(i).some(v => !this.viewPlaced(i.id, v.key)))\n }\n\n private place(item: BoardPlacementItem, viewKey: string) {\n const p = boardPlacementProvider()\n if (!p || !this.scene) return\n const comp = p.componentFor(item, viewKey || undefined)\n if (!comp) return\n const off = (this._dropCount++ % 6) * 22\n dispatchSceneEditOp(this.scene, { op: 'add', component: { ...comp, left: 80 + off, top: 80 + off } } as any)\n this.scanPlaced()\n }\n /* 모두 자동 배치 — 각 항목의 첫 미배치 표현을 배치(빠른 시작). */\n private placeAll() {\n for (const it of this.unplaced) {\n const v = this.viewsOf(it).find(v => !this.viewPlaced(it.id, v.key))\n if (v) this.place(it, v.key)\n }\n this.scanPlaced()\n }\n\n /** 이 항목에 아직 안 놓은 표현이 있나 — 트리에서 \"놓을 것이 있는지\" 를 이걸로 가른다. */\n private hasUnplaced(item: BoardPlacementItem): boolean {\n return this.viewsOf(item).some(v => !this.viewPlaced(item.id, v.key))\n }\n\n /** 항목 한 줄 — 이름과 그 항목에 놓을 수 있는 표현들. */\n private renderItem(it: BoardPlacementItem, depth = 0, context = false) {\n const views = this.viewsOf(it)\n return html`<div class=\"node ${context ? 'context' : ''}\" style=${depth ? `margin-left:${depth * 12}px` : ''}>\n <div class=\"nhead\"><span class=\"nid\">${it.label}</span>${it.sub ? html`<span class=\"nty\">${it.sub}</span>` : ''}</div>\n ${views.length\n ? html`<div class=\"views\">\n ${views.map(v => {\n const placed = this.viewPlaced(it.id, v.key)\n return html`<button class=\"view ${placed ? 'placed' : ''}\" ?disabled=${placed}\n @click=${() => this.place(it, v.key)} title=${placed ? '배치됨' : '이 표현으로 배치'}>\n <span>${placed ? '✓ ' : ''}${v.label}</span>${v.sub ? html`<span class=\"purpose\">${v.sub}</span>` : ''}\n </button>`\n })}\n </div>`\n : ''}\n </div>`\n }\n\n render() {\n const p = boardPlacementProvider()\n if (!p || !this.items.length) return html``\n const un = this.unplaced\n const co = this._collapsed\n /* 호스트가 소속·종류를 주지 않으면 뷰 전환이 의미 없다 — 예전처럼 평평하게 보여 준다. */\n const tree = hasHierarchy(this.items)\n const kinds = hasGroups(this.items)\n const view = tree && kinds ? this._view : tree ? 'tree' : kinds ? 'kind' : 'flat'\n\n return html`<div class=\"tray ${co ? 'collapsed' : ''}\" style=\"left:${this._pos.x}px;top:${this._pos.y}px\">\n <div class=\"ttl\" @pointerdown=${(e: PointerEvent) => this.startDrag(e)}>\n <span class=\"grip\">${p.label || '미배치'}</span>\n <span class=\"rt\"><b>${un.length}</b>\n <button class=\"collapse\" @pointerdown=${(e: Event) => e.stopPropagation()} @click=${() => (this._collapsed = !co)} title=${co ? '펼치기' : '접기'}>${co ? '▸' : '▾'}</button>\n </span>\n </div>\n ${co\n ? ''\n : un.length\n ? html`\n ${tree && kinds\n ? html`<div class=\"vsw\" @pointerdown=${(e: Event) => e.stopPropagation()}>\n <button class=\"${view === 'tree' ? 'on' : ''}\" @click=${() => (this._view = 'tree')} title=\"공간 계층으로 — 어디에 무엇이 있나\">계층</button>\n <button class=\"${view === 'kind' ? 'on' : ''}\" @click=${() => (this._view = 'kind')} title=\"종류별로 — 같은 것 여럿에 한 번에\">종류</button>\n </div>`\n : ''}\n <button class=\"auto\" @click=${() => this.placeAll()} title=\"각 항목의 첫 표현을 일괄 배치\">모두 자동 배치</button>\n <div class=\"hint\">항목의 표현을 골라 배치. 여러 표현을 얹을 수 있습니다.</div>\n ${view === 'tree'\n ? /* 놓을 것이 남은 항목만 남기되 **조상은 길로 함께** 보여 준다 — 트리가 끊기면 어디에\n 속한 것인지 알 수 없다. */\n toPlacementTree(this.items, it => this.hasUnplaced(it)).map(r => this.renderItem(r.item, r.depth, r.context))\n : view === 'kind'\n ? toPlacementGroups(un).map(\n g => html`<div class=\"grp\"><div class=\"ghead\">${g.group} <b>${g.items.length}</b></div>\n ${g.items.map(it => this.renderItem(it))}</div>`\n )\n : un.map(it => this.renderItem(it))}`\n : html`<div class=\"done\">✓ 모두 배치됨</div>`}\n </div>`\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board-placement-view.js","sourceRoot":"","sources":["../client/board-placement-view.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 배치 대상 목록을 **두 가지로 보는** 방법 — 순수.\n *\n * 호스트는 항목만 공급하고(`BoardPlacementItem`), 어떻게 볼지는 보드 층이 정한다. 항목에 `parentId`\n * (소속)와 `group`(종류)이 달려 있으면 두 뷰가 성립한다.\n *\n * 계층 뷰 소속 트리 — \"이 자리에 무엇을 놓을까\". 현장 감각으로 찾는다.\n * 종류 뷰 같은 종류끼리 묶음 — \"같은 것 여럿에 한 번에\". 대량 저작의 단위다.\n *\n * 둘 다 **항목을 잃지 않는다.** 목록에서 조용히 빠지면 저작하는 사람은 \"그런 게 없네\" 로 끝난다 —\n * 오류가 아니라서 아무도 모른다.\n */\nimport type { BoardPlacementItem } from '
|
|
1
|
+
{"version":3,"file":"board-placement-view.js","sourceRoot":"","sources":["../client/board-placement-view.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 배치 대상 목록을 **두 가지로 보는** 방법 — 순수.\n *\n * 호스트는 항목만 공급하고(`BoardPlacementItem`), 어떻게 볼지는 보드 층이 정한다. 항목에 `parentId`\n * (소속)와 `group`(종류)이 달려 있으면 두 뷰가 성립한다.\n *\n * 계층 뷰 소속 트리 — \"이 자리에 무엇을 놓을까\". 현장 감각으로 찾는다.\n * 종류 뷰 같은 종류끼리 묶음 — \"같은 것 여럿에 한 번에\". 대량 저작의 단위다.\n *\n * 둘 다 **항목을 잃지 않는다.** 목록에서 조용히 빠지면 저작하는 사람은 \"그런 게 없네\" 로 끝난다 —\n * 오류가 아니라서 아무도 모른다.\n */\nimport type { BoardPlacementItem } from '@operato/board/board-placement-palette.js'\n\nexport interface PlacementRow {\n item: BoardPlacementItem\n /** 들여쓰기 깊이. 화면은 이 값만 곱하면 된다. */\n depth: number\n /** 길을 보여 주려고 끼운 조상 — 그 자체는 놓을 것이 없다. */\n context?: boolean\n}\n\n/**\n * 계층 뷰 — 부모 순서로 늘어놓고 깊이를 함께 준다.\n *\n * `keep` 을 주면 그 항목들만 남기되 **조상은 길로 함께 남긴다**(맥락). 조상을 빼면 트리가 끊겨 어디에\n * 속한 것인지 알 수 없다. 부모를 못 찾은 항목은 버리지 않고 최상위로 올린다.\n */\nexport function toPlacementTree(items: readonly BoardPlacementItem[], keep?: (item: BoardPlacementItem) => boolean): PlacementRow[] {\n const byId = new Map(items.map(i => [i.id, i]))\n const parentOf = (i: BoardPlacementItem) => (i.parentId && byId.has(i.parentId) ? i.parentId : '')\n\n /* 남길 것 + 그 조상들. 조상은 맥락으로 표시해 \"놓을 것이 있는 항목\" 과 구별한다. */\n let visible: Set<string> | null = null\n const context = new Set<string>()\n if (keep) {\n visible = new Set(items.filter(keep).map(i => i.id))\n for (const id of [...visible]) {\n let p = parentOf(byId.get(id)!)\n while (p && !visible.has(p) && !context.has(p)) {\n context.add(p)\n p = parentOf(byId.get(p)!)\n }\n }\n }\n\n const children = new Map<string, BoardPlacementItem[]>()\n for (const it of items) {\n if (visible && !visible.has(it.id) && !context.has(it.id)) continue\n const p = parentOf(it)\n if (!children.has(p)) children.set(p, [])\n children.get(p)!.push(it)\n }\n\n const rows: PlacementRow[] = []\n const walk = (parent: string, depth: number) => {\n for (const it of children.get(parent) ?? []) {\n rows.push({ item: it, depth, ...(context.has(it.id) ? { context: true } : {}) })\n walk(it.id, depth + 1)\n }\n }\n walk('', 0)\n\n return rows\n}\n\n/**\n * 종류 뷰 — `group` 이 있는 항목만 묶는다.\n *\n * `group` 이 없는 항목(계층 단계 같은 것)은 **종류가 아니므로** 여기 나오지 않는다. 그것들은 계층 뷰에서\n * 고른다. 묶음 이름순으로 낸다.\n */\nexport function toPlacementGroups(items: readonly BoardPlacementItem[]): { group: string; items: BoardPlacementItem[] }[] {\n const by = new Map<string, BoardPlacementItem[]>()\n for (const it of items) {\n if (!it.group) continue\n if (!by.has(it.group)) by.set(it.group, [])\n by.get(it.group)!.push(it)\n }\n\n return [...by.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([group, items]) => ({ group, items }))\n}\n\n/** 두 뷰를 쓸 수 있는 목록인가 — 호스트가 소속·종류를 주지 않으면 예전처럼 평평하게 보여 준다. */\nexport function hasHierarchy(items: readonly BoardPlacementItem[]): boolean {\n return items.some(i => !!i.parentId)\n}\n\nexport function hasGroups(items: readonly BoardPlacementItem[]): boolean {\n return items.some(i => !!i.group)\n}\n"]}
|
package/dist-client/index.d.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
export * from './board-provider.js';
|
|
2
1
|
export * from './pages/board-viewer-page.js';
|
|
3
2
|
export * from './pages/board-player-page.js';
|
|
4
3
|
export * from './pages/board-modeller-page.js';
|
|
5
|
-
export * from './pages/board-edit-dispatch.js';
|
|
6
|
-
export * from './pages/board-action-dispatch.js';
|
|
7
4
|
export * from './setting-let/board-view-setting-let.js';
|
|
8
5
|
export * from './apptools/favorite-tool.js';
|
|
9
6
|
export * from './graphql/index.js';
|
package/dist-client/index.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
export * from './board-provider.js';
|
|
2
1
|
export * from './pages/board-viewer-page.js';
|
|
3
2
|
export * from './pages/board-player-page.js';
|
|
4
3
|
export * from './pages/board-modeller-page.js';
|
|
5
4
|
/* AI 패치/액션의 things-scene in-place 적용기 — 순수 함수. 자체 모델러 셸을 가진 호스트
|
|
6
5
|
* (예: operato-twin 의 트윈 모델러)가 같은 적용 규칙을 쓰도록 공개한다(중복 구현 금지). */
|
|
7
|
-
export * from './pages/board-edit-dispatch.js';
|
|
8
|
-
export * from './pages/board-action-dispatch.js';
|
|
9
6
|
export * from './setting-let/board-view-setting-let.js';
|
|
10
7
|
export * from './apptools/favorite-tool.js';
|
|
11
8
|
export * from './graphql/index.js';
|
package/dist-client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AACA,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C;+DAC+D;AAE/D,cAAc,yCAAyC,CAAA;AACvD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA","sourcesContent":["\nexport * from './pages/board-viewer-page.js'\nexport * from './pages/board-player-page.js'\nexport * from './pages/board-modeller-page.js'\n/* AI 패치/액션의 things-scene in-place 적용기 — 순수 함수. 자체 모델러 셸을 가진 호스트\n * (예: operato-twin 의 트윈 모델러)가 같은 적용 규칙을 쓰도록 공개한다(중복 구현 금지). */\n\nexport * from './setting-let/board-view-setting-let.js'\nexport * from './apptools/favorite-tool.js'\nexport * from './graphql/index.js'\nexport * from './viewparts/index.js'\n"]}
|
|
@@ -4,8 +4,8 @@ import '@operato/oops';
|
|
|
4
4
|
import '@things-factory/board-ai';
|
|
5
5
|
import { BoardModeller } from '@operato/board/ox-board-modeller.js';
|
|
6
6
|
import { PageView } from '@operato/shell';
|
|
7
|
+
import { findSceneComponent } from '@operato/scene-ops';
|
|
7
8
|
import '../board-placement-tray.js';
|
|
8
|
-
import { findSceneComponent } from './board-action-dispatch.js';
|
|
9
9
|
export { findSceneComponent };
|
|
10
10
|
export declare class BoardModellerPage extends PageView {
|
|
11
11
|
constructor();
|
|
@@ -128,7 +128,7 @@ export declare class BoardModellerPage extends PageView {
|
|
|
128
128
|
* 'replace' op (보드 전체 교체) 만은 부득이 wholesale 경로로 빠진다 — 이 경우는
|
|
129
129
|
* 사용자에게 명시적으로 안내 후 진행.
|
|
130
130
|
*/
|
|
131
|
-
private
|
|
131
|
+
private onSceneEditPatch;
|
|
132
132
|
/**
|
|
133
133
|
* 'replace' op 적용 — scene 통째로 재생성 (undo 히스토리 초기화).
|
|
134
134
|
*
|
|
@@ -142,7 +142,7 @@ export declare class BoardModellerPage extends PageView {
|
|
|
142
142
|
private _unsubscribeBoardPatches;
|
|
143
143
|
/**
|
|
144
144
|
* 원격 patch 도착 — apply-once 게이트 통과 시 in-place 적용. 자기발신 echo 는 게이트가 차단
|
|
145
|
-
* (로컬
|
|
145
|
+
* (로컬 onSceneEditPatch 가 이미 같은 patchId 를 claim). 원격분은 revert 인버스 미보관(프로토타입).
|
|
146
146
|
*/
|
|
147
147
|
private _onRemotePatch;
|
|
148
148
|
/**
|
|
@@ -164,10 +164,10 @@ export declare class BoardModellerPage extends PageView {
|
|
|
164
164
|
/**
|
|
165
165
|
* AI 가 ephemeral scene action (selection / view / mode) 을 실행하라고 요청.
|
|
166
166
|
*
|
|
167
|
-
*
|
|
167
|
+
* scene-edit-patch 와 별개 채널 — 모델 변경 X, undo 영향 X, dirty flag 무관.
|
|
168
168
|
* things-scene API 직접 호출. 잘못된 action / 누락된 컴포넌트는 console.warn 만.
|
|
169
169
|
*/
|
|
170
|
-
private
|
|
170
|
+
private onSceneActionExecute;
|
|
171
171
|
/**
|
|
172
172
|
* AI 가 만든 컴포넌트를 things-scene 이 기대하는 완전한 형태로 normalize.
|
|
173
173
|
* BoardModeller 에 등록된 template.model (default 값) 위에 AI 응답을 덮어쓴다.
|
|
@@ -11,17 +11,15 @@ import { LoadTracker, SceneSearchEngine } from '@hatiolab/things-scene';
|
|
|
11
11
|
import { OxPropertyEditor } from '@operato/property-editor';
|
|
12
12
|
import { PageView, FontController } from '@operato/shell';
|
|
13
13
|
import { hasPrivilege } from '@things-factory/auth-base/dist-client/index.js';
|
|
14
|
-
import {
|
|
14
|
+
import { applyScenePatchVerbose, dispatchSceneAction, dispatchSceneEditOp, findSceneComponent } from '@operato/scene-ops';
|
|
15
15
|
import { client, gqlContext, subscribe } from '@operato/graphql';
|
|
16
16
|
import { i18next } from '@operato/i18n';
|
|
17
17
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
18
|
-
import { provider } from '
|
|
18
|
+
import { provider } from '@operato/board/board-provider.js';
|
|
19
19
|
import '../board-placement-tray.js'; // 배치 팔레트 오버레이(호스트-등록 provider 있을 때만 표시)
|
|
20
|
-
import { boardPlacementProvider } from '
|
|
20
|
+
import { boardPlacementProvider } from '@operato/board/board-placement-palette.js';
|
|
21
21
|
import components from 'things-scene-components-with-tools';
|
|
22
22
|
import { notify, notifyError } from '../utils/notify-helper.js';
|
|
23
|
-
import { findSceneComponent, dispatchBoardAction } from './board-action-dispatch.js';
|
|
24
|
-
import { dispatchBoardEditOp } from './board-edit-dispatch.js';
|
|
25
23
|
import { PatchApplyGate } from '../utils/patch-apply-gate.js';
|
|
26
24
|
import { diffBoardModel } from '../utils/board-model-diff.js';
|
|
27
25
|
export { findSceneComponent };
|
|
@@ -206,7 +204,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
206
204
|
* 'replace' op (보드 전체 교체) 만은 부득이 wholesale 경로로 빠진다 — 이 경우는
|
|
207
205
|
* 사용자에게 명시적으로 안내 후 진행.
|
|
208
206
|
*/
|
|
209
|
-
this.
|
|
207
|
+
this.onSceneEditPatch = (e) => {
|
|
210
208
|
const { patch, patchId } = e.detail || {};
|
|
211
209
|
if (!patch)
|
|
212
210
|
return;
|
|
@@ -230,7 +228,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
230
228
|
const inverseOps = [];
|
|
231
229
|
const normalize = (c) => this.normalizeComponent(c);
|
|
232
230
|
for (const op of patch.ops) {
|
|
233
|
-
const r =
|
|
231
|
+
const r = dispatchSceneEditOp(scene, op, { normalize });
|
|
234
232
|
if (r.applied) {
|
|
235
233
|
inverseOps.push(...r.inverseOps);
|
|
236
234
|
}
|
|
@@ -247,7 +245,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
247
245
|
.map((op) => {
|
|
248
246
|
if (op.op === 'modify' || op.op === 'remove')
|
|
249
247
|
return `refid=${op.refid}`;
|
|
250
|
-
if (op.op === '
|
|
248
|
+
if (op.op === 'modifyScene')
|
|
251
249
|
return 'modifyBoard (scene root unavailable)';
|
|
252
250
|
return op.op;
|
|
253
251
|
})
|
|
@@ -290,7 +288,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
290
288
|
summary: 'revert',
|
|
291
289
|
confidence: 1
|
|
292
290
|
};
|
|
293
|
-
// hasReplace 면 wholesale, 그렇지 않으면 in-place — 일반
|
|
291
|
+
// hasReplace 면 wholesale, 그렇지 않으면 in-place — 일반 onSceneEditPatch 와 동일
|
|
294
292
|
const hasReplace = reversed.some(op => op.op === 'replace');
|
|
295
293
|
if (hasReplace) {
|
|
296
294
|
this.applyPatchWholesale(reverseFakePatch);
|
|
@@ -309,10 +307,10 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
309
307
|
/**
|
|
310
308
|
* AI 가 ephemeral scene action (selection / view / mode) 을 실행하라고 요청.
|
|
311
309
|
*
|
|
312
|
-
*
|
|
310
|
+
* scene-edit-patch 와 별개 채널 — 모델 변경 X, undo 영향 X, dirty flag 무관.
|
|
313
311
|
* things-scene API 직접 호출. 잘못된 action / 누락된 컴포넌트는 console.warn 만.
|
|
314
312
|
*/
|
|
315
|
-
this.
|
|
313
|
+
this.onSceneActionExecute = (e) => {
|
|
316
314
|
const { actions } = e.detail || {};
|
|
317
315
|
if (!Array.isArray(actions) || actions.length === 0)
|
|
318
316
|
return;
|
|
@@ -322,7 +320,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
322
320
|
let modeChanged = false;
|
|
323
321
|
for (const action of actions) {
|
|
324
322
|
try {
|
|
325
|
-
|
|
323
|
+
dispatchSceneAction(scene, action);
|
|
326
324
|
if (action?.action === 'setSceneMode')
|
|
327
325
|
modeChanged = true;
|
|
328
326
|
}
|
|
@@ -699,10 +697,10 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
699
697
|
try {
|
|
700
698
|
const baseBoard = this.getLiveBoard();
|
|
701
699
|
const inverseOps = baseBoard
|
|
702
|
-
? [{ op: 'replace',
|
|
700
|
+
? [{ op: 'replace', model: JSON.parse(JSON.stringify(baseBoard)) }]
|
|
703
701
|
: [];
|
|
704
|
-
const report =
|
|
705
|
-
const next = report.
|
|
702
|
+
const report = applyScenePatchVerbose(baseBoard, patch);
|
|
703
|
+
const next = report.model;
|
|
706
704
|
if (report.applied.length === 0)
|
|
707
705
|
return;
|
|
708
706
|
const normalized = (next.components || []).map((c) => this.normalizeComponent(c));
|
|
@@ -755,14 +753,14 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
755
753
|
}
|
|
756
754
|
/**
|
|
757
755
|
* 원격 patch 도착 — apply-once 게이트 통과 시 in-place 적용. 자기발신 echo 는 게이트가 차단
|
|
758
|
-
* (로컬
|
|
756
|
+
* (로컬 onSceneEditPatch 가 이미 같은 patchId 를 claim). 원격분은 revert 인버스 미보관(프로토타입).
|
|
759
757
|
*/
|
|
760
758
|
_onRemotePatch(entry) {
|
|
761
759
|
if (!entry?.id)
|
|
762
760
|
return;
|
|
763
761
|
if (!this._patchGate.tryClaim(entry.id))
|
|
764
762
|
return;
|
|
765
|
-
// server PatchEntry.opsJson 은 GraphQLJSON — 이미 parsed
|
|
763
|
+
// server PatchEntry.opsJson 은 GraphQLJSON — 이미 parsed SceneEditOp[] 로 도착(추가 parse 불필요).
|
|
766
764
|
const ops = Array.isArray(entry.opsJson) ? entry.opsJson : [];
|
|
767
765
|
if (!ops.length)
|
|
768
766
|
return;
|
|
@@ -785,7 +783,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
785
783
|
continue;
|
|
786
784
|
}
|
|
787
785
|
try {
|
|
788
|
-
|
|
786
|
+
dispatchSceneEditOp(scene, op, { normalize });
|
|
789
787
|
}
|
|
790
788
|
catch (ex) {
|
|
791
789
|
notifyError(ex);
|
|
@@ -800,7 +798,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
800
798
|
applyInverseOpsInPlace(scene, ops) {
|
|
801
799
|
const normalize = (c) => this.normalizeComponent(c);
|
|
802
800
|
for (const op of ops) {
|
|
803
|
-
|
|
801
|
+
dispatchSceneEditOp(scene, op, { normalize });
|
|
804
802
|
}
|
|
805
803
|
}
|
|
806
804
|
/** 서버 PatchEntry.reverted=true 영속 — fail 해도 UI 는 이미 되돌렸으니 warn 만. */
|
|
@@ -942,9 +940,9 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
942
940
|
.selectedRefids=${this.extractSelectedRefids()}
|
|
943
941
|
@session-switch=${this.onChatSessionSwitch}
|
|
944
942
|
@session-create=${this.onChatSessionCreate}
|
|
945
|
-
@
|
|
943
|
+
@scene-edit-patch=${this.onSceneEditPatch}
|
|
946
944
|
@board-edit-revert=${this.onBoardEditRevert}
|
|
947
|
-
@
|
|
945
|
+
@scene-action-execute=${this.onSceneActionExecute}>
|
|
948
946
|
</ox-assistant-chat>
|
|
949
947
|
</div>
|
|
950
948
|
`
|