ide-assi 0.355.0 → 0.357.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 +48 -14
- package/dist/bundle.esm.js +48 -14
- package/dist/components/ideDiff.js +49 -16
- package/package.json +1 -1
- package/src/components/ideDiff.js +49 -16
package/dist/bundle.cjs.js
CHANGED
|
@@ -234805,7 +234805,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
234805
234805
|
});
|
|
234806
234806
|
|
|
234807
234807
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
234808
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
234809
234808
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
234810
234809
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
234811
234810
|
|
|
@@ -234818,6 +234817,12 @@ class IdeDiff extends HTMLElement {
|
|
|
234818
234817
|
|
|
234819
234818
|
#languageCompartment = new Compartment();
|
|
234820
234819
|
|
|
234820
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
234821
|
+
#isScrollSyncActive = false;
|
|
234822
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234823
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234824
|
+
|
|
234825
|
+
|
|
234821
234826
|
constructor() {
|
|
234822
234827
|
super();
|
|
234823
234828
|
this.attachShadow({ mode: 'open' });
|
|
@@ -234936,33 +234941,48 @@ class IdeDiff extends HTMLElement {
|
|
|
234936
234941
|
parent: this.#tobeEditorEl
|
|
234937
234942
|
});
|
|
234938
234943
|
|
|
234944
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
234939
234945
|
this.#setupScrollSync();
|
|
234940
234946
|
};
|
|
234941
234947
|
|
|
234948
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
234942
234949
|
#setupScrollSync = () => {
|
|
234950
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
234951
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
234952
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
234953
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
234954
|
+
}
|
|
234955
|
+
|
|
234943
234956
|
let scrollingA = false;
|
|
234944
234957
|
let scrollingB = false;
|
|
234945
234958
|
|
|
234946
|
-
this
|
|
234959
|
+
this._asisScrollHandler = () => {
|
|
234960
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234961
|
+
|
|
234947
234962
|
if (!scrollingB) {
|
|
234948
234963
|
scrollingA = true;
|
|
234949
234964
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
234950
234965
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
234951
234966
|
}
|
|
234952
234967
|
scrollingB = false;
|
|
234953
|
-
}
|
|
234968
|
+
};
|
|
234969
|
+
|
|
234970
|
+
this._tobeScrollHandler = () => {
|
|
234971
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234954
234972
|
|
|
234955
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234956
234973
|
if (!scrollingA) {
|
|
234957
234974
|
scrollingB = true;
|
|
234958
234975
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
234959
234976
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
234960
234977
|
}
|
|
234961
234978
|
scrollingA = false;
|
|
234962
|
-
}
|
|
234979
|
+
};
|
|
234980
|
+
|
|
234981
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
234982
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
234963
234983
|
};
|
|
234964
234984
|
|
|
234965
|
-
//
|
|
234985
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
234966
234986
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
234967
234987
|
const dmp = new diffMatchPatchExports.diff_match_patch();
|
|
234968
234988
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -234976,8 +234996,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234976
234996
|
let asisCursor = 0;
|
|
234977
234997
|
let tobeCursor = 0;
|
|
234978
234998
|
|
|
234979
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
234980
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
234981
234999
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
234982
235000
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
234983
235001
|
|
|
@@ -234995,7 +235013,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234995
235013
|
let currentTobeLineOffset = tobeCursor;
|
|
234996
235014
|
const tobeLines = text.split('\n');
|
|
234997
235015
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
234998
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
234999
235016
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
235000
235017
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
235001
235018
|
tobeLineDecos.push({
|
|
@@ -235023,7 +235040,6 @@ class IdeDiff extends HTMLElement {
|
|
|
235023
235040
|
let currentAsisLineOffset = asisCursor;
|
|
235024
235041
|
const asisLines = text.split('\n');
|
|
235025
235042
|
for (let i = 0; i < asisLines.length; i++) {
|
|
235026
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
235027
235043
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
235028
235044
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
235029
235045
|
asisLineDecos.push({
|
|
@@ -235083,13 +235099,15 @@ class IdeDiff extends HTMLElement {
|
|
|
235083
235099
|
};
|
|
235084
235100
|
};
|
|
235085
235101
|
|
|
235086
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235087
235102
|
initialize = (src1, src2, language = 'javascript') => {
|
|
235088
235103
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
235089
235104
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
235090
235105
|
return;
|
|
235091
235106
|
}
|
|
235092
235107
|
|
|
235108
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
235109
|
+
this.#isScrollSyncActive = false;
|
|
235110
|
+
|
|
235093
235111
|
let langExtension;
|
|
235094
235112
|
switch(language) {
|
|
235095
235113
|
case 'javascript':
|
|
@@ -235107,7 +235125,7 @@ class IdeDiff extends HTMLElement {
|
|
|
235107
235125
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
235108
235126
|
effects: [
|
|
235109
235127
|
this.#languageCompartment.reconfigure(langExtension),
|
|
235110
|
-
|
|
235128
|
+
asisEffect
|
|
235111
235129
|
]
|
|
235112
235130
|
});
|
|
235113
235131
|
|
|
@@ -235115,13 +235133,29 @@ class IdeDiff extends HTMLElement {
|
|
|
235115
235133
|
this.#tobeEditorView.dispatch({
|
|
235116
235134
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
235117
235135
|
effects: [
|
|
235118
|
-
|
|
235119
|
-
|
|
235136
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
235137
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
235120
235138
|
]
|
|
235121
235139
|
});
|
|
235140
|
+
|
|
235141
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
235142
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
235143
|
+
requestAnimationFrame(() => {
|
|
235144
|
+
this.#isScrollSyncActive = true;
|
|
235145
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
235146
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
235147
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
235148
|
+
});
|
|
235122
235149
|
};
|
|
235123
235150
|
|
|
235124
235151
|
disconnectedCallback() {
|
|
235152
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
235153
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
235154
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
235155
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
235156
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
235157
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
235158
|
+
}
|
|
235125
235159
|
if (this.#asisEditorView) {
|
|
235126
235160
|
this.#asisEditorView.destroy();
|
|
235127
235161
|
}
|
package/dist/bundle.esm.js
CHANGED
|
@@ -234801,7 +234801,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
234801
234801
|
});
|
|
234802
234802
|
|
|
234803
234803
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
234804
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
234805
234804
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
234806
234805
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
234807
234806
|
|
|
@@ -234814,6 +234813,12 @@ class IdeDiff extends HTMLElement {
|
|
|
234814
234813
|
|
|
234815
234814
|
#languageCompartment = new Compartment();
|
|
234816
234815
|
|
|
234816
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
234817
|
+
#isScrollSyncActive = false;
|
|
234818
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234819
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
234820
|
+
|
|
234821
|
+
|
|
234817
234822
|
constructor() {
|
|
234818
234823
|
super();
|
|
234819
234824
|
this.attachShadow({ mode: 'open' });
|
|
@@ -234932,33 +234937,48 @@ class IdeDiff extends HTMLElement {
|
|
|
234932
234937
|
parent: this.#tobeEditorEl
|
|
234933
234938
|
});
|
|
234934
234939
|
|
|
234940
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
234935
234941
|
this.#setupScrollSync();
|
|
234936
234942
|
};
|
|
234937
234943
|
|
|
234944
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
234938
234945
|
#setupScrollSync = () => {
|
|
234946
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
234947
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
234948
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
234949
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
234950
|
+
}
|
|
234951
|
+
|
|
234939
234952
|
let scrollingA = false;
|
|
234940
234953
|
let scrollingB = false;
|
|
234941
234954
|
|
|
234942
|
-
this
|
|
234955
|
+
this._asisScrollHandler = () => {
|
|
234956
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234957
|
+
|
|
234943
234958
|
if (!scrollingB) {
|
|
234944
234959
|
scrollingA = true;
|
|
234945
234960
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
234946
234961
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
234947
234962
|
}
|
|
234948
234963
|
scrollingB = false;
|
|
234949
|
-
}
|
|
234964
|
+
};
|
|
234965
|
+
|
|
234966
|
+
this._tobeScrollHandler = () => {
|
|
234967
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
234950
234968
|
|
|
234951
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
234952
234969
|
if (!scrollingA) {
|
|
234953
234970
|
scrollingB = true;
|
|
234954
234971
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
234955
234972
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
234956
234973
|
}
|
|
234957
234974
|
scrollingA = false;
|
|
234958
|
-
}
|
|
234975
|
+
};
|
|
234976
|
+
|
|
234977
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
234978
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
234959
234979
|
};
|
|
234960
234980
|
|
|
234961
|
-
//
|
|
234981
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
234962
234982
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
234963
234983
|
const dmp = new diffMatchPatchExports.diff_match_patch();
|
|
234964
234984
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -234972,8 +234992,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234972
234992
|
let asisCursor = 0;
|
|
234973
234993
|
let tobeCursor = 0;
|
|
234974
234994
|
|
|
234975
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
234976
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
234977
234995
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
234978
234996
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
234979
234997
|
|
|
@@ -234991,7 +235009,6 @@ class IdeDiff extends HTMLElement {
|
|
|
234991
235009
|
let currentTobeLineOffset = tobeCursor;
|
|
234992
235010
|
const tobeLines = text.split('\n');
|
|
234993
235011
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
234994
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
234995
235012
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
234996
235013
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
234997
235014
|
tobeLineDecos.push({
|
|
@@ -235019,7 +235036,6 @@ class IdeDiff extends HTMLElement {
|
|
|
235019
235036
|
let currentAsisLineOffset = asisCursor;
|
|
235020
235037
|
const asisLines = text.split('\n');
|
|
235021
235038
|
for (let i = 0; i < asisLines.length; i++) {
|
|
235022
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
235023
235039
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
235024
235040
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
235025
235041
|
asisLineDecos.push({
|
|
@@ -235079,13 +235095,15 @@ class IdeDiff extends HTMLElement {
|
|
|
235079
235095
|
};
|
|
235080
235096
|
};
|
|
235081
235097
|
|
|
235082
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
235083
235098
|
initialize = (src1, src2, language = 'javascript') => {
|
|
235084
235099
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
235085
235100
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
235086
235101
|
return;
|
|
235087
235102
|
}
|
|
235088
235103
|
|
|
235104
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
235105
|
+
this.#isScrollSyncActive = false;
|
|
235106
|
+
|
|
235089
235107
|
let langExtension;
|
|
235090
235108
|
switch(language) {
|
|
235091
235109
|
case 'javascript':
|
|
@@ -235103,7 +235121,7 @@ class IdeDiff extends HTMLElement {
|
|
|
235103
235121
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
235104
235122
|
effects: [
|
|
235105
235123
|
this.#languageCompartment.reconfigure(langExtension),
|
|
235106
|
-
|
|
235124
|
+
asisEffect
|
|
235107
235125
|
]
|
|
235108
235126
|
});
|
|
235109
235127
|
|
|
@@ -235111,13 +235129,29 @@ class IdeDiff extends HTMLElement {
|
|
|
235111
235129
|
this.#tobeEditorView.dispatch({
|
|
235112
235130
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
235113
235131
|
effects: [
|
|
235114
|
-
|
|
235115
|
-
|
|
235132
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
235133
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
235116
235134
|
]
|
|
235117
235135
|
});
|
|
235136
|
+
|
|
235137
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
235138
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
235139
|
+
requestAnimationFrame(() => {
|
|
235140
|
+
this.#isScrollSyncActive = true;
|
|
235141
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
235142
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
235143
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
235144
|
+
});
|
|
235118
235145
|
};
|
|
235119
235146
|
|
|
235120
235147
|
disconnectedCallback() {
|
|
235148
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
235149
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
235150
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
235151
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
235152
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
235153
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
235154
|
+
}
|
|
235121
235155
|
if (this.#asisEditorView) {
|
|
235122
235156
|
this.#asisEditorView.destroy();
|
|
235123
235157
|
}
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
16
16
|
import { defaultKeymap, selectAll } from "@codemirror/commands";
|
|
17
17
|
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
|
18
|
-
// 올바른 import 구문: '=>' 대신 'from'
|
|
19
18
|
import { bracketMatching } from "@codemirror/language";
|
|
20
19
|
import { javascript } from "@codemirror/lang-javascript";
|
|
21
20
|
import { indentOnInput, syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";
|
|
@@ -23,7 +22,7 @@ import { lintKeymap } from "@codemirror/lint";
|
|
|
23
22
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
24
23
|
|
|
25
24
|
// Diff 로직을 위해 diff-match-patch 사용
|
|
26
|
-
import { diff_match_patch } from 'diff-match-patch';
|
|
25
|
+
import { diff_match_patch } from 'diff-match-patch';
|
|
27
26
|
|
|
28
27
|
// Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
|
|
29
28
|
const asisDiffDecorations = StateField.define({
|
|
@@ -54,7 +53,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
57
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
58
56
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
59
57
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
60
58
|
|
|
@@ -67,6 +65,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
67
65
|
|
|
68
66
|
#languageCompartment = new Compartment();
|
|
69
67
|
|
|
68
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
69
|
+
#isScrollSyncActive = false;
|
|
70
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
71
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
72
|
+
|
|
73
|
+
|
|
70
74
|
constructor() {
|
|
71
75
|
super();
|
|
72
76
|
this.attachShadow({ mode: 'open' });
|
|
@@ -185,33 +189,48 @@ export class IdeDiff extends HTMLElement {
|
|
|
185
189
|
parent: this.#tobeEditorEl
|
|
186
190
|
});
|
|
187
191
|
|
|
192
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
188
193
|
this.#setupScrollSync();
|
|
189
194
|
};
|
|
190
195
|
|
|
196
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
191
197
|
#setupScrollSync = () => {
|
|
198
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
199
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
200
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
201
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
202
|
+
}
|
|
203
|
+
|
|
192
204
|
let scrollingA = false;
|
|
193
205
|
let scrollingB = false;
|
|
194
206
|
|
|
195
|
-
this
|
|
207
|
+
this._asisScrollHandler = () => {
|
|
208
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
209
|
+
|
|
196
210
|
if (!scrollingB) {
|
|
197
211
|
scrollingA = true;
|
|
198
212
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
199
213
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
200
214
|
}
|
|
201
215
|
scrollingB = false;
|
|
202
|
-
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
this._tobeScrollHandler = () => {
|
|
219
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
203
220
|
|
|
204
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
205
221
|
if (!scrollingA) {
|
|
206
222
|
scrollingB = true;
|
|
207
223
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
208
224
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
209
225
|
}
|
|
210
226
|
scrollingA = false;
|
|
211
|
-
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
230
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
212
231
|
};
|
|
213
232
|
|
|
214
|
-
//
|
|
233
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
215
234
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
216
235
|
const dmp = new diff_match_patch();
|
|
217
236
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -225,8 +244,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
225
244
|
let asisCursor = 0;
|
|
226
245
|
let tobeCursor = 0;
|
|
227
246
|
|
|
228
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
229
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
230
247
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
231
248
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
232
249
|
|
|
@@ -244,7 +261,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
244
261
|
let currentTobeLineOffset = tobeCursor;
|
|
245
262
|
const tobeLines = text.split('\n');
|
|
246
263
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
247
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
248
264
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
249
265
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
250
266
|
tobeLineDecos.push({
|
|
@@ -272,7 +288,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
272
288
|
let currentAsisLineOffset = asisCursor;
|
|
273
289
|
const asisLines = text.split('\n');
|
|
274
290
|
for (let i = 0; i < asisLines.length; i++) {
|
|
275
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
276
291
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
277
292
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
278
293
|
asisLineDecos.push({
|
|
@@ -332,13 +347,15 @@ export class IdeDiff extends HTMLElement {
|
|
|
332
347
|
};
|
|
333
348
|
};
|
|
334
349
|
|
|
335
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
336
350
|
initialize = (src1, src2, language = 'javascript') => {
|
|
337
351
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
338
352
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
339
353
|
return;
|
|
340
354
|
}
|
|
341
355
|
|
|
356
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
357
|
+
this.#isScrollSyncActive = false;
|
|
358
|
+
|
|
342
359
|
let langExtension;
|
|
343
360
|
switch(language) {
|
|
344
361
|
case 'javascript':
|
|
@@ -356,7 +373,7 @@ export class IdeDiff extends HTMLElement {
|
|
|
356
373
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
357
374
|
effects: [
|
|
358
375
|
this.#languageCompartment.reconfigure(langExtension),
|
|
359
|
-
|
|
376
|
+
asisEffect
|
|
360
377
|
]
|
|
361
378
|
});
|
|
362
379
|
|
|
@@ -364,13 +381,29 @@ export class IdeDiff extends HTMLElement {
|
|
|
364
381
|
this.#tobeEditorView.dispatch({
|
|
365
382
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
366
383
|
effects: [
|
|
367
|
-
|
|
368
|
-
|
|
384
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
385
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
369
386
|
]
|
|
370
387
|
});
|
|
388
|
+
|
|
389
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
390
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
391
|
+
requestAnimationFrame(() => {
|
|
392
|
+
this.#isScrollSyncActive = true;
|
|
393
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
394
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
395
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
396
|
+
});
|
|
371
397
|
};
|
|
372
398
|
|
|
373
399
|
disconnectedCallback() {
|
|
400
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
401
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
402
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
403
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
404
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
405
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
406
|
+
}
|
|
374
407
|
if (this.#asisEditorView) {
|
|
375
408
|
this.#asisEditorView.destroy();
|
|
376
409
|
}
|
package/package.json
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { history, historyKeymap, indentWithTab } from "@codemirror/commands";
|
|
16
16
|
import { defaultKeymap, selectAll } from "@codemirror/commands";
|
|
17
17
|
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
|
18
|
-
// 올바른 import 구문: '=>' 대신 'from'
|
|
19
18
|
import { bracketMatching } from "@codemirror/language";
|
|
20
19
|
import { javascript } from "@codemirror/lang-javascript";
|
|
21
20
|
import { indentOnInput, syntaxHighlighting, defaultHighlightStyle } from "@codemirror/language";
|
|
@@ -23,7 +22,7 @@ import { lintKeymap } from "@codemirror/lint";
|
|
|
23
22
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
24
23
|
|
|
25
24
|
// Diff 로직을 위해 diff-match-patch 사용
|
|
26
|
-
import { diff_match_patch } from 'diff-match-patch';
|
|
25
|
+
import { diff_match_patch } from 'diff-match-patch';
|
|
27
26
|
|
|
28
27
|
// Diff 데코레이션을 위한 StateField 정의 (asis 에디터용)
|
|
29
28
|
const asisDiffDecorations = StateField.define({
|
|
@@ -54,7 +53,6 @@ const tobeDiffDecorations = StateField.define({
|
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
// 데코레이션 업데이트를 위한 Effect (트랜잭션에 포함시켜 상태 변경 알림)
|
|
57
|
-
// StateEffect.define()을 사용하여 이펙트를 정의합니다.
|
|
58
56
|
const setAsisDecorationsEffect = StateEffect.define();
|
|
59
57
|
const setTobeDecorationsEffect = StateEffect.define();
|
|
60
58
|
|
|
@@ -67,6 +65,12 @@ export class IdeDiff extends HTMLElement {
|
|
|
67
65
|
|
|
68
66
|
#languageCompartment = new Compartment();
|
|
69
67
|
|
|
68
|
+
// ⭐️ 스크롤 동기화 활성/비활성화 플래그 및 핸들러 참조
|
|
69
|
+
#isScrollSyncActive = false;
|
|
70
|
+
_asisScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
71
|
+
_tobeScrollHandler = null; // 클래스 속성으로 선언하여 removeEventListener에서 참조 가능
|
|
72
|
+
|
|
73
|
+
|
|
70
74
|
constructor() {
|
|
71
75
|
super();
|
|
72
76
|
this.attachShadow({ mode: 'open' });
|
|
@@ -185,33 +189,48 @@ export class IdeDiff extends HTMLElement {
|
|
|
185
189
|
parent: this.#tobeEditorEl
|
|
186
190
|
});
|
|
187
191
|
|
|
192
|
+
// ⭐️ 초기 스크롤 동기화는 여기서 설정만 하고, initialize에서 활성화 제어
|
|
188
193
|
this.#setupScrollSync();
|
|
189
194
|
};
|
|
190
195
|
|
|
196
|
+
// ⭐️ 스크롤 리스너를 제어할 수 있도록 플래그 및 클래스 메서드 사용
|
|
191
197
|
#setupScrollSync = () => {
|
|
198
|
+
// 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
|
|
199
|
+
if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
|
|
200
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
201
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
202
|
+
}
|
|
203
|
+
|
|
192
204
|
let scrollingA = false;
|
|
193
205
|
let scrollingB = false;
|
|
194
206
|
|
|
195
|
-
this
|
|
207
|
+
this._asisScrollHandler = () => {
|
|
208
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
209
|
+
|
|
196
210
|
if (!scrollingB) {
|
|
197
211
|
scrollingA = true;
|
|
198
212
|
this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
|
|
199
213
|
this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
|
|
200
214
|
}
|
|
201
215
|
scrollingB = false;
|
|
202
|
-
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
this._tobeScrollHandler = () => {
|
|
219
|
+
if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
|
|
203
220
|
|
|
204
|
-
this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
|
|
205
221
|
if (!scrollingA) {
|
|
206
222
|
scrollingB = true;
|
|
207
223
|
this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
|
|
208
224
|
this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
|
|
209
225
|
}
|
|
210
226
|
scrollingA = false;
|
|
211
|
-
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
|
|
230
|
+
this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
|
|
212
231
|
};
|
|
213
232
|
|
|
214
|
-
//
|
|
233
|
+
// #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
|
|
215
234
|
#applyDiffDecorations = (asisSrc, tobeSrc) => {
|
|
216
235
|
const dmp = new diff_match_patch();
|
|
217
236
|
const diffs = dmp.diff_main(asisSrc, tobeSrc);
|
|
@@ -225,8 +244,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
225
244
|
let asisCursor = 0;
|
|
226
245
|
let tobeCursor = 0;
|
|
227
246
|
|
|
228
|
-
// ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
|
|
229
|
-
// 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
|
|
230
247
|
const asisDoc = Text.of(asisSrc.split('\n'));
|
|
231
248
|
const tobeDoc = Text.of(tobeSrc.split('\n'));
|
|
232
249
|
|
|
@@ -244,7 +261,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
244
261
|
let currentTobeLineOffset = tobeCursor;
|
|
245
262
|
const tobeLines = text.split('\n');
|
|
246
263
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
247
|
-
// ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
|
|
248
264
|
if (currentTobeLineOffset < tobeDoc.length) {
|
|
249
265
|
const line = tobeDoc.lineAt(currentTobeLineOffset);
|
|
250
266
|
tobeLineDecos.push({
|
|
@@ -272,7 +288,6 @@ export class IdeDiff extends HTMLElement {
|
|
|
272
288
|
let currentAsisLineOffset = asisCursor;
|
|
273
289
|
const asisLines = text.split('\n');
|
|
274
290
|
for (let i = 0; i < asisLines.length; i++) {
|
|
275
|
-
// ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
|
|
276
291
|
if (currentAsisLineOffset < asisDoc.length) {
|
|
277
292
|
const line = asisDoc.lineAt(currentAsisLineOffset);
|
|
278
293
|
asisLineDecos.push({
|
|
@@ -332,13 +347,15 @@ export class IdeDiff extends HTMLElement {
|
|
|
332
347
|
};
|
|
333
348
|
};
|
|
334
349
|
|
|
335
|
-
// ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
|
|
336
350
|
initialize = (src1, src2, language = 'javascript') => {
|
|
337
351
|
if (!this.#asisEditorView || !this.#tobeEditorView) {
|
|
338
352
|
console.warn('CodeMirror Editors not initialized yet.');
|
|
339
353
|
return;
|
|
340
354
|
}
|
|
341
355
|
|
|
356
|
+
// ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
|
|
357
|
+
this.#isScrollSyncActive = false;
|
|
358
|
+
|
|
342
359
|
let langExtension;
|
|
343
360
|
switch(language) {
|
|
344
361
|
case 'javascript':
|
|
@@ -356,7 +373,7 @@ export class IdeDiff extends HTMLElement {
|
|
|
356
373
|
changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
|
|
357
374
|
effects: [
|
|
358
375
|
this.#languageCompartment.reconfigure(langExtension),
|
|
359
|
-
|
|
376
|
+
asisEffect
|
|
360
377
|
]
|
|
361
378
|
});
|
|
362
379
|
|
|
@@ -364,13 +381,29 @@ export class IdeDiff extends HTMLElement {
|
|
|
364
381
|
this.#tobeEditorView.dispatch({
|
|
365
382
|
changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
|
|
366
383
|
effects: [
|
|
367
|
-
|
|
368
|
-
|
|
384
|
+
this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
|
|
385
|
+
tobeEffect // <-- 이 주석을 풀어주세요!
|
|
369
386
|
]
|
|
370
387
|
});
|
|
388
|
+
|
|
389
|
+
// ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
|
|
390
|
+
// requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
|
|
391
|
+
requestAnimationFrame(() => {
|
|
392
|
+
this.#isScrollSyncActive = true;
|
|
393
|
+
// 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
|
|
394
|
+
// this.#asisEditorView.scrollDOM.scrollTop = 0;
|
|
395
|
+
// this.#tobeEditorView.scrollDOM.scrollTop = 0;
|
|
396
|
+
});
|
|
371
397
|
};
|
|
372
398
|
|
|
373
399
|
disconnectedCallback() {
|
|
400
|
+
// 컴포넌트 해제 시 스크롤 리스너 제거
|
|
401
|
+
if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
|
|
402
|
+
this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
|
|
403
|
+
this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
|
|
404
|
+
this._asisScrollHandler = null; // 참조 제거
|
|
405
|
+
this._tobeScrollHandler = null; // 참조 제거
|
|
406
|
+
}
|
|
374
407
|
if (this.#asisEditorView) {
|
|
375
408
|
this.#asisEditorView.destroy();
|
|
376
409
|
}
|