@things-factory/board-ui 10.0.0-zeta.3 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist-client/board-placement-palette.d.ts +22 -0
  2. package/dist-client/board-placement-palette.js +8 -0
  3. package/dist-client/board-placement-palette.js.map +1 -0
  4. package/dist-client/board-placement-tray.d.ts +28 -0
  5. package/dist-client/board-placement-tray.js +234 -0
  6. package/dist-client/board-placement-tray.js.map +1 -0
  7. package/dist-client/graphql/board.d.ts +1 -1
  8. package/dist-client/graphql/board.js +5 -4
  9. package/dist-client/graphql/board.js.map +1 -1
  10. package/dist-client/index.d.ts +2 -0
  11. package/dist-client/index.js +4 -0
  12. package/dist-client/index.js.map +1 -1
  13. package/dist-client/pages/board-list-page.js +1 -1
  14. package/dist-client/pages/board-list-page.js.map +1 -1
  15. package/dist-client/pages/board-modeller-page.d.ts +1 -0
  16. package/dist-client/pages/board-modeller-page.js +5 -0
  17. package/dist-client/pages/board-modeller-page.js.map +1 -1
  18. package/dist-client/themes/board-theme.css +0 -14
  19. package/dist-client/tsconfig.tsbuildinfo +1 -1
  20. package/dist-client/viewparts/board-basic-info.js +13 -1
  21. package/dist-client/viewparts/board-basic-info.js.map +1 -1
  22. package/dist-client/viewparts/board-versions.d.ts +17 -1
  23. package/dist-client/viewparts/board-versions.js +314 -70
  24. package/dist-client/viewparts/board-versions.js.map +1 -1
  25. package/dist-server/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +21 -21
  27. package/translations/en.json +10 -1
  28. package/translations/ja.json +10 -1
  29. package/translations/ko.json +10 -1
  30. package/translations/ms.json +10 -1
  31. package/translations/zh.json +10 -1
@@ -0,0 +1,22 @@
1
+ export interface BoardPlacementItem {
2
+ id: string;
3
+ label: string;
4
+ sub?: string;
5
+ }
6
+ /** 한 항목의 가능한 표현(N:M) — 능력×목적별 view. key=컴포넌트 타입(placed 판정 키). */
7
+ export interface BoardPlacementView {
8
+ key: string;
9
+ label: string;
10
+ sub?: string;
11
+ }
12
+ export interface BoardPlacementProvider {
13
+ label?: string;
14
+ /** 이 board 의 배치 후보(전체). 비어있으면 트레이 숨김. boardId 로 컨텍스트 해소. */
15
+ items(boardId: string): BoardPlacementItem[] | Promise<BoardPlacementItem[]>;
16
+ /** 항목의 가능한 표현들(능력×목적, N:M). 없으면(미지정) 단일 componentFor 로 동작. */
17
+ viewsFor?(itemId: string): BoardPlacementView[];
18
+ /** 선택한 view(또는 기본)로 추가할 things-scene 컴포넌트 model(반드시 `tag` 포함). null=표현 없음. */
19
+ componentFor(item: BoardPlacementItem, viewKey?: string): any;
20
+ }
21
+ export declare function registerBoardPlacementProvider(p: BoardPlacementProvider): void;
22
+ export declare function boardPlacementProvider(): BoardPlacementProvider | null;
@@ -0,0 +1,8 @@
1
+ let _provider = null;
2
+ export function registerBoardPlacementProvider(p) {
3
+ _provider = p;
4
+ }
5
+ export function boardPlacementProvider() {
6
+ return _provider;
7
+ }
8
+ //# sourceMappingURL=board-placement-palette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board-placement-palette.js","sourceRoot":"","sources":["../client/board-placement-palette.ts"],"names":[],"mappings":"AAgCA,IAAI,SAAS,GAAkC,IAAI,CAAA;AACnD,MAAM,UAAU,8BAA8B,CAAC,CAAyB;IACtE,SAAS,GAAG,CAAC,CAAA;AACf,CAAC;AACD,MAAM,UAAU,sBAAsB;IACpC,OAAO,SAAS,CAAA;AAClB,CAAC","sourcesContent":["/*\n * board 배치 팔레트 — 일반(표현-무관) 확장점. 호스트가 \"보드에 배치할 항목\"을 공급하고,\n * board-modeller 가 트레이로 노출 + place 시 컴포넌트를 생성한다. board 제품(@operato/board)은 무변경,\n * 이 통합층(board-ui)이 훅만 제공. 트윈은 이 훅의 호스트 구현자일 뿐(트윈 전용 로직 없음).\n *\n * 대칭: operato-twin map-modeller 의 \"미배치 노드 트레이\"(A2)를 board 표현으로 확장하는 일반 메커니즘.\n * place 는 board-edit 'add' op(기존 채널)로 컴포넌트를 추가하고, 컴포넌트 model 의 `tag` 로 라이브 결합\n * (board-provider 가 data(tag:) 구독). 이미 배치됨 = scene 에 같은 tag 컴포넌트 존재.\n */\nexport interface BoardPlacementItem {\n id: string // 곧 컴포넌트 tag (라이브 결합 키)\n label: string\n sub?: string\n}\n\n/** 한 항목의 가능한 표현(N:M) — 능력×목적별 view. key=컴포넌트 타입(placed 판정 키). */\nexport interface BoardPlacementView {\n key: string\n label: string\n sub?: string\n}\n\nexport interface BoardPlacementProvider {\n label?: string // 트레이 제목(기본 \"미배치\")\n /** 이 board 의 배치 후보(전체). 비어있으면 트레이 숨김. boardId 로 컨텍스트 해소. */\n items(boardId: string): BoardPlacementItem[] | Promise<BoardPlacementItem[]>\n /** 항목의 가능한 표현들(능력×목적, N:M). 없으면(미지정) 단일 componentFor 로 동작. */\n viewsFor?(itemId: string): BoardPlacementView[]\n /** 선택한 view(또는 기본)로 추가할 things-scene 컴포넌트 model(반드시 `tag` 포함). null=표현 없음. */\n componentFor(item: BoardPlacementItem, viewKey?: string): any\n}\n\nlet _provider: BoardPlacementProvider | null = null\nexport function registerBoardPlacementProvider(p: BoardPlacementProvider): void {\n _provider = p\n}\nexport function boardPlacementProvider(): BoardPlacementProvider | null {\n return _provider\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class BoardPlacementTray extends LitElement {
3
+ boardId?: string;
4
+ scene: any;
5
+ private items;
6
+ private placedKeys;
7
+ private placedTags;
8
+ private _pos;
9
+ private _collapsed;
10
+ private _drag;
11
+ private _timer?;
12
+ private _dropCount;
13
+ static styles: import("lit").CSSResult;
14
+ connectedCallback(): void;
15
+ disconnectedCallback(): void;
16
+ updated(changed: Map<string, unknown>): void;
17
+ private startDrag;
18
+ private _onDrag;
19
+ private _endDrag;
20
+ private reload;
21
+ private scanPlaced;
22
+ private viewsOf;
23
+ private viewPlaced;
24
+ private get unplaced();
25
+ private place;
26
+ private placeAll;
27
+ render(): import("lit-html").TemplateResult<1>;
28
+ }
@@ -0,0 +1,234 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { LitElement, html, css } from 'lit';
3
+ import { customElement, property, state } from 'lit/decorators.js';
4
+ import { boardPlacementProvider } from './board-placement-palette.js';
5
+ import { dispatchBoardEditOp } from './pages/board-edit-dispatch.js';
6
+ /*
7
+ * board 배치 트레이 = 능력 컴포저 UI — 등록된 BoardPlacementProvider 의 항목을 좌측 트레이로 노출.
8
+ * 항목(엔티티)마다 가능한 **표현들(능력×목적, N:M)** 을 펼쳐 저작이 선택 배치. 1:1 드롭 아님 —
9
+ * 한 엔티티에 여러 표현(모니터/지표/제어/에뮬레이션)을 얹을 수 있다. 표현별 배치 여부 표시(tag::type 스캔).
10
+ * 배치 = board-edit 'add' op(model.tag=엔티티 id) → 라이브 결합(board-provider data(tag:)).
11
+ * board 제품 무변경 — 통합층(board-ui)이 modeller 위에 얹는 일반 오버레이.
12
+ */
13
+ let BoardPlacementTray = class BoardPlacementTray extends LitElement {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.items = [];
17
+ this.placedKeys = new Set(); // `${tag}::${componentType}`
18
+ this.placedTags = new Set(); // tag (fallback 단일 view 판정)
19
+ this._pos = { x: 12, y: 12 };
20
+ this._collapsed = false;
21
+ this._drag = null;
22
+ this._dropCount = 0;
23
+ this._onDrag = (e) => {
24
+ const d = this._drag;
25
+ if (!d)
26
+ return;
27
+ this._pos = { x: Math.max(0, d.ox + (e.clientX - d.sx)), y: Math.max(0, d.oy + (e.clientY - d.sy)) };
28
+ };
29
+ this._endDrag = () => {
30
+ this._drag = null;
31
+ window.removeEventListener('pointermove', this._onDrag);
32
+ window.removeEventListener('pointerup', this._endDrag);
33
+ };
34
+ }
35
+ static { this.styles = css `
36
+ :host { display: contents; }
37
+ .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; }
38
+ .tray.collapsed { overflow: visible; max-height: none; }
39
+ /* 표준 스크롤바(앱 consoleStyles 규약: 얇은 5px·투명 트랙·은은한 thumb). */
40
+ .tray { scrollbar-width: thin; scrollbar-color: rgba(0,0,0,.2) transparent; }
41
+ .tray::-webkit-scrollbar { width: 5px; height: 5px; }
42
+ .tray::-webkit-scrollbar-track { background: transparent; }
43
+ .tray::-webkit-scrollbar-thumb { background: rgba(0,0,0,.2); border-radius: 3px; }
44
+ .tray::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,.38); }
45
+ .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; }
46
+ .ttl b { color: #0b8f9c; font-weight: 700; }
47
+ .ttl .grip::before { content: '⠿'; color: #8a959c; font-size: 12px; margin-right: 5px; }
48
+ .rt { display: inline-flex; align-items: center; gap: 6px; }
49
+ .collapse { border: 0; background: transparent; color: #57676f; cursor: pointer; font-size: 12px; line-height: 1; padding: 2px 3px; }
50
+ .collapse:hover { color: #0b8f9c; }
51
+ .hint { font-size: 10.5px; color: #8a959c; font-family: ui-monospace, Menlo, monospace; margin: 0; }
52
+ .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; }
53
+ .auto:hover { background: #0b8f9c26; }
54
+ .node { border: 1px solid #d5dce0; border-radius: 8px; padding: 6px 8px; display: flex; flex-direction: column; gap: 5px; font-family: ui-monospace, Menlo, monospace; }
55
+ .nhead { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; }
56
+ .nid { font-size: 12px; font-weight: 640; color: #17242c; }
57
+ .nty { font-size: 9.5px; color: #8a959c; }
58
+ .views { display: flex; flex-direction: column; gap: 3px; }
59
+ .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; }
60
+ .view:hover { background: #0b8f9c12; border-color: #0b8f9c; }
61
+ .view.placed { color: #1a8f4c; border-color: #b7e0c5; background: #1a8f4c0e; cursor: default; }
62
+ .purpose { font-size: 9px; color: #8a959c; text-transform: uppercase; letter-spacing: .04em; }
63
+ .done { color: #1a8f4c; font-size: 11.5px; font-family: ui-monospace, Menlo, monospace; }
64
+ @media (prefers-color-scheme: dark) {
65
+ .tray { background: #141b21; border-color: #26323a; scrollbar-color: rgba(255,255,255,.18) transparent; }
66
+ .tray::-webkit-scrollbar-thumb { background: rgba(255,255,255,.18); }
67
+ .tray::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.34); }
68
+ .ttl { color: #8b9aa3; } .ttl b { color: #22c3cf; }
69
+ .node { border-color: #26323a; } .nid { color: #dde7ec; } .nty { color: #8b9aa3; }
70
+ .view { border-color: #234; color: #22c3cf; } .view:hover { background: #22c3cf16; }
71
+ .auto { border-color: #22c3cf88; background: #22c3cf1e; color: #22c3cf; }
72
+ }
73
+ `; }
74
+ connectedCallback() {
75
+ super.connectedCallback();
76
+ this.reload();
77
+ this._timer = setInterval(() => this.scanPlaced(), 2000);
78
+ }
79
+ disconnectedCallback() {
80
+ super.disconnectedCallback();
81
+ clearInterval(this._timer);
82
+ window.removeEventListener('pointermove', this._onDrag);
83
+ window.removeEventListener('pointerup', this._endDrag);
84
+ }
85
+ updated(changed) {
86
+ if (changed.has('boardId'))
87
+ this.reload();
88
+ else if (changed.has('scene'))
89
+ this.scanPlaced();
90
+ }
91
+ /* 헤더 드래그 이동(편집 공간 가림 방지) — 델타 기반. */
92
+ startDrag(e) {
93
+ e.preventDefault();
94
+ this._drag = { sx: e.clientX, sy: e.clientY, ox: this._pos.x, oy: this._pos.y };
95
+ window.addEventListener('pointermove', this._onDrag);
96
+ window.addEventListener('pointerup', this._endDrag);
97
+ }
98
+ async reload() {
99
+ const p = boardPlacementProvider();
100
+ if (!p || !this.boardId) {
101
+ this.items = [];
102
+ return;
103
+ }
104
+ try {
105
+ this.items = await p.items(this.boardId);
106
+ }
107
+ catch {
108
+ this.items = [];
109
+ }
110
+ this.scanPlaced();
111
+ }
112
+ scanPlaced() {
113
+ const map = this.scene?.rootContainer?.refidIndexMap;
114
+ const keys = new Set();
115
+ const tags = new Set();
116
+ map?.forEach?.((comp) => {
117
+ const t = comp?.get?.('tag');
118
+ if (!t)
119
+ return;
120
+ tags.add(String(t));
121
+ /* 표현별 정확 판정 = 배치 시 심은 viewKey(능력:목적:컴포넌트). 없으면(구 컴포넌트) type 로 폴백. */
122
+ const vk = comp?.get?.('viewKey');
123
+ const type = comp?.get?.('type') ?? comp?.model?.type;
124
+ if (vk)
125
+ keys.add(`${t}::${vk}`);
126
+ else if (type)
127
+ keys.add(`${t}::${type}`);
128
+ });
129
+ this.placedKeys = keys;
130
+ this.placedTags = tags;
131
+ this.requestUpdate();
132
+ }
133
+ /* 항목의 표현들(능력×목적). provider 가 viewsFor 지원하면 그 결과(빈 배열=표현 없음)를 그대로,
134
+ 미지원이면 단일 fallback. → 표현 없는 엔티티는 죽은 버튼 없이 자연히 트레이에서 빠짐. */
135
+ viewsOf(item) {
136
+ const p = boardPlacementProvider();
137
+ if (p?.viewsFor)
138
+ return p.viewsFor(item.id);
139
+ return [{ key: '', label: '배치' }];
140
+ }
141
+ viewPlaced(id, key) {
142
+ return key ? this.placedKeys.has(`${id}::${key}`) : this.placedTags.has(id);
143
+ }
144
+ /* 미배치(=아직 안 놓은 표현이 하나라도 있는) 항목. */
145
+ get unplaced() {
146
+ return this.items.filter(i => this.viewsOf(i).some(v => !this.viewPlaced(i.id, v.key)));
147
+ }
148
+ place(item, viewKey) {
149
+ const p = boardPlacementProvider();
150
+ if (!p || !this.scene)
151
+ return;
152
+ const comp = p.componentFor(item, viewKey || undefined);
153
+ if (!comp)
154
+ return;
155
+ const off = (this._dropCount++ % 6) * 22;
156
+ dispatchBoardEditOp(this.scene, { op: 'add', component: { ...comp, left: 80 + off, top: 80 + off } });
157
+ this.scanPlaced();
158
+ }
159
+ /* 모두 자동 배치 — 각 항목의 첫 미배치 표현을 배치(빠른 시작). */
160
+ placeAll() {
161
+ for (const it of this.unplaced) {
162
+ const v = this.viewsOf(it).find(v => !this.viewPlaced(it.id, v.key));
163
+ if (v)
164
+ this.place(it, v.key);
165
+ }
166
+ this.scanPlaced();
167
+ }
168
+ render() {
169
+ const p = boardPlacementProvider();
170
+ if (!p || !this.items.length)
171
+ return html ``;
172
+ const un = this.unplaced;
173
+ const co = this._collapsed;
174
+ return html `<div class="tray ${co ? 'collapsed' : ''}" style="left:${this._pos.x}px;top:${this._pos.y}px">
175
+ <div class="ttl" @pointerdown=${(e) => this.startDrag(e)}>
176
+ <span class="grip">${p.label || '미배치'}</span>
177
+ <span class="rt"><b>${un.length}</b>
178
+ <button class="collapse" @pointerdown=${(e) => e.stopPropagation()} @click=${() => (this._collapsed = !co)} title=${co ? '펼치기' : '접기'}>${co ? '▸' : '▾'}</button>
179
+ </span>
180
+ </div>
181
+ ${co
182
+ ? ''
183
+ : un.length
184
+ ? html `<button class="auto" @click=${() => this.placeAll()} title="각 항목의 첫 표현을 일괄 배치">모두 자동 배치</button>
185
+ <div class="hint">항목의 표현(능력×목적)을 골라 배치. 여러 표현을 얹을 수 있습니다.</div>
186
+ ${un.map(it => html `<div class="node">
187
+ <div class="nhead"><span class="nid">${it.label}</span>${it.sub ? html `<span class="nty">${it.sub}</span>` : ''}</div>
188
+ <div class="views">
189
+ ${this.viewsOf(it).map(v => {
190
+ const placed = this.viewPlaced(it.id, v.key);
191
+ return html `<button class="view ${placed ? 'placed' : ''}" ?disabled=${placed}
192
+ @click=${() => this.place(it, v.key)} title=${placed ? '배치됨' : '이 표현으로 배치'}>
193
+ <span>${placed ? '✓ ' : ''}${v.label}</span>${v.sub ? html `<span class="purpose">${v.sub}</span>` : ''}
194
+ </button>`;
195
+ })}
196
+ </div>
197
+ </div>`)}`
198
+ : html `<div class="done">✓ 모두 배치됨</div>`}
199
+ </div>`;
200
+ }
201
+ };
202
+ __decorate([
203
+ property({ type: String }),
204
+ __metadata("design:type", String)
205
+ ], BoardPlacementTray.prototype, "boardId", void 0);
206
+ __decorate([
207
+ property({ type: Object }),
208
+ __metadata("design:type", Object)
209
+ ], BoardPlacementTray.prototype, "scene", void 0);
210
+ __decorate([
211
+ state(),
212
+ __metadata("design:type", Array)
213
+ ], BoardPlacementTray.prototype, "items", void 0);
214
+ __decorate([
215
+ state(),
216
+ __metadata("design:type", Set)
217
+ ], BoardPlacementTray.prototype, "placedKeys", void 0);
218
+ __decorate([
219
+ state(),
220
+ __metadata("design:type", Set)
221
+ ], BoardPlacementTray.prototype, "placedTags", void 0);
222
+ __decorate([
223
+ state(),
224
+ __metadata("design:type", Object)
225
+ ], BoardPlacementTray.prototype, "_pos", void 0);
226
+ __decorate([
227
+ state(),
228
+ __metadata("design:type", Object)
229
+ ], BoardPlacementTray.prototype, "_collapsed", void 0);
230
+ BoardPlacementTray = __decorate([
231
+ customElement('board-placement-tray')
232
+ ], BoardPlacementTray);
233
+ export { BoardPlacementTray };
234
+ //# sourceMappingURL=board-placement-tray.js.map
@@ -0,0 +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 { 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 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 .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 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 /* 표현별 정확 판정 = 배치 시 심은 viewKey(능력:목적:컴포넌트). 없으면(구 컴포넌트) type 로 폴백. */\n const vk = comp?.get?.('viewKey')\n const type = comp?.get?.('type') ?? comp?.model?.type\n if (vk) keys.add(`${t}::${vk}`)\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 render() {\n const p = boardPlacementProvider()\n if (!p || !this.items.length) return html``\n const un = this.unplaced\n const co = this._collapsed\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`<button class=\"auto\" @click=${() => this.placeAll()} title=\"각 항목의 첫 표현을 일괄 배치\">모두 자동 배치</button>\n <div class=\"hint\">항목의 표현(능력×목적)을 골라 배치. 여러 표현을 얹을 수 있습니다.</div>\n ${un.map(it => html`<div class=\"node\">\n <div class=\"nhead\"><span class=\"nid\">${it.label}</span>${it.sub ? html`<span class=\"nty\">${it.sub}</span>` : ''}</div>\n <div class=\"views\">\n ${this.viewsOf(it).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 </div>`)}`\n : html`<div class=\"done\">✓ 모두 배치됨</div>`}\n </div>`\n }\n}\n"]}
@@ -8,7 +8,7 @@ export declare function cloneBoard({ id, name, description, targetSubdomain, tar
8
8
  targetSubdomain: string;
9
9
  targetGroupId: string;
10
10
  }): Promise<any>;
11
- export declare function releaseBoard(board: any): Promise<any>;
11
+ export declare function releaseBoard(board: any, comment?: any): Promise<any>;
12
12
  export declare function revertBoardVersion(id: any, version: any): Promise<any>;
13
13
  export declare function updateBoard(board: any): Promise<any>;
14
14
  export declare function fetchBoardsUpdatedSince(since: Date): Promise<any>;
@@ -122,12 +122,12 @@ export async function cloneBoard({ id, name, description, targetSubdomain, targe
122
122
  });
123
123
  return response.data;
124
124
  }
125
- export async function releaseBoard(board) {
125
+ export async function releaseBoard(board, comment) {
126
126
  const { id } = board;
127
127
  const response = await client.mutate({
128
128
  mutation: gql `
129
- mutation ReleaseBoard($id: String!) {
130
- releaseBoard(id: $id) {
129
+ mutation ReleaseBoard($id: String!, $comment: String) {
130
+ releaseBoard(id: $id, comment: $comment) {
131
131
  id
132
132
  name
133
133
  description
@@ -145,7 +145,8 @@ export async function releaseBoard(board) {
145
145
  }
146
146
  `,
147
147
  variables: {
148
- id
148
+ id,
149
+ comment: comment ?? null
149
150
  }
150
151
  });
151
152
  return response.data;
@@ -1 +1 @@
1
- {"version":3,"file":"board.js","sourceRoot":"","sources":["../../client/graphql/board.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\n\nimport { client } from '@operato/graphql'\nimport { gqlBuilder } from '@things-factory/utils'\n\nexport async function fetchBoardList(listParam = {}) {\n const response = await client.query({\n query: gql`\n {\n boards(${gqlBuilder.buildArgs(listParam)}) {\n items {\n id\n name\n description\n thumbnail\n thumbnailUpdatedAt\n state\n version\n sortOrder\n createdAt\n updatedAt\n }\n total\n }\n }\n `\n })\n\n return response.data\n}\n\nexport async function fetchBoard(id) {\n const response = await client.query({\n query: gql`\n query FetchBoardById($id: String!) {\n board(id: $id) {\n id\n name\n description\n group {\n id\n name\n }\n thumbnail\n thumbnailUpdatedAt\n model\n createdAt\n creator {\n id\n name\n }\n state\n version\n updatedAt\n updater {\n id\n name\n }\n }\n }\n `,\n variables: { id }\n })\n\n return response.data\n}\n\nexport async function createBoard(board) {\n /*\n input NewBoard {\n name : String!\n description : String\n model : String!\n type : String!\n groupId : String!\n }\n */\n\n board.model = JSON.stringify(board.model)\n\n const response = await client.mutate({\n mutation: gql`\n mutation CreateBoard($board: NewBoard!) {\n createBoard(board: $board) {\n id\n name\n description\n type\n model\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n board\n }\n })\n\n return response.data\n}\n\nexport async function cloneBoard({\n id,\n name,\n description,\n targetSubdomain,\n targetGroupId\n}: {\n id: string\n name: string\n description: string\n targetSubdomain: string\n targetGroupId: string\n}) {\n const response = await client.mutate({\n mutation: gql`\n mutation CloneBoard($id: String!, $patch: BoardPatch!, $targetSubdomain: String!, $targetGroupId: String) {\n cloneBoard(id: $id, patch: $patch, targetSubdomain: $targetSubdomain, targetGroupId: $targetGroupId) {\n id\n name\n description\n type\n model\n group {\n id\n name\n }\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n id,\n patch: { name, description },\n targetSubdomain,\n targetGroupId\n }\n })\n\n return response.data\n}\n\nexport async function releaseBoard(board) {\n const { id } = board\n\n const response = await client.mutate({\n mutation: gql`\n mutation ReleaseBoard($id: String!) {\n releaseBoard(id: $id) {\n id\n name\n description\n type\n model\n group {\n id\n name\n }\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n id\n }\n })\n\n return response.data\n}\n\nexport async function revertBoardVersion(id, version) {\n const response = await client.mutate({\n mutation: gql`\n mutation RevertBoardVersion($id: String!, $version: Float!) {\n revertBoardVersion(id: $id, version: $version) {\n name\n }\n }\n `,\n variables: {\n id,\n version\n }\n })\n\n return response.data.revertBoardVersion\n}\n\nexport async function updateBoard(board) {\n /*\n input BoardPatch {\n name : String\n description : String\n model : String\n groupId : String\n type : String\n }\n */\n let { id, name, description, model, groupId, type } = board\n model = JSON.stringify(model)\n\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateBoard($id: String!, $patch: BoardPatch!) {\n updateBoard(id: $id, patch: $patch) {\n id\n name\n description\n model\n group {\n id\n name\n }\n type\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n id,\n patch: { name, description, model, groupId, type }\n }\n })\n\n return response.data\n}\n\nexport async function fetchBoardsUpdatedSince(since: Date) {\n const response = await client.query({\n query: gql`\n query BoardsUpdatedSince($since: DateTimeISO!) {\n boardsUpdatedSince(since: $since) {\n id\n name\n description\n thumbnail\n thumbnailUpdatedAt\n state\n version\n type\n sortOrder\n group {\n id\n }\n createdAt\n updatedAt\n deletedAt\n }\n }\n `,\n variables: { since: since.toISOString() },\n fetchPolicy: 'network-only'\n })\n\n return response.data.boardsUpdatedSince\n}\n\nexport async function deleteBoard(id) {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteBoard(id: $id)\n }\n `,\n variables: {\n id\n }\n })\n\n return response.data\n}\n\nexport async function importBoards({\n groupId,\n files,\n overwrite\n}: {\n groupId: string\n files: File[]\n overwrite: boolean\n}) {\n const response = await client.mutate({\n mutation: gql`\n mutation importBoards($groupId: String!, $files: [Upload!]!, $overwrite: Boolean!) {\n importBoards(groupId: $groupId, files: $files, overwrite: $overwrite) {\n id\n name\n description\n }\n }\n `,\n variables: {\n groupId,\n files,\n overwrite\n },\n context: { hasUpload: true }\n })\n\n return response.data\n}\n"]}
1
+ {"version":3,"file":"board.js","sourceRoot":"","sources":["../../client/graphql/board.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\n\nimport { client } from '@operato/graphql'\nimport { gqlBuilder } from '@things-factory/utils'\n\nexport async function fetchBoardList(listParam = {}) {\n const response = await client.query({\n query: gql`\n {\n boards(${gqlBuilder.buildArgs(listParam)}) {\n items {\n id\n name\n description\n thumbnail\n thumbnailUpdatedAt\n state\n version\n sortOrder\n createdAt\n updatedAt\n }\n total\n }\n }\n `\n })\n\n return response.data\n}\n\nexport async function fetchBoard(id) {\n const response = await client.query({\n query: gql`\n query FetchBoardById($id: String!) {\n board(id: $id) {\n id\n name\n description\n group {\n id\n name\n }\n thumbnail\n thumbnailUpdatedAt\n model\n createdAt\n creator {\n id\n name\n }\n state\n version\n updatedAt\n updater {\n id\n name\n }\n }\n }\n `,\n variables: { id }\n })\n\n return response.data\n}\n\nexport async function createBoard(board) {\n /*\n input NewBoard {\n name : String!\n description : String\n model : String!\n type : String!\n groupId : String!\n }\n */\n\n board.model = JSON.stringify(board.model)\n\n const response = await client.mutate({\n mutation: gql`\n mutation CreateBoard($board: NewBoard!) {\n createBoard(board: $board) {\n id\n name\n description\n type\n model\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n board\n }\n })\n\n return response.data\n}\n\nexport async function cloneBoard({\n id,\n name,\n description,\n targetSubdomain,\n targetGroupId\n}: {\n id: string\n name: string\n description: string\n targetSubdomain: string\n targetGroupId: string\n}) {\n const response = await client.mutate({\n mutation: gql`\n mutation CloneBoard($id: String!, $patch: BoardPatch!, $targetSubdomain: String!, $targetGroupId: String) {\n cloneBoard(id: $id, patch: $patch, targetSubdomain: $targetSubdomain, targetGroupId: $targetGroupId) {\n id\n name\n description\n type\n model\n group {\n id\n name\n }\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n id,\n patch: { name, description },\n targetSubdomain,\n targetGroupId\n }\n })\n\n return response.data\n}\n\nexport async function releaseBoard(board, comment?) {\n const { id } = board\n\n const response = await client.mutate({\n mutation: gql`\n mutation ReleaseBoard($id: String!, $comment: String) {\n releaseBoard(id: $id, comment: $comment) {\n id\n name\n description\n type\n model\n group {\n id\n name\n }\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n id,\n comment: comment ?? null\n }\n })\n\n return response.data\n}\n\nexport async function revertBoardVersion(id, version) {\n const response = await client.mutate({\n mutation: gql`\n mutation RevertBoardVersion($id: String!, $version: Float!) {\n revertBoardVersion(id: $id, version: $version) {\n name\n }\n }\n `,\n variables: {\n id,\n version\n }\n })\n\n return response.data.revertBoardVersion\n}\n\nexport async function updateBoard(board) {\n /*\n input BoardPatch {\n name : String\n description : String\n model : String\n groupId : String\n type : String\n }\n */\n let { id, name, description, model, groupId, type } = board\n model = JSON.stringify(model)\n\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateBoard($id: String!, $patch: BoardPatch!) {\n updateBoard(id: $id, patch: $patch) {\n id\n name\n description\n model\n group {\n id\n name\n }\n type\n state\n version\n createdAt\n updatedAt\n }\n }\n `,\n variables: {\n id,\n patch: { name, description, model, groupId, type }\n }\n })\n\n return response.data\n}\n\nexport async function fetchBoardsUpdatedSince(since: Date) {\n const response = await client.query({\n query: gql`\n query BoardsUpdatedSince($since: DateTimeISO!) {\n boardsUpdatedSince(since: $since) {\n id\n name\n description\n thumbnail\n thumbnailUpdatedAt\n state\n version\n type\n sortOrder\n group {\n id\n }\n createdAt\n updatedAt\n deletedAt\n }\n }\n `,\n variables: { since: since.toISOString() },\n fetchPolicy: 'network-only'\n })\n\n return response.data.boardsUpdatedSince\n}\n\nexport async function deleteBoard(id) {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!) {\n deleteBoard(id: $id)\n }\n `,\n variables: {\n id\n }\n })\n\n return response.data\n}\n\nexport async function importBoards({\n groupId,\n files,\n overwrite\n}: {\n groupId: string\n files: File[]\n overwrite: boolean\n}) {\n const response = await client.mutate({\n mutation: gql`\n mutation importBoards($groupId: String!, $files: [Upload!]!, $overwrite: Boolean!) {\n importBoards(groupId: $groupId, files: $files, overwrite: $overwrite) {\n id\n name\n description\n }\n }\n `,\n variables: {\n groupId,\n files,\n overwrite\n },\n context: { hasUpload: true }\n })\n\n return response.data\n}\n"]}
@@ -2,6 +2,8 @@ export * from './board-provider.js';
2
2
  export * from './pages/board-viewer-page.js';
3
3
  export * from './pages/board-player-page.js';
4
4
  export * from './pages/board-modeller-page.js';
5
+ export * from './pages/board-edit-dispatch.js';
6
+ export * from './pages/board-action-dispatch.js';
5
7
  export * from './setting-let/board-view-setting-let.js';
6
8
  export * from './apptools/favorite-tool.js';
7
9
  export * from './graphql/index.js';
@@ -2,6 +2,10 @@ export * from './board-provider.js';
2
2
  export * from './pages/board-viewer-page.js';
3
3
  export * from './pages/board-player-page.js';
4
4
  export * from './pages/board-modeller-page.js';
5
+ /* AI 패치/액션의 things-scene in-place 적용기 — 순수 함수. 자체 모델러 셸을 가진 호스트
6
+ * (예: operato-twin 의 트윈 모델러)가 같은 적용 규칙을 쓰도록 공개한다(중복 구현 금지). */
7
+ export * from './pages/board-edit-dispatch.js';
8
+ export * from './pages/board-action-dispatch.js';
5
9
  export * from './setting-let/board-view-setting-let.js';
6
10
  export * from './apptools/favorite-tool.js';
7
11
  export * from './graphql/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AAEnC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAE9C,cAAc,yCAAyC,CAAA;AACvD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA","sourcesContent":["export * from './board-provider.js'\n\nexport * from './pages/board-viewer-page.js'\nexport * from './pages/board-player-page.js'\nexport * from './pages/board-modeller-page.js'\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"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AAEnC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C;+DAC+D;AAC/D,cAAc,gCAAgC,CAAA;AAC9C,cAAc,kCAAkC,CAAA;AAEhD,cAAc,yCAAyC,CAAA;AACvD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,oBAAoB,CAAA;AAClC,cAAc,sBAAsB,CAAA","sourcesContent":["export * from './board-provider.js'\n\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 의 트윈 모델러)가 같은 적용 규칙을 쓰도록 공개한다(중복 구현 금지). */\nexport * from './pages/board-edit-dispatch.js'\nexport * from './pages/board-action-dispatch.js'\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"]}
@@ -513,7 +513,7 @@ let BoardListPage = class BoardListPage extends BoardListPageBase {
513
513
  }
514
514
  async onReleaseBoard(board) {
515
515
  try {
516
- const data = await releaseBoard(board);
516
+ const data = await releaseBoard(board, board.comment);
517
517
  data && notify('info', i18next.t('text.board released', { board: board.name }));
518
518
  }
519
519
  catch (ex) {