@nine-lab/nine-mu 0.1.150 → 0.1.151
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/nine-mu.js +403 -6
- package/dist/nine-mu.js.map +1 -1
- package/dist/nine-mu.umd.js +3 -3
- package/dist/nine-mu.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ChatMessage.js +503 -0
- package/src/components/NineChat.js +1 -1
- package/src/components/ai/aiContainer.js +438 -0
- package/src/components/ai/aiMessage.js +505 -0
- package/src/components/ai/aiSettings.js +149 -0
- package/src/index.js +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
class aiMessage extends HTMLElement
|
|
2
|
+
{
|
|
3
|
+
#message;
|
|
4
|
+
#data;
|
|
5
|
+
#unique;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
this.attachShadow({ mode: 'open' });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
connectedCallback() {
|
|
13
|
+
this.#message = this.getAttribute("message");
|
|
14
|
+
|
|
15
|
+
this.shadowRoot.innerHTML = `
|
|
16
|
+
<style>
|
|
17
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid2@${ninegrid.version}/dist/css/ai.css";
|
|
18
|
+
${ninegrid.getCustomPath(this,"ai.css")}
|
|
19
|
+
</style>
|
|
20
|
+
|
|
21
|
+
<div class="chat-message">
|
|
22
|
+
<div class="message"></div>
|
|
23
|
+
<span class="more"><a href="#">more</a></span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="grid"></div>
|
|
27
|
+
|
|
28
|
+
<div class="chat-menu">
|
|
29
|
+
<div class="left-menu">
|
|
30
|
+
<div class="filter"></div>
|
|
31
|
+
<div class="database"></div>
|
|
32
|
+
<div class="excel"></div>
|
|
33
|
+
<div class="maximize"></div>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
`;
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
<svg class="filter" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
40
|
+
<path d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5zm1 .5v1.308l4.372 4.858A.5.5 0 0 1 7 8.5v5.306l2-.666V8.5a.5.5 0 0 1 .128-.334L13.5 3.308V2z"/>
|
|
41
|
+
</svg>
|
|
42
|
+
<svg class="database" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
43
|
+
<path d="M4.318 2.687C5.234 2.271 6.536 2 8 2s2.766.27 3.682.687C12.644 3.125 13 3.627 13 4c0 .374-.356.875-1.318 1.313C10.766 5.729 9.464 6 8 6s-2.766-.27-3.682-.687C3.356 4.875 3 4.373 3 4c0-.374.356-.875 1.318-1.313M13 5.698V7c0 .374-.356.875-1.318 1.313C10.766 8.729 9.464 9 8 9s-2.766-.27-3.682-.687C3.356 7.875 3 7.373 3 7V5.698c.271.202.58.378.904.525C4.978 6.711 6.427 7 8 7s3.022-.289 4.096-.777A5 5 0 0 0 13 5.698M14 4c0-1.007-.875-1.755-1.904-2.223C11.022 1.289 9.573 1 8 1s-3.022.289-4.096.777C2.875 2.245 2 2.993 2 4v9c0 1.007.875 1.755 1.904 2.223C4.978 15.71 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13zm-1 4.698V10c0 .374-.356.875-1.318 1.313C10.766 11.729 9.464 12 8 12s-2.766-.27-3.682-.687C3.356 10.875 3 10.373 3 10V8.698c.271.202.58.378.904.525C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777A5 5 0 0 0 13 8.698m0 3V13c0 .374-.356.875-1.318 1.313C10.766 14.729 9.464 15 8 15s-2.766-.27-3.682-.687C3.356 13.875 3 13.373 3 13v-1.302c.271.202.58.378.904.525C4.978 12.71 6.427 13 8 13s3.022-.289 4.096-.777c.324-.147.633-.323.904-.525"/>
|
|
44
|
+
</svg>
|
|
45
|
+
<svg class="excel" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
46
|
+
<path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5zM6.472 15.29a1.2 1.2 0 0 1-.111-.449h.765a.58.58 0 0 0 .254.384q.106.073.25.114.143.041.319.041.246 0 .413-.07a.56.56 0 0 0 .255-.193.5.5 0 0 0 .085-.29.39.39 0 0 0-.153-.326q-.152-.12-.462-.193l-.619-.143a1.7 1.7 0 0 1-.539-.214 1 1 0 0 1-.351-.367 1.1 1.1 0 0 1-.123-.524q0-.366.19-.639.19-.272.527-.422.338-.15.777-.149.457 0 .78.152.324.153.5.41.18.255.2.566h-.75a.56.56 0 0 0-.12-.258.6.6 0 0 0-.247-.181.9.9 0 0 0-.369-.068q-.325 0-.513.152a.47.47 0 0 0-.184.384q0 .18.143.3a1 1 0 0 0 .405.175l.62.143q.326.075.566.211a1 1 0 0 1 .375.358q.135.222.135.56 0 .37-.188.656a1.2 1.2 0 0 1-.539.439q-.351.158-.858.158-.381 0-.665-.09a1.4 1.4 0 0 1-.478-.252 1.1 1.1 0 0 1-.29-.375m-2.945-3.358h-.893L1.81 13.37h-.036l-.832-1.438h-.93l1.227 1.983L0 15.931h.861l.853-1.415h.035l.85 1.415h.908L2.253 13.94zm2.727 3.325H4.557v-3.325h-.79v4h2.487z"/>
|
|
47
|
+
</svg>
|
|
48
|
+
<svg class="maximize" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
49
|
+
<path fill-rule="evenodd" d="M5.828 10.172a.5.5 0 0 0-.707 0l-4.096 4.096V11.5a.5.5 0 0 0-1 0v3.975a.5.5 0 0 0 .5.5H4.5a.5.5 0 0 0 0-1H1.732l4.096-4.096a.5.5 0 0 0 0-.707m4.344 0a.5.5 0 0 1 .707 0l4.096 4.096V11.5a.5.5 0 1 1 1 0v3.975a.5.5 0 0 1-.5.5H11.5a.5.5 0 0 1 0-1h2.768l-4.096-4.096a.5.5 0 0 1 0-.707m0-4.344a.5.5 0 0 0 .707 0l4.096-4.096V4.5a.5.5 0 1 0 1 0V.525a.5.5 0 0 0-.5-.5H11.5a.5.5 0 0 0 0 1h2.768l-4.096 4.096a.5.5 0 0 0 0 .707m-4.344 0a.5.5 0 0 1-.707 0L1.025 1.732V4.5a.5.5 0 0 1-1 0V.525a.5.5 0 0 1 .5-.5H4.5a.5.5 0 0 1 0 1H1.732l4.096 4.096a.5.5 0 0 1 0 .707"/>
|
|
50
|
+
</svg>
|
|
51
|
+
*/
|
|
52
|
+
requestAnimationFrame(() => {
|
|
53
|
+
const elMessage = this.shadowRoot.querySelector(".message");
|
|
54
|
+
elMessage.setAttribute("title", this.#message);
|
|
55
|
+
elMessage.innerHTML = this.#message.replaceAll("\n\n", "<br/>").replaceAll("\n", "<br/>");
|
|
56
|
+
|
|
57
|
+
this.shadowRoot.querySelector(".filter").addEventListener("click", e => {
|
|
58
|
+
|
|
59
|
+
//if (e.target.classList.contains("disabled")) return;
|
|
60
|
+
if (this.#unique) {
|
|
61
|
+
const grd = document.querySelector("nine-grid");
|
|
62
|
+
|
|
63
|
+
grd.filtering.set(this.#data.map(item =>
|
|
64
|
+
Object.fromEntries(this.#unique.map(key => [key, item[key]]))
|
|
65
|
+
));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
this.shadowRoot.querySelector(".database").addEventListener("click", e => {
|
|
70
|
+
//this.shadowRoot.querySelector("nine-grid").maximize();
|
|
71
|
+
const keys = this.#data.map(item =>
|
|
72
|
+
Object.fromEntries(this.#unique.map(key => [key, item[key]]))
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
console.log(keys);
|
|
76
|
+
|
|
77
|
+
this.dispatchEvent(new CustomEvent("database", { bubbles: true, detail: { key : keys } }));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
this.shadowRoot.querySelector(".maximize").addEventListener("click", e => {
|
|
81
|
+
this.shadowRoot.querySelector("nine-grid").maximize();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
this.shadowRoot.querySelector(".excel").addEventListener("click", e => {
|
|
85
|
+
this.shadowRoot.querySelector("nine-grid").export();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
this.shadowRoot.querySelector(".more a").addEventListener("click", () => {
|
|
89
|
+
alert(this.#message);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
const elMore = this.shadowRoot.querySelector(".more");
|
|
94
|
+
|
|
95
|
+
//if (ninegrid.isEllipsis(this.shadowRoot.querySelector(".message"))) {
|
|
96
|
+
if (this.#message.length > 50) {
|
|
97
|
+
elMore.style.display = "flex";
|
|
98
|
+
elMore.style.position = "absolute";
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
elMore.style.display = "none";
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
initialize = (info, data, unique) => {
|
|
108
|
+
this.#data = data;
|
|
109
|
+
this.#unique = unique;
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
if (!unique) {
|
|
114
|
+
this.shadowRoot.querySelector(".filter").classList.add("disabled");
|
|
115
|
+
this.shadowRoot.querySelector(".database").classList.add("disabled");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.#init(info);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#init = (info) => {
|
|
123
|
+
|
|
124
|
+
const rowCount = Number(this.getAttribute("row-count"));
|
|
125
|
+
if (rowCount < 1) {
|
|
126
|
+
this.shadowRoot.querySelector(".grid").style.display = "none";
|
|
127
|
+
this.shadowRoot.querySelector(".chat-menu").style.display = "none";
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let colgroup = info.map(() => `<col background-color="#888"/>`).join("");
|
|
132
|
+
let thead = info.map(item => `<th>${item.desc}</th>`).join("");
|
|
133
|
+
let tbody = info.map(item => `<td data-bind="${item.name}" text-align="${item.type == 'number' ? 'right' : 'left'}"></td>`).join("");
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
const html = `
|
|
137
|
+
<nine-grid class="ai" display-row-count="${rowCount > 5 ? 5 : rowCount}" show-title-bar="false" show-menu-icon="false" show-status-bar="false" select-type="row" auto-fit-col="true" show-title-bar="true" show-menu-icon="true" show-status-bar="true" enable-fixed-col="true" row-resizable="false" col-movable="true">
|
|
138
|
+
<table style="display:none;">
|
|
139
|
+
<colgroup>
|
|
140
|
+
<col background-color="#888" width="30" fixed="left" />
|
|
141
|
+
${colgroup}
|
|
142
|
+
</colgroup>
|
|
143
|
+
<thead>
|
|
144
|
+
<tr>
|
|
145
|
+
<th>No.</th>
|
|
146
|
+
${thead}
|
|
147
|
+
</tr>
|
|
148
|
+
</thead>
|
|
149
|
+
<tbody>
|
|
150
|
+
<tr>
|
|
151
|
+
<th><ng-row-indicator/></th>
|
|
152
|
+
${tbody}
|
|
153
|
+
</tr>
|
|
154
|
+
</tbody>
|
|
155
|
+
</table>
|
|
156
|
+
</nine-grid>
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
this.shadowRoot.querySelector(".grid").innerHTML = html;
|
|
160
|
+
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
const grd = this.shadowRoot.querySelector("nine-grid");
|
|
163
|
+
grd.data.source = this.#data;
|
|
164
|
+
|
|
165
|
+
//this.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
class aiIngMessage extends HTMLElement
|
|
171
|
+
{
|
|
172
|
+
#message;
|
|
173
|
+
|
|
174
|
+
constructor() {
|
|
175
|
+
super();
|
|
176
|
+
this.attachShadow({ mode: 'open' });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
connectedCallback() {
|
|
180
|
+
//const message = this.getAttribute("message");
|
|
181
|
+
|
|
182
|
+
this.#message = ".";
|
|
183
|
+
|
|
184
|
+
this.shadowRoot.innerHTML = `
|
|
185
|
+
<style>
|
|
186
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid2@${ninegrid.version}/dist/css/ai.css";
|
|
187
|
+
${ninegrid.getCustomPath(this,"ai.css")}
|
|
188
|
+
</style>
|
|
189
|
+
|
|
190
|
+
<div class="chat-message">
|
|
191
|
+
.
|
|
192
|
+
</div>
|
|
193
|
+
`;
|
|
194
|
+
|
|
195
|
+
this.#init();
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
#init = () => {
|
|
199
|
+
const interval = setInterval(() => {
|
|
200
|
+
this.#message = this.#message.length < 3 ? this.#message + "." : "";
|
|
201
|
+
this.shadowRoot.querySelector(".chat-message").innerHTML = this.#message || " ";
|
|
202
|
+
}, 500);
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class aiMyMessage extends HTMLElement
|
|
207
|
+
{
|
|
208
|
+
constructor() {
|
|
209
|
+
super();
|
|
210
|
+
this.attachShadow({ mode: 'open' });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
connectedCallback() {
|
|
214
|
+
const message = this.getAttribute("message");
|
|
215
|
+
|
|
216
|
+
this.shadowRoot.innerHTML = `
|
|
217
|
+
<style>
|
|
218
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid2@${ninegrid.version}/dist/css/ai.css";
|
|
219
|
+
${ninegrid.getCustomPath(this,"ai.css")}
|
|
220
|
+
</style>
|
|
221
|
+
|
|
222
|
+
<div class="chat-message">
|
|
223
|
+
${message}
|
|
224
|
+
</div>
|
|
225
|
+
`;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// aiProgressMessage 클래스 정의
|
|
230
|
+
// aiProgressMessage 클래스 정의 수정
|
|
231
|
+
class aiProgressMessage extends HTMLElement {
|
|
232
|
+
#progressData = [];
|
|
233
|
+
#progressElements = new Map();
|
|
234
|
+
#animationIntervals = new Map();
|
|
235
|
+
|
|
236
|
+
constructor() {
|
|
237
|
+
super();
|
|
238
|
+
this.attachShadow({ mode: 'open' });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
connectedCallback() {
|
|
242
|
+
this.shadowRoot.innerHTML = `
|
|
243
|
+
<style>
|
|
244
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid2@${ninegrid.version}/dist/css/ai.css";
|
|
245
|
+
${ninegrid.getCustomPath(this, "ai.css")}
|
|
246
|
+
</style>
|
|
247
|
+
<div class="chat-message progress-message">
|
|
248
|
+
<ul class="progress-list"></ul>
|
|
249
|
+
</div>
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 진행 상태 데이터를 초기화하고 화면에 표시합니다.
|
|
255
|
+
* @param {Array<Object>} data - [{id: 'step1', message: '분석중입니다.', completedMessage: '분석이 완료되었습니다.'}, ...]
|
|
256
|
+
*/
|
|
257
|
+
initialize(data) {
|
|
258
|
+
this.#progressData = data.map(item => ({
|
|
259
|
+
...item,
|
|
260
|
+
status: 'pending',
|
|
261
|
+
originalMessage: item.message
|
|
262
|
+
}));
|
|
263
|
+
setTimeout(() => {
|
|
264
|
+
this.#renderProgress(); // 초기 렌더링 시 현재 진행 단계를 활성화
|
|
265
|
+
this.#updateCurrentActiveProgress(); // 초기 활성 단계 설정
|
|
266
|
+
}, 300);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 특정 단계의 진행 상태를 업데이트합니다.
|
|
271
|
+
* @param {string} id - 업데이트할 단계의 고유 ID
|
|
272
|
+
* @param {string} status - 'completed' 또는 'pending'
|
|
273
|
+
*/
|
|
274
|
+
updateProgress(id, status) {
|
|
275
|
+
const itemIndex = this.#progressData.findIndex(item => item.id === id);
|
|
276
|
+
if (itemIndex > -1) {
|
|
277
|
+
if (this.#progressData[itemIndex].status !== status) {
|
|
278
|
+
this.#progressData[itemIndex].status = status;
|
|
279
|
+
this.#updateProgressItem(this.#progressData[itemIndex]); // 개별 항목 업데이트
|
|
280
|
+
this.#updateCurrentActiveProgress(); // 활성 진행 단계 재설정
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 현재 진행 중인 첫 번째 pending 항목을 찾아 'active' 상태로 만들고 애니메이션 시작
|
|
286
|
+
// 이전에 active였던 항목의 애니메이션은 중지
|
|
287
|
+
#updateCurrentActiveProgress() {
|
|
288
|
+
let foundActive = false;
|
|
289
|
+
this.#progressData.forEach(item => {
|
|
290
|
+
const li = this.#progressElements.get(item.id);
|
|
291
|
+
if (!li) return; // 요소가 아직 렌더링되지 않았으면 스킵
|
|
292
|
+
|
|
293
|
+
// 모든 항목에서 'active' 클래스 제거 및 애니메이션 중지
|
|
294
|
+
li.classList.remove('active');
|
|
295
|
+
if (this.#animationIntervals.has(item.id)) {
|
|
296
|
+
clearInterval(this.#animationIntervals.get(item.id));
|
|
297
|
+
this.#animationIntervals.delete(item.id);
|
|
298
|
+
li.querySelector(".animated-dots").textContent = ''; // 점 제거
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 첫 번째 pending 항목을 찾으면 'active'로 설정하고 애니메이션 시작
|
|
302
|
+
if (item.status === 'pending' && !foundActive) {
|
|
303
|
+
li.classList.add('active');
|
|
304
|
+
this.#startAnimation(item);
|
|
305
|
+
foundActive = true;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#renderProgress() {
|
|
311
|
+
const progressList = this.shadowRoot.querySelector(".progress-list");
|
|
312
|
+
progressList.innerHTML = '';
|
|
313
|
+
this.#progressElements.clear();
|
|
314
|
+
this.#animationIntervals.forEach(intervalId => clearInterval(intervalId));
|
|
315
|
+
this.#animationIntervals.clear();
|
|
316
|
+
|
|
317
|
+
this.#progressData.forEach((item, index) => {
|
|
318
|
+
const li = document.createElement("li");
|
|
319
|
+
li.setAttribute("data-id", item.id);
|
|
320
|
+
li.classList.add("progress-item");
|
|
321
|
+
li.innerHTML = `
|
|
322
|
+
<span class="step-number">${index + 1}.</span>
|
|
323
|
+
<span class="text">${item.originalMessage}</span>
|
|
324
|
+
<span class="animated-dots"></span>
|
|
325
|
+
`;
|
|
326
|
+
progressList.appendChild(li);
|
|
327
|
+
this.#progressElements.set(item.id, li);
|
|
328
|
+
// 초기 렌더링 시에는 'active' 상태는 `#updateCurrentActiveProgress`에서 처리
|
|
329
|
+
this.#updateProgressItemVisuals(item); // 텍스트만 업데이트
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 항목의 시각적 상태(텍스트, 완료 체크)만 업데이트
|
|
334
|
+
#updateProgressItemVisuals(item) {
|
|
335
|
+
const li = this.#progressElements.get(item.id);
|
|
336
|
+
if (li) {
|
|
337
|
+
li.classList.remove('pending', 'completed');
|
|
338
|
+
li.classList.add(item.status);
|
|
339
|
+
|
|
340
|
+
const textSpan = li.querySelector(".text");
|
|
341
|
+
const dotsSpan = li.querySelector(".animated-dots");
|
|
342
|
+
|
|
343
|
+
if (item.status === 'completed') {
|
|
344
|
+
textSpan.textContent = item.completedMessage;
|
|
345
|
+
dotsSpan.textContent = ''; // 완료 시 점 제거
|
|
346
|
+
} else { // pending 상태
|
|
347
|
+
textSpan.textContent = item.originalMessage;
|
|
348
|
+
// pending이지만 active가 아닌 경우 점 없음 (CSS의 role)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 점 애니메이션 시작 함수
|
|
354
|
+
#startAnimation(item) {
|
|
355
|
+
const li = this.#progressElements.get(item.id);
|
|
356
|
+
if (li) {
|
|
357
|
+
const dotsSpan = li.querySelector(".animated-dots");
|
|
358
|
+
let dotsCount = 0;
|
|
359
|
+
const intervalId = setInterval(() => {
|
|
360
|
+
dotsCount = (dotsCount + 1) % 4; // 0, 1, 2, 3 반복
|
|
361
|
+
dotsSpan.textContent = '.'.repeat(dotsCount);
|
|
362
|
+
}, 300);
|
|
363
|
+
this.#animationIntervals.set(item.id, intervalId);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 개별 항목의 상태 업데이트 (텍스트 변경, 클래스 추가/제거)
|
|
368
|
+
// 애니메이션 시작/중지는 `#updateCurrentActiveProgress`에서 담당
|
|
369
|
+
#updateProgressItem(item) {
|
|
370
|
+
this.#updateProgressItemVisuals(item); // 텍스트와 클래스 업데이트
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class ChatMessage extends HTMLElement
|
|
377
|
+
{
|
|
378
|
+
constructor() {
|
|
379
|
+
super();
|
|
380
|
+
this.attachShadow({ mode: 'open' });
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
connectedCallback() {
|
|
384
|
+
const message = this.getAttribute("message");
|
|
385
|
+
|
|
386
|
+
this.shadowRoot.innerHTML = `
|
|
387
|
+
<style>
|
|
388
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid2@${ninegrid.version}/dist/css/ai.css";
|
|
389
|
+
${ninegrid.getCustomPath(this,"ai.css")}
|
|
390
|
+
</style>
|
|
391
|
+
|
|
392
|
+
<div class="chat-body">
|
|
393
|
+
</div>
|
|
394
|
+
`;
|
|
395
|
+
|
|
396
|
+
this.#init();
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
#init = () => {
|
|
400
|
+
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
add = (sender, message, info, data, unique) => {
|
|
405
|
+
|
|
406
|
+
if (unique && !Array.isArray(unique)) {
|
|
407
|
+
unique = unique.split(",");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
const target = this.shadowRoot.querySelector(".chat-body");
|
|
411
|
+
|
|
412
|
+
//console.log(data);
|
|
413
|
+
|
|
414
|
+
let el;
|
|
415
|
+
switch (sender) {
|
|
416
|
+
case "me":
|
|
417
|
+
el = document.createElement("nx-ai-my-message");
|
|
418
|
+
el.setAttribute("message", message);
|
|
419
|
+
target.appendChild(el);
|
|
420
|
+
break;
|
|
421
|
+
|
|
422
|
+
case "ing":
|
|
423
|
+
el = document.createElement("nx-ai-ing-message");
|
|
424
|
+
//el.setAttribute("message", message);
|
|
425
|
+
target.appendChild(el);
|
|
426
|
+
break;
|
|
427
|
+
|
|
428
|
+
case "ai":
|
|
429
|
+
this.shadowRoot.querySelectorAll("nx-ai-ing-message").forEach(el => {
|
|
430
|
+
el.remove();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
el = document.createElement("nx-ai-message");
|
|
434
|
+
el.setAttribute("message", message);
|
|
435
|
+
el.setAttribute("row-count", data ? data.length : 0);
|
|
436
|
+
//el.data = data;
|
|
437
|
+
el.initialize(info, data, unique);
|
|
438
|
+
target.appendChild(el);
|
|
439
|
+
break;
|
|
440
|
+
|
|
441
|
+
default:
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
setTimeout(() => {
|
|
446
|
+
el.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
447
|
+
}, 200);
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* 챗 메시지를 추가합니다.
|
|
452
|
+
* @param {string} sender - 메시지 발신자 ('me', 'ing', 'ai', 'progress')
|
|
453
|
+
* @param {string} message - 메시지 내용 (progress 타입의 경우 사용되지 않음)
|
|
454
|
+
* @param {Array<Object>} info - nine-grid 정보 (ai 타입에만 해당)
|
|
455
|
+
* @param {Array<Object>} data - nine-grid 데이터 (ai 타입에만 해당)
|
|
456
|
+
* @param {Array<string>|string} unique - nine-grid 고유 키 (ai 타입에만 해당)
|
|
457
|
+
* @param {Array<Object>} progressData - aiProgressMessage의 초기 진행 데이터 (progress 타입에만 해당)
|
|
458
|
+
* @returns {HTMLElement|null} 생성된 메시지 엘리먼트 (특히 aiProgressMessage의 경우 업데이트를 위해 반환)
|
|
459
|
+
*/
|
|
460
|
+
addProgress = (progressData = null) => { // progressData 파라미터 추가
|
|
461
|
+
|
|
462
|
+
const target = this.shadowRoot.querySelector(".chat-body");
|
|
463
|
+
|
|
464
|
+
this.shadowRoot.querySelectorAll("nx-ai-ing-message").forEach(el => {
|
|
465
|
+
el.remove();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
let el = document.createElement("nx-ai-progress-message");
|
|
469
|
+
if (progressData) {
|
|
470
|
+
el.initialize(progressData); // progressData로 초기화
|
|
471
|
+
}
|
|
472
|
+
target.appendChild(el);
|
|
473
|
+
|
|
474
|
+
setTimeout(() => {
|
|
475
|
+
if (el) { // el이 null이 아닐 때만 scrollIntoView 호출
|
|
476
|
+
el.scrollIntoView({ behavior: "smooth", block: "end" });
|
|
477
|
+
}
|
|
478
|
+
}, 200);
|
|
479
|
+
|
|
480
|
+
return el; // 생성된 엘리먼트를 반환
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
if (!customElements.get('nx-ai-message')) {
|
|
486
|
+
customElements.define("nx-ai-message", aiMessage);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (!customElements.get('nx-ai-ing-message')) {
|
|
490
|
+
customElements.define("nx-ai-ing-message", aiIngMessage);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (!customElements.get('nx-ai-my-message')) {
|
|
494
|
+
customElements.define("nx-ai-my-message", aiMyMessage);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (!customElements.get('nx-ai-progress-message')) {
|
|
498
|
+
customElements.define("nx-ai-progress-message", aiProgressMessage);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (!customElements.get('nine-chat-message')) {
|
|
502
|
+
customElements.define("nine-chat-message", ChatMessage);
|
|
503
|
+
}
|
|
@@ -95,7 +95,7 @@ export class NineChat extends HTMLElement {
|
|
|
95
95
|
<div class="logo"><span></span></div>
|
|
96
96
|
<div id="status-tag" style="font-size:10px; color:#ff5722; padding:5px;">nine-mu Engine Ready</div>
|
|
97
97
|
</div>
|
|
98
|
-
<
|
|
98
|
+
<nine-chat-message></nine-chat-message>
|
|
99
99
|
<div class="foot">
|
|
100
100
|
<div class="apply-src">
|
|
101
101
|
${['MyBatis', 'Service', 'Controller', 'JavaScript'].map(t => `
|