@nine-lab/nine-mu 0.1.351 → 0.1.352
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 +79 -72
- package/dist/nine-mu.js.map +1 -1
- package/dist/nine-mu.umd.js +1 -1
- package/dist/nine-mu.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/exception/ExceptionHook.js +76 -83
package/package.json
CHANGED
|
@@ -1,73 +1,68 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
// ==========================================
|
|
4
|
-
// 1. 터미널
|
|
4
|
+
// 1. 순수 리눅스 터미널 CLI 스타일 (박스 테두리 제거)
|
|
5
5
|
// ==========================================
|
|
6
6
|
export const NineExceptionStyles = {
|
|
7
7
|
container: {
|
|
8
8
|
padding: '30px',
|
|
9
9
|
margin: '20px',
|
|
10
10
|
background: '#121314', // 터미널 다크 블랙 배경
|
|
11
|
-
border: '1px solid #343A40', //
|
|
11
|
+
border: '1px solid #343A40', // 외곽 메탈릭 테두리만 유지
|
|
12
12
|
borderRadius: '6px',
|
|
13
|
-
fontFamily: 'Consolas, Monaco, "Courier New", monospace', //
|
|
13
|
+
fontFamily: 'Consolas, Monaco, "Courier New", monospace', // 완벽한 고정폭 서체
|
|
14
14
|
textAlign: 'left',
|
|
15
|
-
boxShadow: '0 4px 12px rgba(0,0,0,0.3)'
|
|
15
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
|
16
|
+
lineHeight: '1.6'
|
|
16
17
|
},
|
|
17
|
-
|
|
18
|
-
color: '#
|
|
19
|
-
marginTop: 0,
|
|
20
|
-
fontSize: '16px',
|
|
21
|
-
fontWeight: '600',
|
|
22
|
-
display: 'flex',
|
|
23
|
-
alignItems: 'center',
|
|
24
|
-
gap: '8px',
|
|
25
|
-
borderBottom: '1px solid #2D3139', // 상단 바 구분선 느낌
|
|
26
|
-
paddingBottom: '12px'
|
|
27
|
-
},
|
|
28
|
-
label: {
|
|
29
|
-
margin: '15px 0 8px 0',
|
|
18
|
+
systemError: {
|
|
19
|
+
color: '#E53E3E', // 강렬한 터미널 레드 Warning
|
|
30
20
|
fontWeight: 'bold',
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
fontSize: '14px',
|
|
22
|
+
margin: '0 0 10px 0',
|
|
23
|
+
letterSpacing: '0.5px'
|
|
33
24
|
},
|
|
25
|
+
// 내부 테두리 박스를 완전히 제거하고 순수 텍스트만 출력되도록 수정
|
|
34
26
|
pre: {
|
|
35
|
-
background: '
|
|
36
|
-
color: '#F8F9FA',
|
|
37
|
-
padding: '
|
|
38
|
-
|
|
39
|
-
border: '
|
|
27
|
+
background: 'transparent',
|
|
28
|
+
color: '#F8F9FA',
|
|
29
|
+
padding: '0',
|
|
30
|
+
margin: '0 0 20px 0',
|
|
31
|
+
border: 'none',
|
|
40
32
|
overflowX: 'auto',
|
|
41
33
|
whiteSpace: 'pre-wrap',
|
|
42
34
|
fontFamily: 'Consolas, Monaco, monospace',
|
|
43
|
-
fontSize: '
|
|
44
|
-
lineHeight: '1.5'
|
|
35
|
+
fontSize: '14px'
|
|
45
36
|
},
|
|
46
37
|
footer: {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
color: '#718096',
|
|
38
|
+
fontSize: '14px',
|
|
39
|
+
color: '#E2E8F0',
|
|
50
40
|
display: 'flex',
|
|
51
41
|
flexDirection: 'column',
|
|
42
|
+
gap: '8px'
|
|
43
|
+
},
|
|
44
|
+
promptRow: {
|
|
45
|
+
display: 'flex',
|
|
46
|
+
alignItems: 'center',
|
|
52
47
|
gap: '10px'
|
|
53
48
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
background: '
|
|
57
|
-
color: '#
|
|
49
|
+
// 버튼을 없애고 터미널 선택지 [Y/n] 스타일의 상호작용 요소로 변경
|
|
50
|
+
cliButton: {
|
|
51
|
+
background: 'transparent',
|
|
52
|
+
color: '#38A169', // 터미널 그린 포인트
|
|
58
53
|
border: 'none',
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
padding: '0',
|
|
55
|
+
fontFamily: 'Consolas, Monaco, monospace',
|
|
56
|
+
fontSize: '14px',
|
|
61
57
|
fontWeight: 'bold',
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
fontFamily: 'sans-serif' // 버튼 텍스트는 일반 폰트로 가독성 확보
|
|
58
|
+
cursor: 'pointer',
|
|
59
|
+
textDecoration: 'underline',
|
|
60
|
+
textUnderlineOffset: '3px'
|
|
66
61
|
}
|
|
67
62
|
};
|
|
68
63
|
|
|
69
64
|
// ==========================================
|
|
70
|
-
// 2. 컴포넌트 본문 (
|
|
65
|
+
// 2. 컴포넌트 본문 (키보드 이벤트 내장)
|
|
71
66
|
// ==========================================
|
|
72
67
|
export class NineExceptionHook extends React.Component {
|
|
73
68
|
|
|
@@ -77,6 +72,7 @@ export class NineExceptionHook extends React.Component {
|
|
|
77
72
|
this.lastPath = window.location.pathname;
|
|
78
73
|
this.handleLocationChange = this.handleLocationChange.bind(this);
|
|
79
74
|
this.handleReport = this.handleReport.bind(this);
|
|
75
|
+
this.handleKeyDown = this.handleKeyDown.bind(this);
|
|
80
76
|
}
|
|
81
77
|
|
|
82
78
|
static getDerivedStateFromError(error) {
|
|
@@ -85,7 +81,6 @@ export class NineExceptionHook extends React.Component {
|
|
|
85
81
|
|
|
86
82
|
componentDidCatch(error, errorInfo) {
|
|
87
83
|
console.error("🚨 [Nine-Library] 본문 렌더링 에러 포착:", error);
|
|
88
|
-
|
|
89
84
|
if (this.props.onCatch) {
|
|
90
85
|
this.props.onCatch(error, errorInfo);
|
|
91
86
|
}
|
|
@@ -94,17 +89,29 @@ export class NineExceptionHook extends React.Component {
|
|
|
94
89
|
componentDidMount() {
|
|
95
90
|
window.addEventListener('popstate', this.handleLocationChange);
|
|
96
91
|
document.addEventListener('click', this.handleLocationChange, true);
|
|
92
|
+
window.addEventListener('keydown', this.handleKeyDown); // ⌨️ 키보드 훅 등록
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
componentWillUnmount() {
|
|
100
96
|
window.removeEventListener('popstate', this.handleLocationChange);
|
|
101
97
|
document.removeEventListener('click', this.handleLocationChange, true);
|
|
98
|
+
window.removeEventListener('keydown', this.handleKeyDown);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 실제 사용자가 키보드로 'Y' 또는 'y'를 누르면 마우스 클릭과 동일하게 관제탑 전송 실행
|
|
102
|
+
handleKeyDown(e) {
|
|
103
|
+
if (this.state.hasError && this.state.reportStatus === 'idle') {
|
|
104
|
+
if (e.key === 'y' || e.key === 'Y') {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
this.handleReport();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
handleLocationChange() {
|
|
105
112
|
setTimeout(() => {
|
|
106
113
|
if (this.state.hasError && this.lastPath !== window.location.pathname) {
|
|
107
|
-
console.log("[Nine-Library] 페이지 주소 변경 감지 ->
|
|
114
|
+
console.log("[Nine-Library] 페이지 주소 변경 감지 -> 에러 상태 초기화");
|
|
108
115
|
this.lastPath = window.location.pathname;
|
|
109
116
|
this.setState({ hasError: false, error: null, reportStatus: 'idle' });
|
|
110
117
|
}
|
|
@@ -124,7 +131,7 @@ export class NineExceptionHook extends React.Component {
|
|
|
124
131
|
console.warn("[Nine-Library] window.triggerAutoRecovery 가 정의되지 않았습니다.");
|
|
125
132
|
}
|
|
126
133
|
this.setState({ reportStatus: 'success' });
|
|
127
|
-
},
|
|
134
|
+
}, 600);
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
render() {
|
|
@@ -136,66 +143,52 @@ export class NineExceptionHook extends React.Component {
|
|
|
136
143
|
const errorMessage = this.state.error?.message || String(this.state.error);
|
|
137
144
|
const customStyles = this.props.styles || {};
|
|
138
145
|
|
|
139
|
-
let buttonText = '🚀 관제탑에 에러 피드백 전송하기';
|
|
140
|
-
if (this.state.reportStatus === 'sending') buttonText = '⏳ 관제탑 연락 중...';
|
|
141
|
-
if (this.state.reportStatus === 'success') buttonText = '✅ 전송 완료! AI 분석 시작';
|
|
142
|
-
|
|
143
146
|
return React.createElement(
|
|
144
147
|
'div',
|
|
145
148
|
{
|
|
146
149
|
style: { ...NineExceptionStyles.container, ...customStyles.container, ...this.props.containerStyle }
|
|
147
150
|
},
|
|
148
151
|
|
|
149
|
-
|
|
150
|
-
// 1. 상단 프롬프트 헤더 (시스템 상태 알림)
|
|
152
|
+
// 1. 에러 감지 시스템 로그 (레드)
|
|
151
153
|
React.createElement(
|
|
152
154
|
'div',
|
|
153
|
-
{ style: { ...NineExceptionStyles.
|
|
154
|
-
React.createElement('span', { style: { color: '#38A169', marginRight: '5px' } }, 'nandoo@nine-mu:~$'),
|
|
155
|
-
'./system-check --status'
|
|
156
|
-
), */
|
|
157
|
-
|
|
158
|
-
// 2. 시스템 에러 감지 출력 로그
|
|
159
|
-
React.createElement(
|
|
160
|
-
'div',
|
|
161
|
-
{ style: { color: 'red', margin: '15px 0 5px 0', letterSpacing: '0.5px' } },
|
|
155
|
+
{ style: { ...NineExceptionStyles.systemError, ...customStyles.systemError } },
|
|
162
156
|
'[SYSTEM] COMPONENT RENDER ERROR DETECTED'
|
|
163
157
|
),
|
|
164
158
|
|
|
165
|
-
|
|
166
|
-
// 3. 명령어 입력 시뮬레이션 ($ cat error.log)
|
|
167
|
-
React.createElement(
|
|
168
|
-
'p',
|
|
169
|
-
{ style: { ...NineExceptionStyles.label, ...customStyles.label } },
|
|
170
|
-
React.createElement('span', { style: { color: '#3182CE', marginRight: '6px' } }, 'nandoo@nine-mu:~$'),
|
|
171
|
-
'cat error.log'
|
|
172
|
-
), */
|
|
173
|
-
|
|
174
|
-
// 4. 에러 내용 본문 박스
|
|
159
|
+
// 2. 에러 상세 원인 (박스 없는 순수 텍스트 출력)
|
|
175
160
|
React.createElement('pre', { style: { ...NineExceptionStyles.pre, ...customStyles.pre } }, errorMessage),
|
|
176
161
|
|
|
177
|
-
//
|
|
162
|
+
// 3. CLI 양방향 인터페이스 (y/n 프롬프트)
|
|
178
163
|
React.createElement(
|
|
179
164
|
'div',
|
|
180
165
|
{ style: { ...NineExceptionStyles.footer, ...customStyles.footer } },
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
166
|
+
|
|
167
|
+
this.state.reportStatus === 'idle' && React.createElement(
|
|
168
|
+
'div',
|
|
169
|
+
{ style: NineExceptionStyles.promptRow },
|
|
170
|
+
React.createElement('span', null, 'Send error log to Control Tower (y/n)? '),
|
|
171
|
+
React.createElement(
|
|
172
|
+
'button',
|
|
173
|
+
{
|
|
174
|
+
style: { ...NineExceptionStyles.cliButton, ...customStyles.cliButton },
|
|
175
|
+
onClick: this.handleReport
|
|
189
176
|
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
177
|
+
'[Y]'
|
|
178
|
+
)
|
|
179
|
+
),
|
|
180
|
+
|
|
181
|
+
this.state.reportStatus === 'sending' && React.createElement(
|
|
182
|
+
'div',
|
|
183
|
+
{ style: { color: '#ED8936' } },
|
|
184
|
+
'>> Connecting to Control Tower... Sending payload.'
|
|
194
185
|
),
|
|
186
|
+
|
|
195
187
|
this.state.reportStatus === 'success' && React.createElement(
|
|
196
188
|
'div',
|
|
197
|
-
{ style: {
|
|
198
|
-
'
|
|
189
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: '4px' } },
|
|
190
|
+
React.createElement('div', { style: { color: '#38A169', fontWeight: 'bold' } }, '>> Transmit complete. Core engine initialized.'),
|
|
191
|
+
React.createElement('div', { style: { color: '#A0AEC0', fontStyle: 'italic', fontSize: '13px' } }, 'AI가 소스 코드를 재수정하는 동안 잠시만 기다려주세요...')
|
|
199
192
|
)
|
|
200
193
|
)
|
|
201
194
|
);
|