import '@material/web/icon/icon.js'; import 'things-scene-components'; import '@operato/board/ox-board-modeller.js'; import '@operato/oops'; import '@things-factory/board-ai'; import { BoardModeller } from '@operato/board/ox-board-modeller.js'; import { PageView } from '@operato/shell'; import { findSceneComponent } from '@operato/scene-ops'; import { OxPrompt } from '@operato/popup/ox-prompt.js'; import '../board-placement-tray.js'; export { findSceneComponent }; export declare class BoardModellerPage extends PageView { constructor(); static styles: import("lit").CSSResult[]; boardId?: string | null; boardName?: string; private toolbarMode; model: any; selected: any; mode?: number; hideProperty?: boolean; overlay?: string | null; scene: any; componentGroupList?: any[]; propertyEditor: any; preparing?: boolean; /** * The AI conversation open for this board - the framework's dock owns it (design/ai/shared-dock.md) and tells * this page through the board surface's `sessionChanged`. Direct edits are recorded under it (C3). */ private chatSessionId?; /** * 적용된 patch 별 inverse op 시퀀스 — Revert 기능용. * * Map: 각 patch 적용 시점에 계산해 저장. revert 클릭 * 시 역순 in-place 적용. 페이지 reload 시 손실 — reload 후 revert 시도는 * notify 로 안내 (메모리 외 영속은 별개 작업). */ private patchInverses; private _patchGate; private _patchSub?; private _syncedModel; /** 모델러에 등록된 컴포넌트 type 목록 — chat 호출 시 LLM 에 전달 (lazy cache) */ private _knownTypesCache?; /** 등록된 type 의 group 분류 — categories 로 LLM 에 전달 */ private _knownCategoriesCache?; private get knownTypes(); private get knownCategories(); /** * type 별 유효 속성 스킴 — LLM 이 정확한 컴포넌트를 만들도록 (lazy cache). * * 핵심: 좌표 표현은 type 마다 다르다 (left/top vs cx/cy vs points 등). * 따라서 OMIT 하지 않고 `geometryKeys` 로 별도 노출 → LLM 이 type 별 좌표 키 사용 가능. */ private _componentSchemasCache?; private get componentSchemas(); private board; /** When the board was last saved, as the server said (ADR-0098) - the next save must find it unchanged. */ private savedAt; /** A draft was put back and not saved yet; the scene's own count of unsaved steps starts from it. */ private restoredDraft; /** The save a restored draft began from - the next save goes out against it, not the newer one. */ private draftBase; private drafts; private _loadTracker?; /** Something is not saved: an edit since the last save, or a draft put back and not saved yet. */ private unsaved; private _draftScene; private _onEdit; private _watchScene; private _fontCtrl; modeller?: BoardModeller; get context(): { title: string | undefined; help: string; widebleed: boolean; heading: boolean; }; get oopsNote(): { icon: string; title: string; description: string; }; refresh(): Promise; /** * 모델러의 selected (things-scene Component 인스턴스 배열) 에서 refid 추출. * * refid 는 things-scene 이 모든 컴포넌트에 자동 발급하는 universal numeric handle. * AI 의 selection 표현은 항상 refid 기반 (id 는 데이터 바인딩 이름이며 unique 가 * 아니라 targeting 에 부적합). */ private extractSelectedRefids; /** * AI 채팅 mention popup (`#`) 검색 위임 — things-scene SceneSearchEngine 으로 wrap. * * F-key Finder 와 동일한 검색 룰 (id → name → tag → type → class → text → data → property) * 을 그대로 mention popup 에 재사용. 컴포넌트 트리를 직접 순회하므로 model JSON 으로 * 변환할 필요 없음 — 일관성 + 성능 이점. * * 반환 구조 (FilterResult) 는 chat 의 mention-popup 표시 모델. host 는 things-scene * Component 에서 refid/id/type/tag/class/text 를 뽑아 candidate 로 채워 넘긴다. */ private searchSceneForMention; /** * 라이브 보드 모델 — things-scene 캔버스가 들고 있는 가장 최신 상태의 deep clone. * * 사용자 수작업 편집은 scene.model 에만 반영되고 this.model property 에는 자동 * 동기화되지 않는다. AI 한테 보드 상태를 넘기거나 patch 를 적용할 때는 반드시 * 이쪽을 쓴다. * * id 와 refid 는 별개 필드 — 컴포넌트는 model.id (선택, 사용자/AI 가 명시 설정) * 와 model.refid (필수, things-scene 자동 발급) 를 둘 다 가질 수 있고 AI 가 * 양쪽을 구별해서 다룬다 (tool 인자가 분리됨). 따라서 여기서는 model 을 mutate * 하지 않고 raw deep clone 만 반환. */ private getLiveBoard; /** * 채팅에서 patch 도착 — things-scene 에 in-place 적용. * * 핵심: `this.model = next` 로 모델을 통째 교체하면 ox-scene-viewer 가 scene 을 * dispose → 재생성 → undo 히스토리/dirty 플래그가 모두 초기화된다. * 따라서 일반 add/modify/remove 는 scene 의 in-place mutation API 로 적용 → * commander 가 자동으로 snapshot push → CMD+Z 와 저장 안내 팝업이 정상 동작. * * 'replace' op (보드 전체 교체) 만은 부득이 wholesale 경로로 빠진다 — 이 경우는 * 사용자에게 명시적으로 안내 후 진행. */ private onSceneEditPatch; /** * 'replace' op 적용 — scene 통째로 재생성 (undo 히스토리 초기화). * * AI 가 보드 전체 재구성을 요청한 경우. 사용자에게 history 손실 안내. * board-import 데이터 wholesale 적용 등 드문 케이스에 한정. * * Revert 지원 — replace 직전 보드 model 을 inverse 로 보관 (또 다른 replace). */ private applyPatchWholesale; private _subscribeBoardPatches; private _unsubscribeBoardPatches; /** * 원격 patch 도착 — apply-once 게이트 통과 시 in-place 적용. 자기발신 echo 는 게이트가 차단 * (로컬 onSceneEditPatch 가 이미 같은 patchId 를 claim). 원격분은 revert 인버스 미보관(프로토타입). */ private _onRemotePatch; /** * 채팅에서 board-edit-revert 도착 — 누적된 inverse 를 역순 in-place 적용. * * - 메모리상 patchInverses Map 만 본다. 페이지 reload 후엔 inverse 가 없어 * 되돌릴 수 없음 (사용자에게 안내). * - 성공 시 서버 revertPatch mutation 호출 → PatchEntry.reverted=true 영속. * - in-place 적용이라 undo/dirty/selection 보존 (chatViaTools 의 모든 강점 그대로). */ private onBoardEditRevert; /** * inverse op 시퀀스를 things-scene 위에 in-place 적용 (snapshot/undo 보존). * dispatcher 재사용 — inverse 누적은 무시 (revert 의 revert 는 redo, 별개 기능). */ private applyInverseOpsInPlace; /** 서버 PatchEntry.reverted=true 영속 — fail 해도 UI 는 이미 되돌렸으니 warn 만. */ private recordRevertOnServer; /** * AI 가 ephemeral scene action (selection / view / mode) 을 실행하라고 요청. * * scene-edit-patch 와 별개 채널 — 모델 변경 X, undo 영향 X, dirty flag 무관. * things-scene API 직접 호출. 잘못된 action / 누락된 컴포넌트는 console.warn 만. */ private onSceneActionExecute; /** * AI 가 만든 컴포넌트를 things-scene 이 기대하는 완전한 형태로 normalize. * BoardModeller 에 등록된 template.model (default 값) 위에 AI 응답을 덮어쓴다. * 이 단계 없이 things-scene 에 넣으면 필수 속성 누락 시 render 에서 crash 가능 * (예: gauge-circle 의 value 가 undefined → toString() TypeError). */ private normalizeComponent; private findTemplateByType; updated(changes: any): Promise; disconnectedCallback(): void; /** * This board, told to the framework's AI dock (design/ai/shared-dock.md §3). The dock owns the * conversation - sessions, modes, the manager - and hands the chat's scene edits back to this page. * Decided 2026-09-25: the modeller no longer carries its own "AI" button and panel; the band's forum * button opens the same dock as every other screen. */ private provideAssistantBoard; pageUpdated(changes: any, lifecycle: any): void; render(): import("lit-html").TemplateResult<1>; /** Saves the board; true when the server took it. A failure is told to the person and answered false. */ updateBoard(): Promise; saveBoard(): Promise; /** * C3: 마지막 동기 모델 → 현재 모델 diff 를 recordDirectPatch 로 기록(=같은 board 구독자 방송). * 반환된 patchId 를 게이트에 claim → 자기발신 echo skip. baseline 갱신. * sessionId 없으면(AI 세션 미연결) skip — 협업 비활성, 편집은 정상. */ private _captureDirectEdits; get addressDirty(): boolean; get addressEditsKept(): boolean; get deactivation(): boolean | Parameters[0]; discardAddressEdits(): void; }