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.
@@ -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.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
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
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
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
- //asisEffect // 계산된 데코레이션 효과를 포함
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
- //this.#languageCompartment.reconfigure(langExtension),
235119
- //tobeEffect // 계산된 데코레이션 효과를 포함
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
  }
@@ -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.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
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
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
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
- //asisEffect // 계산된 데코레이션 효과를 포함
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
- //this.#languageCompartment.reconfigure(langExtension),
235115
- //tobeEffect // 계산된 데코레이션 효과를 포함
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.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
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
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
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
- //asisEffect // 계산된 데코레이션 효과를 포함
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
- //this.#languageCompartment.reconfigure(langExtension),
368
- //tobeEffect // 계산된 데코레이션 효과를 포함
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ide-assi",
3
3
  "type": "module",
4
- "version": "0.355.0",
4
+ "version": "0.357.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -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.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
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
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
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
- //asisEffect // 계산된 데코레이션 효과를 포함
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
- //this.#languageCompartment.reconfigure(langExtension),
368
- //tobeEffect // 계산된 데코레이션 효과를 포함
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
  }