ide-assi 0.356.0 → 0.358.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,16 @@ 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 = () => {
234943
- let scrollingA = false;
234944
- let scrollingB = false;
234945
-
234946
- this.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
234947
- if (!scrollingB) {
234948
- scrollingA = true;
234949
- this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
234950
- this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
234951
- }
234952
- scrollingB = false;
234953
- });
234954
-
234955
- this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
234956
- if (!scrollingA) {
234957
- scrollingB = true;
234958
- this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
234959
- this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
234960
- }
234961
- scrollingA = false;
234962
- });
234950
+ return;
234963
234951
  };
234964
234952
 
234965
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
234953
+ // #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
234966
234954
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
234967
234955
  const dmp = new diffMatchPatchExports.diff_match_patch();
234968
234956
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -234976,8 +234964,6 @@ class IdeDiff extends HTMLElement {
234976
234964
  let asisCursor = 0;
234977
234965
  let tobeCursor = 0;
234978
234966
 
234979
- // ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
234980
- // 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
234981
234967
  const asisDoc = Text.of(asisSrc.split('\n'));
234982
234968
  const tobeDoc = Text.of(tobeSrc.split('\n'));
234983
234969
 
@@ -234995,7 +234981,6 @@ class IdeDiff extends HTMLElement {
234995
234981
  let currentTobeLineOffset = tobeCursor;
234996
234982
  const tobeLines = text.split('\n');
234997
234983
  for (let i = 0; i < tobeLines.length; i++) {
234998
- // ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
234999
234984
  if (currentTobeLineOffset < tobeDoc.length) {
235000
234985
  const line = tobeDoc.lineAt(currentTobeLineOffset);
235001
234986
  tobeLineDecos.push({
@@ -235023,7 +235008,6 @@ class IdeDiff extends HTMLElement {
235023
235008
  let currentAsisLineOffset = asisCursor;
235024
235009
  const asisLines = text.split('\n');
235025
235010
  for (let i = 0; i < asisLines.length; i++) {
235026
- // ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
235027
235011
  if (currentAsisLineOffset < asisDoc.length) {
235028
235012
  const line = asisDoc.lineAt(currentAsisLineOffset);
235029
235013
  asisLineDecos.push({
@@ -235083,13 +235067,15 @@ class IdeDiff extends HTMLElement {
235083
235067
  };
235084
235068
  };
235085
235069
 
235086
- // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235087
235070
  initialize = (src1, src2, language = 'javascript') => {
235088
235071
  if (!this.#asisEditorView || !this.#tobeEditorView) {
235089
235072
  console.warn('CodeMirror Editors not initialized yet.');
235090
235073
  return;
235091
235074
  }
235092
235075
 
235076
+ // ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
235077
+ this.#isScrollSyncActive = false;
235078
+
235093
235079
  let langExtension;
235094
235080
  switch(language) {
235095
235081
  case 'javascript':
@@ -235102,13 +235088,12 @@ class IdeDiff extends HTMLElement {
235102
235088
  // 먼저 데코레이션 효과를 계산하여 가져옵니다.
235103
235089
  const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
235104
235090
 
235105
- console.log(asisEffect, tobeEffect);
235106
235091
  // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235107
235092
  this.#asisEditorView.dispatch({
235108
235093
  changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
235109
235094
  effects: [
235110
235095
  this.#languageCompartment.reconfigure(langExtension),
235111
- asisEffect // 계산된 데코레이션 효과를 포함
235096
+ asisEffect
235112
235097
  ]
235113
235098
  });
235114
235099
 
@@ -235116,13 +235101,29 @@ class IdeDiff extends HTMLElement {
235116
235101
  this.#tobeEditorView.dispatch({
235117
235102
  changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
235118
235103
  effects: [
235119
- //this.#languageCompartment.reconfigure(langExtension),
235120
- //tobeEffect // 계산된 데코레이션 효과를 포함
235104
+ this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
235105
+ tobeEffect // <-- 주석을 풀어주세요!
235121
235106
  ]
235122
235107
  });
235108
+
235109
+ // ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
235110
+ // requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
235111
+ requestAnimationFrame(() => {
235112
+ this.#isScrollSyncActive = true;
235113
+ // 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
235114
+ // this.#asisEditorView.scrollDOM.scrollTop = 0;
235115
+ // this.#tobeEditorView.scrollDOM.scrollTop = 0;
235116
+ });
235123
235117
  };
235124
235118
 
235125
235119
  disconnectedCallback() {
235120
+ // 컴포넌트 해제 시 스크롤 리스너 제거
235121
+ if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
235122
+ this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
235123
+ this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
235124
+ this._asisScrollHandler = null; // 참조 제거
235125
+ this._tobeScrollHandler = null; // 참조 제거
235126
+ }
235126
235127
  if (this.#asisEditorView) {
235127
235128
  this.#asisEditorView.destroy();
235128
235129
  }
@@ -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,16 @@ 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 = () => {
234939
- let scrollingA = false;
234940
- let scrollingB = false;
234941
-
234942
- this.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
234943
- if (!scrollingB) {
234944
- scrollingA = true;
234945
- this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
234946
- this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
234947
- }
234948
- scrollingB = false;
234949
- });
234950
-
234951
- this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
234952
- if (!scrollingA) {
234953
- scrollingB = true;
234954
- this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
234955
- this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
234956
- }
234957
- scrollingA = false;
234958
- });
234946
+ return;
234959
234947
  };
234960
234948
 
234961
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
234949
+ // #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
234962
234950
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
234963
234951
  const dmp = new diffMatchPatchExports.diff_match_patch();
234964
234952
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -234972,8 +234960,6 @@ class IdeDiff extends HTMLElement {
234972
234960
  let asisCursor = 0;
234973
234961
  let tobeCursor = 0;
234974
234962
 
234975
- // ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
234976
- // 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
234977
234963
  const asisDoc = Text.of(asisSrc.split('\n'));
234978
234964
  const tobeDoc = Text.of(tobeSrc.split('\n'));
234979
234965
 
@@ -234991,7 +234977,6 @@ class IdeDiff extends HTMLElement {
234991
234977
  let currentTobeLineOffset = tobeCursor;
234992
234978
  const tobeLines = text.split('\n');
234993
234979
  for (let i = 0; i < tobeLines.length; i++) {
234994
- // ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
234995
234980
  if (currentTobeLineOffset < tobeDoc.length) {
234996
234981
  const line = tobeDoc.lineAt(currentTobeLineOffset);
234997
234982
  tobeLineDecos.push({
@@ -235019,7 +235004,6 @@ class IdeDiff extends HTMLElement {
235019
235004
  let currentAsisLineOffset = asisCursor;
235020
235005
  const asisLines = text.split('\n');
235021
235006
  for (let i = 0; i < asisLines.length; i++) {
235022
- // ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
235023
235007
  if (currentAsisLineOffset < asisDoc.length) {
235024
235008
  const line = asisDoc.lineAt(currentAsisLineOffset);
235025
235009
  asisLineDecos.push({
@@ -235079,13 +235063,15 @@ class IdeDiff extends HTMLElement {
235079
235063
  };
235080
235064
  };
235081
235065
 
235082
- // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235083
235066
  initialize = (src1, src2, language = 'javascript') => {
235084
235067
  if (!this.#asisEditorView || !this.#tobeEditorView) {
235085
235068
  console.warn('CodeMirror Editors not initialized yet.');
235086
235069
  return;
235087
235070
  }
235088
235071
 
235072
+ // ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
235073
+ this.#isScrollSyncActive = false;
235074
+
235089
235075
  let langExtension;
235090
235076
  switch(language) {
235091
235077
  case 'javascript':
@@ -235098,13 +235084,12 @@ class IdeDiff extends HTMLElement {
235098
235084
  // 먼저 데코레이션 효과를 계산하여 가져옵니다.
235099
235085
  const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
235100
235086
 
235101
- console.log(asisEffect, tobeEffect);
235102
235087
  // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
235103
235088
  this.#asisEditorView.dispatch({
235104
235089
  changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
235105
235090
  effects: [
235106
235091
  this.#languageCompartment.reconfigure(langExtension),
235107
- asisEffect // 계산된 데코레이션 효과를 포함
235092
+ asisEffect
235108
235093
  ]
235109
235094
  });
235110
235095
 
@@ -235112,13 +235097,29 @@ class IdeDiff extends HTMLElement {
235112
235097
  this.#tobeEditorView.dispatch({
235113
235098
  changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
235114
235099
  effects: [
235115
- //this.#languageCompartment.reconfigure(langExtension),
235116
- //tobeEffect // 계산된 데코레이션 효과를 포함
235100
+ this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
235101
+ tobeEffect // <-- 주석을 풀어주세요!
235117
235102
  ]
235118
235103
  });
235104
+
235105
+ // ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
235106
+ // requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
235107
+ requestAnimationFrame(() => {
235108
+ this.#isScrollSyncActive = true;
235109
+ // 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
235110
+ // this.#asisEditorView.scrollDOM.scrollTop = 0;
235111
+ // this.#tobeEditorView.scrollDOM.scrollTop = 0;
235112
+ });
235119
235113
  };
235120
235114
 
235121
235115
  disconnectedCallback() {
235116
+ // 컴포넌트 해제 시 스크롤 리스너 제거
235117
+ if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
235118
+ this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
235119
+ this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
235120
+ this._asisScrollHandler = null; // 참조 제거
235121
+ this._tobeScrollHandler = null; // 참조 제거
235122
+ }
235122
235123
  if (this.#asisEditorView) {
235123
235124
  this.#asisEditorView.destroy();
235124
235125
  }
@@ -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,49 @@ 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
+ return;
199
+ // 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
200
+ if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
201
+ this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
202
+ this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
203
+ }
204
+
192
205
  let scrollingA = false;
193
206
  let scrollingB = false;
194
207
 
195
- this.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
208
+ this._asisScrollHandler = () => {
209
+ if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
210
+
196
211
  if (!scrollingB) {
197
212
  scrollingA = true;
198
213
  this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
199
214
  this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
200
215
  }
201
216
  scrollingB = false;
202
- });
217
+ };
218
+
219
+ this._tobeScrollHandler = () => {
220
+ if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
203
221
 
204
- this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
205
222
  if (!scrollingA) {
206
223
  scrollingB = true;
207
224
  this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
208
225
  this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
209
226
  }
210
227
  scrollingA = false;
211
- });
228
+ };
229
+
230
+ this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
231
+ this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
212
232
  };
213
233
 
214
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
234
+ // #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
215
235
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
216
236
  const dmp = new diff_match_patch();
217
237
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -225,8 +245,6 @@ export class IdeDiff extends HTMLElement {
225
245
  let asisCursor = 0;
226
246
  let tobeCursor = 0;
227
247
 
228
- // ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
229
- // 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
230
248
  const asisDoc = Text.of(asisSrc.split('\n'));
231
249
  const tobeDoc = Text.of(tobeSrc.split('\n'));
232
250
 
@@ -244,7 +262,6 @@ export class IdeDiff extends HTMLElement {
244
262
  let currentTobeLineOffset = tobeCursor;
245
263
  const tobeLines = text.split('\n');
246
264
  for (let i = 0; i < tobeLines.length; i++) {
247
- // ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
248
265
  if (currentTobeLineOffset < tobeDoc.length) {
249
266
  const line = tobeDoc.lineAt(currentTobeLineOffset);
250
267
  tobeLineDecos.push({
@@ -272,7 +289,6 @@ export class IdeDiff extends HTMLElement {
272
289
  let currentAsisLineOffset = asisCursor;
273
290
  const asisLines = text.split('\n');
274
291
  for (let i = 0; i < asisLines.length; i++) {
275
- // ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
276
292
  if (currentAsisLineOffset < asisDoc.length) {
277
293
  const line = asisDoc.lineAt(currentAsisLineOffset);
278
294
  asisLineDecos.push({
@@ -332,13 +348,15 @@ export class IdeDiff extends HTMLElement {
332
348
  };
333
349
  };
334
350
 
335
- // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
336
351
  initialize = (src1, src2, language = 'javascript') => {
337
352
  if (!this.#asisEditorView || !this.#tobeEditorView) {
338
353
  console.warn('CodeMirror Editors not initialized yet.');
339
354
  return;
340
355
  }
341
356
 
357
+ // ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
358
+ this.#isScrollSyncActive = false;
359
+
342
360
  let langExtension;
343
361
  switch(language) {
344
362
  case 'javascript':
@@ -351,13 +369,12 @@ export class IdeDiff extends HTMLElement {
351
369
  // 먼저 데코레이션 효과를 계산하여 가져옵니다.
352
370
  const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
353
371
 
354
- console.log(asisEffect, tobeEffect);
355
372
  // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
356
373
  this.#asisEditorView.dispatch({
357
374
  changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
358
375
  effects: [
359
376
  this.#languageCompartment.reconfigure(langExtension),
360
- asisEffect // 계산된 데코레이션 효과를 포함
377
+ asisEffect
361
378
  ]
362
379
  });
363
380
 
@@ -365,13 +382,29 @@ export class IdeDiff extends HTMLElement {
365
382
  this.#tobeEditorView.dispatch({
366
383
  changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
367
384
  effects: [
368
- //this.#languageCompartment.reconfigure(langExtension),
369
- //tobeEffect // 계산된 데코레이션 효과를 포함
385
+ this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
386
+ tobeEffect // <-- 주석을 풀어주세요!
370
387
  ]
371
388
  });
389
+
390
+ // ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
391
+ // requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
392
+ requestAnimationFrame(() => {
393
+ this.#isScrollSyncActive = true;
394
+ // 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
395
+ // this.#asisEditorView.scrollDOM.scrollTop = 0;
396
+ // this.#tobeEditorView.scrollDOM.scrollTop = 0;
397
+ });
372
398
  };
373
399
 
374
400
  disconnectedCallback() {
401
+ // 컴포넌트 해제 시 스크롤 리스너 제거
402
+ if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
403
+ this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
404
+ this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
405
+ this._asisScrollHandler = null; // 참조 제거
406
+ this._tobeScrollHandler = null; // 참조 제거
407
+ }
375
408
  if (this.#asisEditorView) {
376
409
  this.#asisEditorView.destroy();
377
410
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ide-assi",
3
3
  "type": "module",
4
- "version": "0.356.0",
4
+ "version": "0.358.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,49 @@ 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
+ return;
199
+ // 기존 리스너가 있다면 제거 (중복 방지 및 disconnectedCallback에서 제거 용이)
200
+ if (this._asisScrollHandler) { // 핸들러가 이미 할당되어 있다면
201
+ this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
202
+ this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
203
+ }
204
+
192
205
  let scrollingA = false;
193
206
  let scrollingB = false;
194
207
 
195
- this.#asisEditorView.scrollDOM.addEventListener('scroll', () => {
208
+ this._asisScrollHandler = () => {
209
+ if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
210
+
196
211
  if (!scrollingB) {
197
212
  scrollingA = true;
198
213
  this.#tobeEditorView.scrollDOM.scrollTop = this.#asisEditorView.scrollDOM.scrollTop;
199
214
  this.#tobeEditorView.scrollDOM.scrollLeft = this.#asisEditorView.scrollDOM.scrollLeft;
200
215
  }
201
216
  scrollingB = false;
202
- });
217
+ };
218
+
219
+ this._tobeScrollHandler = () => {
220
+ if (!this.#isScrollSyncActive) return; // 활성화되지 않았으면 스킵
203
221
 
204
- this.#tobeEditorView.scrollDOM.addEventListener('scroll', () => {
205
222
  if (!scrollingA) {
206
223
  scrollingB = true;
207
224
  this.#asisEditorView.scrollDOM.scrollTop = this.#tobeEditorView.scrollDOM.scrollTop;
208
225
  this.#asisEditorView.scrollDOM.scrollLeft = this.#tobeEditorView.scrollDOM.scrollLeft;
209
226
  }
210
227
  scrollingA = false;
211
- });
228
+ };
229
+
230
+ this.#asisEditorView.scrollDOM.addEventListener('scroll', this._asisScrollHandler);
231
+ this.#tobeEditorView.scrollDOM.addEventListener('scroll', this._tobeScrollHandler);
212
232
  };
213
233
 
214
- // ⭐️ #applyDiffDecorations 함수를 수정하여 Effect 객체를 반환하도록 변경
234
+ // #applyDiffDecorations 함수 (이전 수정본과 동일, Text.of 사용)
215
235
  #applyDiffDecorations = (asisSrc, tobeSrc) => {
216
236
  const dmp = new diff_match_patch();
217
237
  const diffs = dmp.diff_main(asisSrc, tobeSrc);
@@ -225,8 +245,6 @@ export class IdeDiff extends HTMLElement {
225
245
  let asisCursor = 0;
226
246
  let tobeCursor = 0;
227
247
 
228
- // ⭐️ 중요: 새로운 문서 내용을 기반으로 임시 Doc 객체를 생성합니다.
229
- // 이 Doc 객체를 사용하여 lineAt()을 호출합니다.
230
248
  const asisDoc = Text.of(asisSrc.split('\n'));
231
249
  const tobeDoc = Text.of(tobeSrc.split('\n'));
232
250
 
@@ -244,7 +262,6 @@ export class IdeDiff extends HTMLElement {
244
262
  let currentTobeLineOffset = tobeCursor;
245
263
  const tobeLines = text.split('\n');
246
264
  for (let i = 0; i < tobeLines.length; i++) {
247
- // ⭐️ this.#tobeEditorView.state.doc 대신 tobeDoc 사용
248
265
  if (currentTobeLineOffset < tobeDoc.length) {
249
266
  const line = tobeDoc.lineAt(currentTobeLineOffset);
250
267
  tobeLineDecos.push({
@@ -272,7 +289,6 @@ export class IdeDiff extends HTMLElement {
272
289
  let currentAsisLineOffset = asisCursor;
273
290
  const asisLines = text.split('\n');
274
291
  for (let i = 0; i < asisLines.length; i++) {
275
- // ⭐️ this.#asisEditorView.state.doc 대신 asisDoc 사용
276
292
  if (currentAsisLineOffset < asisDoc.length) {
277
293
  const line = asisDoc.lineAt(currentAsisLineOffset);
278
294
  asisLineDecos.push({
@@ -332,13 +348,15 @@ export class IdeDiff extends HTMLElement {
332
348
  };
333
349
  };
334
350
 
335
- // ⭐️ initialize 함수를 수정하여 텍스트 변경과 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
336
351
  initialize = (src1, src2, language = 'javascript') => {
337
352
  if (!this.#asisEditorView || !this.#tobeEditorView) {
338
353
  console.warn('CodeMirror Editors not initialized yet.');
339
354
  return;
340
355
  }
341
356
 
357
+ // ⭐️ initialize 시작 시 스크롤 동기화 일시 중지
358
+ this.#isScrollSyncActive = false;
359
+
342
360
  let langExtension;
343
361
  switch(language) {
344
362
  case 'javascript':
@@ -351,13 +369,12 @@ export class IdeDiff extends HTMLElement {
351
369
  // 먼저 데코레이션 효과를 계산하여 가져옵니다.
352
370
  const { asisEffect, tobeEffect } = this.#applyDiffDecorations(src1, src2);
353
371
 
354
- console.log(asisEffect, tobeEffect);
355
372
  // asis 에디터의 텍스트 변경 및 데코레이션 효과를 단일 트랜잭션으로 디스패치합니다.
356
373
  this.#asisEditorView.dispatch({
357
374
  changes: { from: 0, to: this.#asisEditorView.state.doc.length, insert: src1 },
358
375
  effects: [
359
376
  this.#languageCompartment.reconfigure(langExtension),
360
- asisEffect // 계산된 데코레이션 효과를 포함
377
+ asisEffect
361
378
  ]
362
379
  });
363
380
 
@@ -365,13 +382,29 @@ export class IdeDiff extends HTMLElement {
365
382
  this.#tobeEditorView.dispatch({
366
383
  changes: { from: 0, to: this.#tobeEditorView.state.doc.length, insert: src2 },
367
384
  effects: [
368
- //this.#languageCompartment.reconfigure(langExtension),
369
- //tobeEffect // 계산된 데코레이션 효과를 포함
385
+ this.#languageCompartment.reconfigure(langExtension), // <-- 이 주석을 풀어주세요!
386
+ tobeEffect // <-- 주석을 풀어주세요!
370
387
  ]
371
388
  });
389
+
390
+ // ⭐️ 모든 뷰 업데이트가 완료된 후 스크롤 동기화를 다시 활성화
391
+ // requestAnimationFrame을 사용하여 다음 브라우저 렌더링 사이클에서 실행
392
+ requestAnimationFrame(() => {
393
+ this.#isScrollSyncActive = true;
394
+ // 필요하다면 여기서 초기 스크롤 위치를 0으로 리셋하여 정렬을 보장할 수 있습니다.
395
+ // this.#asisEditorView.scrollDOM.scrollTop = 0;
396
+ // this.#tobeEditorView.scrollDOM.scrollTop = 0;
397
+ });
372
398
  };
373
399
 
374
400
  disconnectedCallback() {
401
+ // 컴포넌트 해제 시 스크롤 리스너 제거
402
+ if (this._asisScrollHandler) { // 핸들러가 존재하면 제거
403
+ this.#asisEditorView.scrollDOM.removeEventListener('scroll', this._asisScrollHandler);
404
+ this.#tobeEditorView.scrollDOM.removeEventListener('scroll', this._tobeScrollHandler);
405
+ this._asisScrollHandler = null; // 참조 제거
406
+ this._tobeScrollHandler = null; // 참조 제거
407
+ }
375
408
  if (this.#asisEditorView) {
376
409
  this.#asisEditorView.destroy();
377
410
  }