ide-assi 0.330.0 → 0.333.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.
- package/dist/bundle.cjs.js +93327 -55365
- package/dist/bundle.esm.js +93327 -55365
- package/dist/components/ideDiff.js +229 -110
- package/dist/components/ideDiff.js.bak +141 -0
- package/package.json +14 -7
- package/src/components/ideDiff.js +229 -110
- package/src/components/ideDiff.js.bak +141 -0
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import ninegrid from "ninegrid2";
|
|
2
|
-
import {diff_match_patch} from 'diff-match-patch';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
// CodeMirror 6 핵심 및 확장 임포트
|
|
4
|
+
import { EditorView, lineNumbers, highlightSpecialChars, drawSelection, dropCursor, keymap, highlightActiveLine, highlightActiveLineGutter } from "@codemirror/view";
|
|
5
|
+
import { EditorState, Compartment } from "@codemirror/state";
|
|
6
|
+
import { history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
7
|
+
// ⭐️ 여기서 searchKeymap을 제거합니다.
|
|
8
|
+
import { defaultKeymap, selectAll } from "@codemirror/commands"; // selectAll은 commands에 남아있습니다.
|
|
9
|
+
// ⭐️ searchKeymap은 이제 @codemirror/search 에서 가져옵니다.
|
|
10
|
+
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
|
11
|
+
import { bracketMatching } from "@codemirror/language";
|
|
12
|
+
import { javascript } from "@codemirror/lang-javascript"; // 예시 언어 (필요에 따라 다른 언어 팩 추가)
|
|
13
|
+
import { indentOnInput, syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language"; // syntaxHighlighting 추가
|
|
14
|
+
import { lintKeymap } from "@codemirror/lint";
|
|
15
|
+
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
16
|
+
|
|
17
|
+
// Diff 로직을 위해 diff-match-patch 사용
|
|
18
|
+
import { diff_match_patch } from 'diff-match-patch';
|
|
19
|
+
|
|
20
|
+
// ... (나머지 IdeDiff 클래스 코드는 동일) ...
|
|
21
|
+
|
|
22
|
+
export class IdeDiff extends HTMLElement {
|
|
23
|
+
#asisEditorView; // asis 패널의 CodeMirror EditorView 인스턴스
|
|
24
|
+
#tobeEditorView; // tobe 패널의 CodeMirror EditorView 인스턴스
|
|
25
|
+
#asisEditorEl; // asis 에디터를 렌더링할 DOM 요소
|
|
26
|
+
#tobeEditorEl; // tobe 에디터를 렌더링할 DOM 요소
|
|
27
|
+
|
|
28
|
+
#languageCompartment = new Compartment(); // 언어 확장을 동적으로 변경하기 위한 Compartment
|
|
10
29
|
|
|
11
30
|
constructor() {
|
|
12
31
|
super();
|
|
@@ -15,127 +34,227 @@ export class IdeDiff extends HTMLElement
|
|
|
15
34
|
|
|
16
35
|
connectedCallback() {
|
|
17
36
|
this.shadowRoot.innerHTML = `
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
37
|
+
<style>
|
|
38
|
+
/* ninegrid CSS 및 필요한 기본 스타일 */
|
|
39
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ideDiff.css";
|
|
40
|
+
${ninegrid.getCustomPath(this, "ideDiff.css")}
|
|
41
|
+
|
|
42
|
+
/* CodeMirror를 위한 기본적인 Flexbox 레이아웃 */
|
|
43
|
+
.wrapper {
|
|
44
|
+
display: flex;
|
|
45
|
+
width: 100%;
|
|
46
|
+
height: 100%; /* 부모의 높이를 채우도록 설정 */
|
|
47
|
+
overflow: hidden; /* 내부 스크롤을 위해 overflow 처리 */
|
|
48
|
+
}
|
|
49
|
+
.panel {
|
|
50
|
+
flex: 1; /* 패널들이 남은 공간을 채우도록 */
|
|
51
|
+
overflow: hidden; /* CodeMirror EditorView 자체가 스크롤을 처리 */
|
|
52
|
+
/* position: relative; */ /* CodeMirror가 자체적으로 관리하므로 필요 없을 수 있음 */
|
|
53
|
+
min-width: 0; /* Flexbox 아이템이 content 때문에 늘어나는 것을 방지 */
|
|
54
|
+
}
|
|
55
|
+
.cm-editor {
|
|
56
|
+
height: 100%; /* EditorView가 부모 div의 높이를 채우도록 */
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Diff 시각화를 위한 기본 스타일 (나중에 데코레이션으로 더 정교하게) */
|
|
60
|
+
.cm-line.cm-deleted-line {
|
|
61
|
+
background-color: #ffeef0; /* 삭제된 줄 배경 */
|
|
62
|
+
}
|
|
63
|
+
.cm-line.cm-inserted-line {
|
|
64
|
+
background-color: #e6ffed; /* 삽입된 줄 배경 */
|
|
65
|
+
}
|
|
66
|
+
.cm-deleted-inline {
|
|
67
|
+
background-color: #ffcccc; /* 인라인 삭제 */
|
|
68
|
+
text-decoration: line-through;
|
|
69
|
+
}
|
|
70
|
+
.cm-inserted-inline {
|
|
71
|
+
background-color: #ccffcc; /* 인라인 삽입 */
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
74
|
+
|
|
75
|
+
<div class="wrapper">
|
|
76
|
+
<div class="panel asis"></div>
|
|
77
|
+
<nx-splitter></nx-splitter>
|
|
78
|
+
<div class="panel tobe"></div>
|
|
79
|
+
</div>
|
|
80
|
+
`;
|
|
61
81
|
|
|
62
82
|
requestAnimationFrame(() => {
|
|
63
|
-
this.#
|
|
83
|
+
this.#initCodeMirror();
|
|
64
84
|
});
|
|
65
|
-
}
|
|
85
|
+
}
|
|
66
86
|
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
this.#
|
|
70
|
-
this.#tobePre = this.shadowRoot.querySelector('.tobe pre');
|
|
87
|
+
#initCodeMirror = () => {
|
|
88
|
+
this.#asisEditorEl = this.shadowRoot.querySelector('.panel.asis');
|
|
89
|
+
this.#tobeEditorEl = this.shadowRoot.querySelector('.panel.tobe');
|
|
71
90
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
if (!this.#asisEditorEl || !this.#tobeEditorEl) {
|
|
92
|
+
console.error('CodeMirror panel containers not found!');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// CodeMirror 확장 기본 설정
|
|
97
|
+
const basicExtensions = [
|
|
98
|
+
lineNumbers(), // 줄 번호
|
|
99
|
+
highlightSpecialChars(), // 특수 문자 강조 (예: 공백)
|
|
100
|
+
history(), // 실행 취소/다시 실행
|
|
101
|
+
drawSelection(), // 선택 영역 표시
|
|
102
|
+
dropCursor(), // 드래그 시 커서 위치
|
|
103
|
+
EditorState.allowMultipleSelections.of(true), // 다중 선택 허용
|
|
104
|
+
indentOnInput(), // 입력 시 자동 들여쓰기
|
|
105
|
+
bracketMatching(), // 괄호 짝 맞추기
|
|
106
|
+
highlightActiveLine(), // 현재 줄 강조
|
|
107
|
+
highlightSelectionMatches(), // 선택된 텍스트와 일치하는 부분 강조
|
|
108
|
+
keymap.of([
|
|
109
|
+
...defaultKeymap, // 기본 키맵 (Enter, Backspace 등)
|
|
110
|
+
...searchKeymap, // 검색 관련 키맵
|
|
111
|
+
...historyKeymap, // undo/redo 키맵
|
|
112
|
+
...lintKeymap, // 린트 관련 키맵
|
|
113
|
+
...completionKeymap, // 자동 완성 키맵
|
|
114
|
+
indentWithTab // 탭으로 들여쓰기 (필요시)
|
|
115
|
+
]),
|
|
116
|
+
syntaxHighlighting(defaultHighlightStyle, { fallback: true }), // 기본 구문 하이라이팅
|
|
117
|
+
autocompletion(), // 자동 완성
|
|
118
|
+
// EditorState.readOnly.of(true) // 기본적으로 읽기 전용으로 설정
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
// asis 에디터 생성 (읽기 전용)
|
|
122
|
+
this.#asisEditorView = new EditorView({
|
|
123
|
+
state: EditorState.create({
|
|
124
|
+
doc: '', // 초기 내용 비워둠
|
|
125
|
+
extensions: [
|
|
126
|
+
basicExtensions,
|
|
127
|
+
javascript(), // JS 언어 모드 (필요에 따라 변경)
|
|
128
|
+
EditorState.readOnly.of(true), // asis는 읽기 전용
|
|
129
|
+
this.#languageCompartment.of(javascript()) // 언어 변경을 위한 compartment
|
|
130
|
+
]
|
|
131
|
+
}),
|
|
132
|
+
parent: this.#asisEditorEl
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// tobe 에디터 생성 (읽기 전용, 또는 편집 가능하게 할 수도 있음)
|
|
136
|
+
this.#tobeEditorView = new EditorView({
|
|
137
|
+
state: EditorState.create({
|
|
138
|
+
doc: '', // 초기 내용 비워둠
|
|
139
|
+
extensions: [
|
|
140
|
+
basicExtensions,
|
|
141
|
+
javascript(), // JS 언어 모드 (필요에 따라 변경)
|
|
142
|
+
EditorState.readOnly.of(true), // tobe도 읽기 전용으로 설정 (필요시 false)
|
|
143
|
+
this.#languageCompartment.of(javascript()) // 언어 변경을 위한 compartment
|
|
144
|
+
]
|
|
145
|
+
}),
|
|
146
|
+
parent: this.#tobeEditorEl
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// 스크롤 동기화 설정
|
|
150
|
+
this.#setupScrollSync();
|
|
75
151
|
};
|
|
76
152
|
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
153
|
+
#setupScrollSync = () => {
|
|
154
|
+
let scrollingA = false;
|
|
155
|
+
let scrollingB = false;
|
|
156
|
+
|
|
157
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
158
|
+
if (!scrollingB) {
|
|
159
|
+
scrollingA = true;
|
|
160
|
+
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
161
|
+
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
162
|
+
}
|
|
163
|
+
scrollingB = false;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
167
|
+
if (!scrollingA) {
|
|
168
|
+
scrollingB = true;
|
|
169
|
+
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
170
|
+
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
171
|
+
}
|
|
172
|
+
scrollingA = false;
|
|
173
|
+
});
|
|
85
174
|
};
|
|
86
175
|
|
|
87
|
-
|
|
176
|
+
// Diff를 계산하고 에디터에 적용하는 메서드
|
|
177
|
+
#renderDiff = (asisSrc, tobeSrc) => {
|
|
88
178
|
const dmp = new diff_match_patch();
|
|
89
|
-
|
|
90
|
-
const cleanedAsisSrc = this.#asisSrc.replace(/\r\n/g, '\n');
|
|
91
|
-
const cleanedTobeSrc = this.#tobeSrc.replace(/\r\n/g, '\n');
|
|
92
|
-
|
|
93
|
-
const diffs = dmp.diff_main(cleanedAsisSrc, cleanedTobeSrc);
|
|
179
|
+
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
94
180
|
dmp.diff_cleanupSemantic(diffs);
|
|
95
181
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
182
|
+
// TODO: diffs 결과를 바탕으로 CodeMirror 데코레이션(Decoration)을 생성하고 에디터에 적용하는 로직 구현
|
|
183
|
+
// 이 부분은 CodeMirror의 Decoration, StateField, ViewPlugin 등을 이해하고 구현해야 합니다.
|
|
184
|
+
// 각 줄에 대한 변경 상태(삽입/삭제/동일)를 판단하고, 인라인 변경(단어 단위)도 처리해야 합니다.
|
|
185
|
+
// 예를 들어:
|
|
186
|
+
// const asisDecorations = [];
|
|
187
|
+
// const tobeDecorations = [];
|
|
188
|
+
// let asisCursor = 0;
|
|
189
|
+
// let tobeCursor = 0;
|
|
190
|
+
// for (const [op, text] of diffs) {
|
|
191
|
+
// const len = text.length;
|
|
192
|
+
// if (op === diff_match_patch.DIFF_INSERT) {
|
|
193
|
+
// // tobe 쪽에 삽입된 부분 데코레이션 추가
|
|
194
|
+
// tobeDecorations.push(Decoration.mark({class: "cm-inserted-inline"}).range(tobeCursor, tobeCursor + len));
|
|
195
|
+
// tobeCursor += len;
|
|
196
|
+
// } else if (op === diff_match_patch.DIFF_DELETE) {
|
|
197
|
+
// // asis 쪽에 삭제된 부분 데코레이션 추가
|
|
198
|
+
// asisDecorations.push(Decoration.mark({class: "cm-deleted-inline"}).range(asisCursor, asisCursor + len));
|
|
199
|
+
// asisCursor += len;
|
|
200
|
+
// } else { // DIFF_EQUAL
|
|
201
|
+
// asisCursor += len;
|
|
202
|
+
// tobeCursor += len;
|
|
203
|
+
// }
|
|
204
|
+
// }
|
|
205
|
+
// 에디터 상태를 업데이트하고 데코레이션을 적용하는 복잡한 로직이 필요합니다.
|
|
206
|
+
// 이 예시에서는 생략하지만, 실제 구현 시 핵심이 됩니다.
|
|
122
207
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.#
|
|
208
|
+
// 현재는 단순히 텍스트만 업데이트
|
|
209
|
+
this.#asisEditorView.dispatch({
|
|
210
|
+
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: asisSrc }
|
|
211
|
+
});
|
|
212
|
+
this.#tobeEditorView.dispatch({
|
|
213
|
+
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: tobeSrc }
|
|
214
|
+
});
|
|
128
215
|
};
|
|
129
216
|
|
|
217
|
+
initialize = (src1, src2, language = 'javascript') => {
|
|
218
|
+
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
219
|
+
console.warn('CodeMirror Editors not initialized yet.');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
130
222
|
|
|
131
|
-
|
|
132
|
-
|
|
223
|
+
// 언어 변경 (선택 사항, 필요 시 활성화)
|
|
224
|
+
let langExtension;
|
|
225
|
+
switch(language) {
|
|
226
|
+
case 'javascript':
|
|
227
|
+
langExtension = javascript();
|
|
228
|
+
break;
|
|
229
|
+
// case 'html':
|
|
230
|
+
// langExtension = html(); // @codemirror/lang-html 임포트 필요
|
|
231
|
+
// break;
|
|
232
|
+
// case 'css':
|
|
233
|
+
// langExtension = css(); // @codemirror/lang-css 임포트 필요
|
|
234
|
+
// break;
|
|
235
|
+
default:
|
|
236
|
+
langExtension = javascript(); // 기본값
|
|
237
|
+
}
|
|
133
238
|
|
|
134
|
-
|
|
135
|
-
this.#
|
|
239
|
+
// 언어 확장 업데이트
|
|
240
|
+
this.#asisEditorView.dispatch({
|
|
241
|
+
effects: this.#languageCompartment.reconfigure(langExtension)
|
|
242
|
+
});
|
|
243
|
+
this.#tobeEditorView.dispatch({
|
|
244
|
+
effects: this.#languageCompartment.reconfigure(langExtension)
|
|
245
|
+
});
|
|
136
246
|
|
|
137
|
-
this.#renderDiff();
|
|
247
|
+
this.#renderDiff(src1, src2);
|
|
138
248
|
};
|
|
139
|
-
}
|
|
140
249
|
|
|
250
|
+
disconnectedCallback() {
|
|
251
|
+
// 컴포넌트가 DOM에서 제거될 때 에디터 인스턴스 정리
|
|
252
|
+
if (this.#asisEditorView) {
|
|
253
|
+
this.#asisEditorView.destroy();
|
|
254
|
+
}
|
|
255
|
+
if (this.#tobeEditorView) {
|
|
256
|
+
this.#tobeEditorView.destroy();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
141
260
|
customElements.define("ide-diff", IdeDiff);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import ninegrid from "ninegrid2";
|
|
2
|
+
import {diff_match_patch} from 'diff-match-patch';
|
|
3
|
+
|
|
4
|
+
export class IdeDiff extends HTMLElement
|
|
5
|
+
{
|
|
6
|
+
#asisSrc;
|
|
7
|
+
#tobeSrc;
|
|
8
|
+
#asisPre; // <pre> 요소 참조 저장
|
|
9
|
+
#tobePre; // <pre> 요소 참조 저장
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
this.attachShadow({ mode: 'open' });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
connectedCallback() {
|
|
17
|
+
this.shadowRoot.innerHTML = `
|
|
18
|
+
<style>
|
|
19
|
+
/* 전역 또는 컴포넌트 스코프에서 box-sizing: border-box; 적용 권장 */
|
|
20
|
+
* {
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
}
|
|
23
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ideDiff.css";
|
|
24
|
+
${ninegrid.getCustomPath(this,"ideDiff.css")}
|
|
25
|
+
|
|
26
|
+
/* 추가 CSS (필요 시) */
|
|
27
|
+
.wrapper {
|
|
28
|
+
display: flex; /* 스플리터와 함께 작동하도록 flex 컨테이너 */
|
|
29
|
+
height: 100%; /* 부모의 높이를 채우도록 설정 */
|
|
30
|
+
overflow: hidden; /* 내부 스크롤을 위해 overflow 처리 */
|
|
31
|
+
}
|
|
32
|
+
.panel {
|
|
33
|
+
flex: 1; /* 패널들이 남은 공간을 채우도록 */
|
|
34
|
+
overflow: auto; /* 각 패널 내부에서 스크롤 가능하게 */
|
|
35
|
+
position: relative; /* 자식 요소의 absolute 포지셔닝 기준 */
|
|
36
|
+
}
|
|
37
|
+
.panel pre {
|
|
38
|
+
margin: 0; /* pre 태그의 기본 마진 제거 */
|
|
39
|
+
padding: 10px; /* 코드 가독성을 위한 내부 패딩 */
|
|
40
|
+
white-space: pre-wrap; /* 긴 줄 자동 줄바꿈 */
|
|
41
|
+
word-break: break-all; /* 단어가 길어도 강제 줄바꿈 */
|
|
42
|
+
/* white-space: pre-wrap; 을 사용하면 가로 스크롤 없이 줄바꿈됩니다.
|
|
43
|
+
만약 가로 스크롤을 원하면 white-space: pre; 로 두고 overflow-x: auto; 를 panel에 추가 */
|
|
44
|
+
}
|
|
45
|
+
ins {
|
|
46
|
+
background-color: #d4edda; /* 삽입된 텍스트 배경색 (예시) */
|
|
47
|
+
text-decoration: none; /* 밑줄 제거 (일반적으로) */
|
|
48
|
+
}
|
|
49
|
+
del {
|
|
50
|
+
background-color: #f8d7da; /* 삭제된 텍스트 배경색 (예시) */
|
|
51
|
+
text-decoration: none; /* 취소선 제거 (일반적으로) */
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
54
|
+
|
|
55
|
+
<div class="wrapper">
|
|
56
|
+
<div class="panel asis"><pre></pre></div>
|
|
57
|
+
<nx-splitter></nx-splitter>
|
|
58
|
+
<div class="panel tobe"><pre></pre></div>
|
|
59
|
+
</div>
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
requestAnimationFrame(() => {
|
|
63
|
+
this.#init();
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
#init = () => {
|
|
68
|
+
// 패널 pre 요소에 대한 참조를 저장
|
|
69
|
+
this.#asisPre = this.shadowRoot.querySelector('.asis pre');
|
|
70
|
+
this.#tobePre = this.shadowRoot.querySelector('.tobe pre');
|
|
71
|
+
|
|
72
|
+
// 스크롤 동기화 이벤트 리스너 추가
|
|
73
|
+
this.#asisPre.addEventListener('scroll', this.#syncScroll);
|
|
74
|
+
this.#tobePre.addEventListener('scroll', this.#syncScroll);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
#syncScroll = (e) => {
|
|
78
|
+
// 스크롤 이벤트 발생 시 다른 패널의 스크롤 위치를 동기화
|
|
79
|
+
// `e.target`이 스크롤 이벤트를 발생시킨 요소
|
|
80
|
+
if (e.target === this.#asisPre) {
|
|
81
|
+
this.#tobePre.scrollTop = this.#asisPre.scrollTop;
|
|
82
|
+
} else if (e.target === this.#tobePre) {
|
|
83
|
+
this.#asisPre.scrollTop = this.#tobePre.scrollTop;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
#renderDiff = () => {
|
|
88
|
+
const dmp = new diff_match_patch();
|
|
89
|
+
// 공백 문자를 정규화하여 diff 정확도 향상 (선택 사항)
|
|
90
|
+
const cleanedAsisSrc = this.#asisSrc.replace(/\r\n/g, '\n');
|
|
91
|
+
const cleanedTobeSrc = this.#tobeSrc.replace(/\r\n/g, '\n');
|
|
92
|
+
|
|
93
|
+
const diffs = dmp.diff_main(cleanedAsisSrc, cleanedTobeSrc);
|
|
94
|
+
dmp.diff_cleanupSemantic(diffs);
|
|
95
|
+
|
|
96
|
+
const asisHtml = [];
|
|
97
|
+
const tobeHtml = [];
|
|
98
|
+
|
|
99
|
+
for (const [op, text] of diffs) {
|
|
100
|
+
// HTML 이스케이프 처리: <, >, & 등 특수문자 변환
|
|
101
|
+
const escapedText = text
|
|
102
|
+
.replace(/&/g, '&')
|
|
103
|
+
.replace(/</g, '<')
|
|
104
|
+
.replace(/>/g, '>');
|
|
105
|
+
|
|
106
|
+
switch (op) {
|
|
107
|
+
case diff_match_patch.DIFF_INSERT:
|
|
108
|
+
tobeHtml.push(`<ins>${escapedText}</ins>`);
|
|
109
|
+
// 삭제된 내용이 없으므로 asis에는 공백을 유지 (줄 맞춤 고려)
|
|
110
|
+
// 만약 줄 단위로 완벽하게 매칭하려면 여기에 다른 로직 필요
|
|
111
|
+
break;
|
|
112
|
+
case diff_match_patch.DIFF_DELETE:
|
|
113
|
+
asisHtml.push(`<del>${escapedText}</del>`);
|
|
114
|
+
// 추가된 내용이 없으므로 tobe에는 공백을 유지
|
|
115
|
+
break;
|
|
116
|
+
case diff_match_patch.DIFF_EQUAL:
|
|
117
|
+
asisHtml.push(`<span>${escapedText}</span>`);
|
|
118
|
+
tobeHtml.push(`<span>${escapedText}</span>`);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 중요한 부분: `innerHTML` 대신 `textContent`를 사용하면
|
|
124
|
+
// `diff-match-patch`의 출력에 따라 HTML 태그가 그대로 보이므로,
|
|
125
|
+
// 여기서는 `innerHTML`을 사용하되, 텍스트는 `escapedText`로 처리하여 XSS 방지.
|
|
126
|
+
this.#asisPre.innerHTML = asisHtml.join('');
|
|
127
|
+
this.#tobePre.innerHTML = tobeHtml.join('');
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
initialize = (src1, src2) => {
|
|
132
|
+
console.log("Initializing IdeDiff with sources:", src1, src2);
|
|
133
|
+
|
|
134
|
+
this.#asisSrc = src1;
|
|
135
|
+
this.#tobeSrc = src2;
|
|
136
|
+
|
|
137
|
+
this.#renderDiff();
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
customElements.define("ide-diff", IdeDiff);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ide-assi",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.333.0",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -24,15 +24,22 @@
|
|
|
24
24
|
"rollup": "^4.44.1"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
30
|
-
"@
|
|
27
|
+
"@codemirror/autocomplete": "^6.18.6",
|
|
28
|
+
"@codemirror/commands": "^6.8.1",
|
|
29
|
+
"@codemirror/lang-javascript": "^6.2.4",
|
|
30
|
+
"@codemirror/language": "^6.11.2",
|
|
31
|
+
"@codemirror/lint": "^6.8.5",
|
|
32
|
+
"@codemirror/search": "^6.5.11",
|
|
33
|
+
"@codemirror/state": "^6.5.2",
|
|
34
|
+
"@codemirror/view": "^6.38.1",
|
|
31
35
|
"diff-match-patch": "^1.0.5",
|
|
32
|
-
"exceljs": "^4.4.0",
|
|
33
36
|
"jquery": "^3.6.0",
|
|
34
37
|
"ninegrid2": "^6.650.0",
|
|
35
|
-
"
|
|
38
|
+
"exceljs": "^4.4.0",
|
|
39
|
+
"@langchain/core": "^0.1.x",
|
|
40
|
+
"@langchain/google-genai": "^0.0.x",
|
|
41
|
+
"@langchain/ollama": "^0.0.x",
|
|
42
|
+
"@langchain/openai": "^0.0.x"
|
|
36
43
|
},
|
|
37
44
|
"peerDependencies": {
|
|
38
45
|
"jquery": "^3.6.0"
|