@things-factory/board-ui 10.1.46 → 10.1.50
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/pages/board-list-page.js +1 -1
- package/dist-client/pages/board-list-page.js.map +1 -1
- package/dist-client/pages/board-modeller-page.d.ts +12 -24
- package/dist-client/pages/board-modeller-page.js +42 -234
- package/dist-client/pages/board-modeller-page.js.map +1 -1
- package/dist-client/pages/board-template/board-template-list-page.js +1 -1
- package/dist-client/pages/board-template/board-template-list-page.js.map +1 -1
- package/dist-client/pages/play-list-page.js +1 -1
- package/dist-client/pages/play-list-page.js.map +1 -1
- package/dist-client/pages/theme/theme-list-page.js +2 -2
- package/dist-client/pages/theme/theme-list-page.js.map +1 -1
- package/dist-client/styles/board-card-styles.js +1 -4
- package/dist-client/styles/board-card-styles.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/board-basic-info.js +1 -1
- package/dist-client/viewparts/board-basic-info.js.map +1 -1
- package/dist-client/viewparts/board-template-info.js +1 -1
- package/dist-client/viewparts/board-template-info.js.map +1 -1
- package/package.json +7 -6
- package/translations/en.json +0 -16
- package/translations/ja.json +88 -16
- package/translations/ko.json +0 -16
- package/translations/ms.json +94 -20
- package/translations/zh.json +89 -16
|
@@ -15,6 +15,7 @@ import { PageView, FontController } from '@operato/shell';
|
|
|
15
15
|
import { hasPrivilege } from '@things-factory/auth-base/dist-client/index.js';
|
|
16
16
|
import { applyScenePatchVerbose, dispatchSceneAction, dispatchSceneEditOp, findSceneComponent } from '@operato/scene-ops';
|
|
17
17
|
import { client, gqlContext, subscribe } from '@operato/graphql';
|
|
18
|
+
import { clearAssistantSurface, provideAssistantSurface } from '@things-factory/ai-assistant/dist-client/index.js';
|
|
18
19
|
import { i18next } from '@operato/i18n';
|
|
19
20
|
import { OxPrompt } from '@operato/popup/ox-prompt.js';
|
|
20
21
|
import { provider } from '@operato/board/board-provider.js';
|
|
@@ -77,10 +78,6 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
77
78
|
this.overlay = null;
|
|
78
79
|
this.scene = null;
|
|
79
80
|
this.componentGroupList = BoardModeller.groups;
|
|
80
|
-
/** 보드의 모든 ChatSession (탭으로 표시). */
|
|
81
|
-
this.chatSessions = [];
|
|
82
|
-
/** AI 채팅 패널 열림 상태 (기본 닫힘) */
|
|
83
|
-
this.aiPanelOpen = false;
|
|
84
81
|
/**
|
|
85
82
|
* 적용된 patch 별 inverse op 시퀀스 — Revert 기능용.
|
|
86
83
|
*
|
|
@@ -95,67 +92,6 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
95
92
|
this._syncedModel = null;
|
|
96
93
|
this.board = null;
|
|
97
94
|
this._fontCtrl = new FontController(this);
|
|
98
|
-
/** `@` mention popup 의 도메인 사용자 후보 — chat 의 userProvider prop 으로 wire.
|
|
99
|
-
* query 변할 때마다 호출되므로 server query (`domainUsersForMention`) 를 그대로 위임. */
|
|
100
|
-
this.fetchDomainUsersForMention = async (query) => {
|
|
101
|
-
try {
|
|
102
|
-
const result = await client.query({
|
|
103
|
-
query: gql `
|
|
104
|
-
query DomainUsersForMention($query: String, $limit: Int) {
|
|
105
|
-
domainUsersForMention(query: $query, limit: $limit) {
|
|
106
|
-
id
|
|
107
|
-
name
|
|
108
|
-
email
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
`,
|
|
112
|
-
variables: { query: query || null, limit: 50 },
|
|
113
|
-
context: gqlContext(),
|
|
114
|
-
fetchPolicy: 'cache-first'
|
|
115
|
-
});
|
|
116
|
-
return result.data?.domainUsersForMention ?? [];
|
|
117
|
-
}
|
|
118
|
-
catch (ex) {
|
|
119
|
-
// 조용히 — chat panel 이 빈 결과로 처리
|
|
120
|
-
console.warn('[board-modeller-page] fetchDomainUsersForMention failed:', ex);
|
|
121
|
-
return [];
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
/** 채팅 패널의 탭 클릭 — 활성 세션 전환. */
|
|
125
|
-
this.onChatSessionSwitch = (e) => {
|
|
126
|
-
const newId = e.detail?.sessionId;
|
|
127
|
-
if (typeof newId === 'string' && newId !== this.chatSessionId) {
|
|
128
|
-
this.chatSessionId = newId;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
/** 채팅 패널의 "+" 버튼 — 새 세션 생성 후 활성으로 전환. */
|
|
132
|
-
this.onChatSessionCreate = async () => {
|
|
133
|
-
if (!this.boardId)
|
|
134
|
-
return;
|
|
135
|
-
try {
|
|
136
|
-
const created = await client.mutate({
|
|
137
|
-
mutation: gql `
|
|
138
|
-
mutation CreateChatSession($boardId: String!, $name: String) {
|
|
139
|
-
createChatSession(boardId: $boardId, name: $name) {
|
|
140
|
-
id
|
|
141
|
-
name
|
|
142
|
-
createdAt
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
`,
|
|
146
|
-
variables: { boardId: this.boardId, name: null },
|
|
147
|
-
context: gqlContext()
|
|
148
|
-
});
|
|
149
|
-
const s = created.data?.createChatSession;
|
|
150
|
-
if (s) {
|
|
151
|
-
this.chatSessions = [...this.chatSessions, s];
|
|
152
|
-
this.chatSessionId = s.id;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
catch (ex) {
|
|
156
|
-
notifyError(ex);
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
95
|
/**
|
|
160
96
|
* AI 채팅 mention popup (`#`) 검색 위임 — things-scene SceneSearchEngine 으로 wrap.
|
|
161
97
|
*
|
|
@@ -374,54 +310,6 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
374
310
|
flex: 1;
|
|
375
311
|
}
|
|
376
312
|
|
|
377
|
-
/*
|
|
378
|
-
* The assistant's door and its column, on the scheme's own surfaces. The door is a quiet
|
|
379
|
-
* tonal pill on the canvas, like the save FAB beside it; open, it takes the pressed tone.
|
|
380
|
-
*/
|
|
381
|
-
.ai-toggle {
|
|
382
|
-
position: absolute;
|
|
383
|
-
right: 12px;
|
|
384
|
-
bottom: 12px;
|
|
385
|
-
z-index: 10;
|
|
386
|
-
display: inline-flex;
|
|
387
|
-
align-items: center;
|
|
388
|
-
gap: 6px;
|
|
389
|
-
height: 36px;
|
|
390
|
-
padding: 0 14px 0 11px;
|
|
391
|
-
border: 1px solid var(--md-sys-color-outline-variant);
|
|
392
|
-
border-radius: 999px;
|
|
393
|
-
background: var(--md-sys-color-surface-container-high);
|
|
394
|
-
color: var(--md-sys-color-on-surface);
|
|
395
|
-
font: 500 0.8rem/1 var(--theme-font, Roboto, sans-serif);
|
|
396
|
-
cursor: pointer;
|
|
397
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
|
398
|
-
transition: background 0.15s, transform 0.1s;
|
|
399
|
-
}
|
|
400
|
-
.ai-toggle md-icon {
|
|
401
|
-
--md-icon-size: 18px;
|
|
402
|
-
color: var(--md-sys-color-primary);
|
|
403
|
-
font-variation-settings: 'FILL' 1;
|
|
404
|
-
}
|
|
405
|
-
.ai-toggle:hover { background: var(--md-sys-color-surface-container-highest); }
|
|
406
|
-
.ai-toggle:active { transform: scale(0.97); }
|
|
407
|
-
.ai-toggle.open {
|
|
408
|
-
border-color: transparent;
|
|
409
|
-
background: var(--md-sys-color-secondary-container);
|
|
410
|
-
color: var(--md-sys-color-on-secondary-container);
|
|
411
|
-
}
|
|
412
|
-
.ai-toggle.open md-icon { color: inherit; font-variation-settings: 'FILL' 0; }
|
|
413
|
-
|
|
414
|
-
.ai-panel {
|
|
415
|
-
flex: 0 0 340px;
|
|
416
|
-
display: flex;
|
|
417
|
-
min-width: 0;
|
|
418
|
-
border-left: 1px solid var(--md-sys-color-outline-variant);
|
|
419
|
-
background: var(--md-sys-color-surface-container-low);
|
|
420
|
-
color: var(--md-sys-color-on-surface);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
ox-assistant-chat { flex: 1; }
|
|
424
|
-
|
|
425
313
|
ox-oops-note {
|
|
426
314
|
display: block;
|
|
427
315
|
position: absolute;
|
|
@@ -430,14 +318,6 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
430
318
|
transform: translate(-50%, -50%);
|
|
431
319
|
}
|
|
432
320
|
|
|
433
|
-
@media (max-width: 900px) {
|
|
434
|
-
.ai-panel {
|
|
435
|
-
flex-basis: 100%;
|
|
436
|
-
position: absolute;
|
|
437
|
-
inset: 0;
|
|
438
|
-
z-index: 20;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
321
|
`
|
|
442
322
|
]; }
|
|
443
323
|
get knownTypes() {
|
|
@@ -598,14 +478,9 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
598
478
|
this.model = {
|
|
599
479
|
...this.board.model
|
|
600
480
|
};
|
|
601
|
-
|
|
602
|
-
// sessionId/sessions 변화 감지 → history 비우고 새로 로드). 패널 열린 채 보드 옮겼다면
|
|
603
|
-
// 새 보드의 세션 리스트를 즉시 ensure (fire-and-forget — 에러는 ensureChatSession 처리).
|
|
481
|
+
/* Another board is another conversation - the dock reopens for the new surface. */
|
|
604
482
|
this.chatSessionId = undefined;
|
|
605
|
-
this.
|
|
606
|
-
if (this.aiPanelOpen && this.boardId) {
|
|
607
|
-
this.ensureChatSession();
|
|
608
|
-
}
|
|
483
|
+
this.provideAssistantBoard();
|
|
609
484
|
}
|
|
610
485
|
catch (ex) {
|
|
611
486
|
notifyError(ex);
|
|
@@ -615,66 +490,6 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
615
490
|
this.updateContext();
|
|
616
491
|
}
|
|
617
492
|
}
|
|
618
|
-
/** 패널 토글 시 호출 — 열 때만 chatSession 보장 (세션 리스트 로드 + 활성 세션 결정) */
|
|
619
|
-
async toggleAIPanel() {
|
|
620
|
-
const willOpen = !this.aiPanelOpen;
|
|
621
|
-
if (willOpen && this.boardId) {
|
|
622
|
-
await this.ensureChatSession();
|
|
623
|
-
}
|
|
624
|
-
this.aiPanelOpen = willOpen;
|
|
625
|
-
}
|
|
626
|
-
/** 보드의 모든 ChatSession 목록을 로드 + 활성 세션 보장.
|
|
627
|
-
* - 세션이 0 개면 startBoardAISession 으로 첫 세션 생성 (단일 세션 UX 호환)
|
|
628
|
-
* - 활성 세션 미설정 시 첫 번째 세션을 활성으로
|
|
629
|
-
* - 이미 활성 세션 set 돼있고 list 에 포함되면 유지 */
|
|
630
|
-
async ensureChatSession() {
|
|
631
|
-
if (!this.boardId)
|
|
632
|
-
return;
|
|
633
|
-
try {
|
|
634
|
-
const result = await client.query({
|
|
635
|
-
query: gql `
|
|
636
|
-
query ChatSessionsByBoard($boardId: String!) {
|
|
637
|
-
chatSessionsByBoard(boardId: $boardId) {
|
|
638
|
-
id
|
|
639
|
-
name
|
|
640
|
-
createdAt
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
`,
|
|
644
|
-
variables: { boardId: this.boardId },
|
|
645
|
-
context: gqlContext(),
|
|
646
|
-
fetchPolicy: 'network-only'
|
|
647
|
-
});
|
|
648
|
-
let sessions = result.data?.chatSessionsByBoard ?? [];
|
|
649
|
-
if (sessions.length === 0) {
|
|
650
|
-
// 첫 세션 ensure (idempotent)
|
|
651
|
-
const created = await client.mutate({
|
|
652
|
-
mutation: gql `
|
|
653
|
-
mutation StartBoardAISession($boardId: String!) {
|
|
654
|
-
startBoardAISession(boardId: $boardId) {
|
|
655
|
-
id
|
|
656
|
-
name
|
|
657
|
-
createdAt
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
`,
|
|
661
|
-
variables: { boardId: this.boardId },
|
|
662
|
-
context: gqlContext()
|
|
663
|
-
});
|
|
664
|
-
const s = created.data?.startBoardAISession;
|
|
665
|
-
if (s)
|
|
666
|
-
sessions = [s];
|
|
667
|
-
}
|
|
668
|
-
this.chatSessions = sessions;
|
|
669
|
-
// 활성 세션 결정 — 기존 active 가 list 에 있으면 유지, 아니면 첫 번째
|
|
670
|
-
if (!this.chatSessionId || !sessions.some(s => s.id === this.chatSessionId)) {
|
|
671
|
-
this.chatSessionId = sessions[0]?.id;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
catch (ex) {
|
|
675
|
-
notifyError(ex);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
493
|
/**
|
|
679
494
|
* 모델러의 selected (things-scene Component 인스턴스 배열) 에서 refid 추출.
|
|
680
495
|
*
|
|
@@ -889,6 +704,44 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
889
704
|
disconnectedCallback() {
|
|
890
705
|
super.disconnectedCallback();
|
|
891
706
|
this._unsubscribeBoardPatches();
|
|
707
|
+
clearAssistantSurface(this);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* This board, told to the framework's AI dock (design/ai/shared-dock.md §3). The dock owns the
|
|
711
|
+
* conversation - sessions, modes, the manager - and hands the chat's scene edits back to this page.
|
|
712
|
+
* Decided 2026-09-25: the modeller no longer carries its own "AI" button and panel; the band's forum
|
|
713
|
+
* button opens the same dock as every other screen.
|
|
714
|
+
*/
|
|
715
|
+
provideAssistantBoard() {
|
|
716
|
+
if (!this.boardId)
|
|
717
|
+
return;
|
|
718
|
+
const boardId = this.boardId;
|
|
719
|
+
provideAssistantSurface(this, {
|
|
720
|
+
key: `board:${boardId}`,
|
|
721
|
+
subject: 'board',
|
|
722
|
+
icon: 'dashboard',
|
|
723
|
+
label: () => this.boardName || i18next.t('text.ai_assistant'),
|
|
724
|
+
detail: () => i18next.t('label.board', { defaultValue: 'board' }),
|
|
725
|
+
anchor: () => ({ anchorType: 'board', anchorId: boardId }),
|
|
726
|
+
sessionChanged: sessionId => (this.chatSessionId = sessionId || undefined),
|
|
727
|
+
board: {
|
|
728
|
+
chatEndpoint: 'boardAIChat',
|
|
729
|
+
currentBoard: () => this.model,
|
|
730
|
+
boardProvider: () => this.getLiveBoard(),
|
|
731
|
+
selectedRefids: () => this.extractSelectedRefids(),
|
|
732
|
+
knownTypes: () => this.knownTypes,
|
|
733
|
+
categories: () => this.knownCategories,
|
|
734
|
+
componentSchemas: () => this.componentSchemas,
|
|
735
|
+
searchProvider: this.searchSceneForMention,
|
|
736
|
+
applyPatch: (detail) => {
|
|
737
|
+
/* The modeller tells the person which ops missed itself, so the dock is told none to repeat. */
|
|
738
|
+
this.onSceneEditPatch(new CustomEvent('scene-edit-patch', { detail }));
|
|
739
|
+
return { applied: 0, missed: 0 };
|
|
740
|
+
},
|
|
741
|
+
revertPatch: (patchId) => this.onBoardEditRevert(new CustomEvent('board-edit-revert', { detail: { patchId } })),
|
|
742
|
+
applyAction: (detail) => this.onSceneActionExecute(new CustomEvent('scene-action-execute', { detail }))
|
|
743
|
+
}
|
|
744
|
+
});
|
|
892
745
|
}
|
|
893
746
|
pageUpdated(changes, lifecycle) {
|
|
894
747
|
if (this.active) {
|
|
@@ -898,6 +751,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
898
751
|
this.boardId = lifecycle.resourceId;
|
|
899
752
|
}
|
|
900
753
|
else {
|
|
754
|
+
clearAssistantSurface(this);
|
|
901
755
|
this.boardId = null;
|
|
902
756
|
this.board = null;
|
|
903
757
|
this.model = null;
|
|
@@ -946,41 +800,7 @@ let BoardModellerPage = class BoardModellerPage extends PageView {
|
|
|
946
800
|
${this.scene && boardPlacementProvider()
|
|
947
801
|
? html `<board-placement-tray .boardId=${this.boardId} .scene=${this.scene}></board-placement-tray>`
|
|
948
802
|
: nothing}
|
|
949
|
-
${this.boardId
|
|
950
|
-
? html `
|
|
951
|
-
<button
|
|
952
|
-
class="ai-toggle ${this.aiPanelOpen ? 'open' : ''}"
|
|
953
|
-
title="${i18next.t('text.ai_assistant') || 'AI Assistant'}"
|
|
954
|
-
@click=${this.toggleAIPanel}>
|
|
955
|
-
<md-icon>${this.aiPanelOpen ? 'close' : 'auto_awesome'}</md-icon>AI
|
|
956
|
-
</button>
|
|
957
|
-
`
|
|
958
|
-
: nothing}
|
|
959
803
|
</div>
|
|
960
|
-
${this.aiPanelOpen
|
|
961
|
-
? html `
|
|
962
|
-
<div class="ai-panel">
|
|
963
|
-
<ox-assistant-chat
|
|
964
|
-
chat-endpoint="boardAIChat"
|
|
965
|
-
.sessionId=${this.chatSessionId}
|
|
966
|
-
.sessions=${this.chatSessions}
|
|
967
|
-
.currentBoard=${this.model}
|
|
968
|
-
.boardProvider=${() => this.getLiveBoard()}
|
|
969
|
-
.searchProvider=${this.searchSceneForMention}
|
|
970
|
-
.userProvider=${this.fetchDomainUsersForMention}
|
|
971
|
-
.knownTypes=${this.knownTypes}
|
|
972
|
-
.categories=${this.knownCategories}
|
|
973
|
-
.componentSchemas=${this.componentSchemas}
|
|
974
|
-
.selectedRefids=${this.extractSelectedRefids()}
|
|
975
|
-
@session-switch=${this.onChatSessionSwitch}
|
|
976
|
-
@session-create=${this.onChatSessionCreate}
|
|
977
|
-
@scene-edit-patch=${this.onSceneEditPatch}
|
|
978
|
-
@board-edit-revert=${this.onBoardEditRevert}
|
|
979
|
-
@scene-action-execute=${this.onSceneActionExecute}>
|
|
980
|
-
</ox-assistant-chat>
|
|
981
|
-
</div>
|
|
982
|
-
`
|
|
983
|
-
: nothing}
|
|
984
804
|
`;
|
|
985
805
|
}
|
|
986
806
|
async updateBoard() {
|
|
@@ -1118,18 +938,6 @@ __decorate([
|
|
|
1118
938
|
property({ type: Boolean }),
|
|
1119
939
|
__metadata("design:type", Boolean)
|
|
1120
940
|
], BoardModellerPage.prototype, "preparing", void 0);
|
|
1121
|
-
__decorate([
|
|
1122
|
-
state(),
|
|
1123
|
-
__metadata("design:type", String)
|
|
1124
|
-
], BoardModellerPage.prototype, "chatSessionId", void 0);
|
|
1125
|
-
__decorate([
|
|
1126
|
-
state(),
|
|
1127
|
-
__metadata("design:type", Array)
|
|
1128
|
-
], BoardModellerPage.prototype, "chatSessions", void 0);
|
|
1129
|
-
__decorate([
|
|
1130
|
-
state(),
|
|
1131
|
-
__metadata("design:type", Object)
|
|
1132
|
-
], BoardModellerPage.prototype, "aiPanelOpen", void 0);
|
|
1133
941
|
__decorate([
|
|
1134
942
|
query('ox-board-modeller'),
|
|
1135
943
|
__metadata("design:type", BoardModeller)
|