@nine-lab/nine-mu 0.1.333 → 0.1.335
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/css/nine-mu.css +88 -0
- package/dist/nine-mu.js +128 -68
- 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/public/css/nine-mu.css +88 -0
- package/src/components/ChatMessage.js +1 -1
- package/src/components/NineChat.js +2 -1
- package/src/components/NineDiff.js +1 -1
- package/src/components/NineNatualQueryInput.js +1 -1
- package/src/components/exception/ExceptionHook.js +107 -46
package/package.json
CHANGED
package/public/css/nine-mu.css
CHANGED
|
@@ -940,4 +940,92 @@
|
|
|
940
940
|
/* JS로 내용이 채워지므로 별도의 CSS 애니메이션은 필요 없음 */
|
|
941
941
|
}
|
|
942
942
|
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
:host(nine-exception-hook) {
|
|
946
|
+
display: block;
|
|
947
|
+
width: 100%;
|
|
948
|
+
height: 100%;
|
|
949
|
+
color: inherit;
|
|
950
|
+
font-size: inherit;
|
|
951
|
+
line-height: inherit;
|
|
952
|
+
|
|
953
|
+
/* 내부 모든 엘리먼트의 여백 계산법을 어드민 기본형에 동기화 */
|
|
954
|
+
.nine-error-container *,
|
|
955
|
+
.nine-error-container *::before,
|
|
956
|
+
.nine-error-container *::after {
|
|
957
|
+
box-sizing: border-box;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
.nine-error-container {
|
|
961
|
+
padding: 30px;
|
|
962
|
+
margin: 20px;
|
|
963
|
+
background-color: #FFF5F5;
|
|
964
|
+
border: 1px solid #FEB2B2;
|
|
965
|
+
border-radius: 8px;
|
|
966
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.nine-error-title {
|
|
970
|
+
color: #C53030;
|
|
971
|
+
margin-top: 0;
|
|
972
|
+
font-size: 18px;
|
|
973
|
+
font-weight: bold;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
.nine-error-label {
|
|
977
|
+
margin: 10px 0 5px 0;
|
|
978
|
+
font-weight: bold;
|
|
979
|
+
color: #4A5568;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.nine-error-log {
|
|
983
|
+
background-color: #1A202C;
|
|
984
|
+
color: #EDF2F7;
|
|
985
|
+
padding: 15px;
|
|
986
|
+
border-radius: 6px;
|
|
987
|
+
overflow-x: auto;
|
|
988
|
+
white-space: pre-wrap;
|
|
989
|
+
font-family: 'Courier New', Courier, monospace;
|
|
990
|
+
font-size: 14px;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/* 💡 버튼 커스텀 디자인 세트 */
|
|
994
|
+
.nine-ai-btn {
|
|
995
|
+
background-color: #3182CE;
|
|
996
|
+
color: white;
|
|
997
|
+
padding: 10px 20px;
|
|
998
|
+
font-size: 14px;
|
|
999
|
+
font-weight: bold;
|
|
1000
|
+
border: none;
|
|
1001
|
+
border-radius: 6px;
|
|
1002
|
+
cursor: pointer;
|
|
1003
|
+
transition: background-color 0.2s ease;
|
|
1004
|
+
white-space: nowrap;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.nine-ai-btn:hover {
|
|
1008
|
+
background-color: #2B6CB0;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
.nine-ai-btn:disabled {
|
|
1012
|
+
background-color: #A0AEC0;
|
|
1013
|
+
cursor: not-allowed;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
.nine-ai-btn.loading {
|
|
1017
|
+
background-color: #4A5568;
|
|
1018
|
+
animation: pulse 1.5s infinite ease-in-out;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
.nine-error-footer {
|
|
1022
|
+
font-size: 14px;
|
|
1023
|
+
color: #718096;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
@keyframes pulse {
|
|
1028
|
+
0% { opacity: 0.6; }
|
|
1029
|
+
50% { opacity: 1; }
|
|
1030
|
+
100% { opacity: 0.6; }
|
|
943
1031
|
}
|
|
@@ -367,7 +367,7 @@ class ChatMessageBody extends HTMLElement
|
|
|
367
367
|
|
|
368
368
|
#renderer = () => {
|
|
369
369
|
|
|
370
|
-
const customImport = nine.cssPath ? `@import "${nine.cssPath}/
|
|
370
|
+
const customImport = nine.cssPath ? `@import "${nine.cssPath}/nine-mu.css";` : "";
|
|
371
371
|
|
|
372
372
|
this.shadowRoot.innerHTML = `
|
|
373
373
|
<style>
|
|
@@ -131,7 +131,8 @@ export class NineChat extends HTMLElement {
|
|
|
131
131
|
|
|
132
132
|
#render() {
|
|
133
133
|
const placeholder = this.getAttribute("placeholder") || "나에게 무엇이든 물어봐...";
|
|
134
|
-
|
|
134
|
+
|
|
135
|
+
const customImport = nine.cssPath ? `@import "${nine.cssPath}/nine-mu.css";` : "";
|
|
135
136
|
|
|
136
137
|
this.shadowRoot.innerHTML = `
|
|
137
138
|
<style>
|
|
@@ -196,7 +196,7 @@ export class NineDiff extends HTMLElement {
|
|
|
196
196
|
|
|
197
197
|
connectedCallback() {
|
|
198
198
|
|
|
199
|
-
const customImport =
|
|
199
|
+
const customImport = nine.cssPath ? `@import "${nine.cssPath}/nine-mu.css";` : "";
|
|
200
200
|
|
|
201
201
|
this.shadowRoot.innerHTML = `
|
|
202
202
|
<style>
|
|
@@ -161,7 +161,7 @@ export class NineChat extends HTMLElement {
|
|
|
161
161
|
|
|
162
162
|
#render() {
|
|
163
163
|
const placeholder = this.getAttribute("placeholder") || "나에게 무엇이든 물어봐...";
|
|
164
|
-
const customImport =
|
|
164
|
+
const customImport = nine.cssPath ? `@import "${nine.cssPath}/nine-mu.css";` : "";
|
|
165
165
|
|
|
166
166
|
this.shadowRoot.innerHTML = `
|
|
167
167
|
<style>
|
|
@@ -1,94 +1,155 @@
|
|
|
1
|
+
// src/components/exception/ExceptionHook.js
|
|
1
2
|
export class ExceptionHook extends HTMLElement {
|
|
3
|
+
// 💡 [핵심] 비공개 인스턴스 필드는 클래스 최상단에 먼저 선언해야 합니다.
|
|
4
|
+
#lastPath = window.location.pathname;
|
|
5
|
+
#hasError = false;
|
|
6
|
+
#cssPath = "/css/nine-exception-hook.css";
|
|
7
|
+
|
|
2
8
|
constructor() {
|
|
3
9
|
super();
|
|
4
|
-
|
|
5
|
-
this.
|
|
6
|
-
|
|
7
|
-
// 💡 깔끔하게 명칭을 싱크한 외부 CSS 경로
|
|
8
|
-
this.cssPath = "/css/nine-exception-hook.css";
|
|
10
|
+
// Shadow DOM(open 모드) 활성화
|
|
11
|
+
this.attachShadow({ mode: 'open' });
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
connectedCallback() {
|
|
12
|
-
|
|
15
|
+
// 기본 스켈레톤 마크업 렌더링
|
|
16
|
+
this.#renderer();
|
|
13
17
|
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
window.addEventListener('
|
|
18
|
-
|
|
18
|
+
// 정적 외부 스타일시트 링크 안전하게 결합
|
|
19
|
+
this.#injectShadowStylesheet();
|
|
20
|
+
|
|
21
|
+
window.addEventListener('error', this.#handleRuntimeError, true);
|
|
22
|
+
window.addEventListener('unhandledrejection', this.#handlePromiseError, true);
|
|
23
|
+
window.addEventListener('popstate', this.#handleLocationChange);
|
|
24
|
+
document.addEventListener('click', this.#handleLocationChange, true);
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
disconnectedCallback() {
|
|
22
|
-
window.removeEventListener('error', this
|
|
23
|
-
window.removeEventListener('unhandledrejection', this
|
|
24
|
-
window.removeEventListener('popstate', this
|
|
25
|
-
document.removeEventListener('click', this
|
|
28
|
+
window.removeEventListener('error', this.#handleRuntimeError, true);
|
|
29
|
+
window.removeEventListener('unhandledrejection', this.#handlePromiseError, true);
|
|
30
|
+
window.removeEventListener('popstate', this.#handleLocationChange);
|
|
31
|
+
document.removeEventListener('click', this.#handleLocationChange, true);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#renderer = () => {
|
|
35
|
+
const customImport = nine.cssPath ? `@import "${nine.cssPath}/nine-mu.css";` : "";
|
|
36
|
+
|
|
37
|
+
this.shadowRoot.innerHTML = `
|
|
38
|
+
<style>
|
|
39
|
+
@import "https://cdn.jsdelivr.net/npm/@nine-lab/nine-mu@${__APP_VERSION__}/dist/css/nine-mu.css";
|
|
40
|
+
${customImport}
|
|
41
|
+
</style>
|
|
42
|
+
|
|
43
|
+
<div class="nine-hook-core">
|
|
44
|
+
<slot></slot>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="nine-exception-board"></div>
|
|
47
|
+
`;
|
|
26
48
|
}
|
|
27
49
|
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
#injectShadowStylesheet() {
|
|
51
|
+
// 💡 내부 필드 호출 시에도 모두 this.#형식으로 매핑합니다.
|
|
52
|
+
if (this.shadowRoot.querySelector(`link[href="${this.#cssPath}"]`)) return;
|
|
30
53
|
|
|
31
54
|
const link = document.createElement('link');
|
|
32
55
|
link.rel = 'stylesheet';
|
|
33
|
-
link.href = this
|
|
34
|
-
|
|
56
|
+
link.href = this.#cssPath;
|
|
57
|
+
this.shadowRoot.appendChild(link);
|
|
35
58
|
}
|
|
36
59
|
|
|
37
|
-
handleRuntimeError(event) {
|
|
60
|
+
#handleRuntimeError = (event) => {
|
|
61
|
+
if (this.#hasError) return;
|
|
62
|
+
|
|
38
63
|
if (this.contains(event.target) || event.error) {
|
|
39
|
-
this
|
|
64
|
+
this.#renderError(event.error?.message || event.message, event.error?.stack);
|
|
40
65
|
}
|
|
41
66
|
}
|
|
42
67
|
|
|
43
|
-
handlePromiseError(event) {
|
|
44
|
-
|
|
68
|
+
#handlePromiseError = (event) => {
|
|
69
|
+
if (this.#hasError) return;
|
|
70
|
+
this.#renderError(event.reason?.message || String(event.reason), event.reason?.stack);
|
|
45
71
|
}
|
|
46
72
|
|
|
47
|
-
handleLocationChange() {
|
|
73
|
+
#handleLocationChange = () => {
|
|
48
74
|
setTimeout(() => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this
|
|
53
|
-
this.hasError = false;
|
|
75
|
+
if (this.#hasError && this.#lastPath !== window.location.pathname) {
|
|
76
|
+
console.log("✨ [nine-exception-hook] Shadow DOM 내부 예외 청소 및 정상 복구");
|
|
77
|
+
this.#lastPath = window.location.pathname;
|
|
78
|
+
this.#hasError = false;
|
|
54
79
|
|
|
55
|
-
const
|
|
56
|
-
if (
|
|
80
|
+
const exceptionBoard = this.shadowRoot.querySelector('.nine-exception-board');
|
|
81
|
+
if (exceptionBoard) exceptionBoard.innerHTML = '';
|
|
57
82
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
});
|
|
83
|
+
const coreContainer = this.shadowRoot.querySelector('.nine-hook-core');
|
|
84
|
+
if (coreContainer) coreContainer.style.display = '';
|
|
61
85
|
}
|
|
62
86
|
}, 10);
|
|
63
87
|
}
|
|
64
88
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
89
|
+
#sendToAiAction = async (errorMessage, errorStack) => {
|
|
90
|
+
const btn = this.shadowRoot.querySelector('.nine-ai-btn');
|
|
91
|
+
const footer = this.shadowRoot.querySelector('.nine-error-footer');
|
|
92
|
+
if (!btn) return;
|
|
93
|
+
|
|
94
|
+
btn.disabled = true;
|
|
95
|
+
btn.innerText = "⚡ AI 관제탑에 소스 분석 요청 중...";
|
|
96
|
+
btn.classList.add('loading');
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
if (window.triggerAutoRecovery) {
|
|
100
|
+
await window.triggerAutoRecovery({
|
|
101
|
+
message: errorMessage,
|
|
102
|
+
stack: errorStack || "No stack trace available",
|
|
103
|
+
path: window.location.pathname
|
|
104
|
+
});
|
|
68
105
|
|
|
69
|
-
|
|
70
|
-
|
|
106
|
+
footer.innerHTML = `✅ <b>AI 관제탑 접수 완료:</b> 분석을 바탕으로 소스 코드를 교정하고 있습니다. 잠시만 기다려주세요.`;
|
|
107
|
+
btn.innerText = "🚀 전송 완료";
|
|
108
|
+
} else {
|
|
109
|
+
throw new Error("window.triggerAutoRecovery 관제탑 엔진이 로드되지 않았습니다.");
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error("❌ AI 전송 실패:", err);
|
|
113
|
+
btn.disabled = false;
|
|
114
|
+
btn.innerText = "❌ 재전송 시도";
|
|
115
|
+
footer.innerHTML = `<span style="color: #E53E3E;">⚠️ 전송 실패: ${err.message}</span>`;
|
|
71
116
|
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#renderError(errorMessage, errorStack) {
|
|
120
|
+
if (this.#hasError) return;
|
|
121
|
+
this.#hasError = true;
|
|
122
|
+
|
|
123
|
+
const coreContainer = this.shadowRoot.querySelector('.nine-hook-core');
|
|
124
|
+
if (coreContainer) coreContainer.style.display = 'none';
|
|
72
125
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
});
|
|
126
|
+
const exceptionBoard = this.shadowRoot.querySelector('.nine-exception-board');
|
|
127
|
+
if (!exceptionBoard) return;
|
|
76
128
|
|
|
77
129
|
const errorHTML = `
|
|
78
130
|
<div class="nine-error-container">
|
|
79
131
|
<h3 class="nine-error-title">⚠️ 시스템 예외 감지 (${this.tagName.toLowerCase()})</h3>
|
|
80
132
|
<p class="nine-error-label">[에러 원인 로그]:</p>
|
|
81
133
|
<pre class="nine-error-log">${errorMessage}</pre>
|
|
82
|
-
|
|
83
|
-
|
|
134
|
+
|
|
135
|
+
<div class="nine-action-area" style="margin-top: 20px; display: flex; align-items: center; gap: 15px;">
|
|
136
|
+
<button class="nine-ai-btn">🤖 AI에게 소스코드 수정 요청</button>
|
|
137
|
+
<div class="nine-error-footer">
|
|
138
|
+
버튼을 누르면 AI가 로그 및 컴포넌트 맵을 역추적하여 수정을 시작합니다.
|
|
139
|
+
</div>
|
|
84
140
|
</div>
|
|
85
141
|
</div>
|
|
86
142
|
`;
|
|
87
|
-
|
|
143
|
+
|
|
144
|
+
exceptionBoard.innerHTML = errorHTML;
|
|
145
|
+
|
|
146
|
+
const aiBtn = this.shadowRoot.querySelector('.nine-ai-btn');
|
|
147
|
+
if (aiBtn) {
|
|
148
|
+
aiBtn.addEventListener('click', () => this.#sendToAiAction(errorMessage, errorStack));
|
|
149
|
+
}
|
|
88
150
|
}
|
|
89
151
|
}
|
|
90
152
|
|
|
91
|
-
// 💡 전역 브라우저 템플릿 태그로 공식 등록
|
|
92
153
|
if (!customElements.get('nine-exception-hook')) {
|
|
93
154
|
customElements.define('nine-exception-hook', ExceptionHook);
|
|
94
155
|
}
|