ide-assi 0.441.0 → 0.442.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 +55 -47
- package/dist/bundle.esm.js +55 -47
- package/dist/components/ideDiff.js +55 -47
- package/package.json +1 -1
- package/src/components/ideDiff.js +55 -47
package/dist/bundle.cjs.js
CHANGED
|
@@ -234903,22 +234903,34 @@ const tobeButtonDecorations = StateField.define({
|
|
|
234903
234903
|
class MergeButtonWidget extends WidgetType {
|
|
234904
234904
|
constructor(isAsisButton, textToApply, targetEditorView, diffRange, hostComponent) {
|
|
234905
234905
|
super();
|
|
234906
|
-
this.isAsisButton = isAsisButton;
|
|
234907
|
-
this.textToApply = textToApply;
|
|
234908
|
-
this.targetEditorView = targetEditorView;
|
|
234909
|
-
this.diffRange = diffRange;
|
|
234910
|
-
this.hostComponent = hostComponent;
|
|
234906
|
+
this.isAsisButton = isAsisButton; // 이 버튼이 ASIS 에디터에 붙는 버튼인가 (true) TOBE 에디터에 붙는 버튼인가 (false)
|
|
234907
|
+
this.textToApply = textToApply; // 적용할 텍스트
|
|
234908
|
+
this.targetEditorView = targetEditorView; // 텍스트를 적용할 에디터 뷰 (상대편 에디터)
|
|
234909
|
+
this.diffRange = diffRange; // 대상 에디터에서 변경이 일어날 정확한 from/to 오프셋
|
|
234910
|
+
this.hostComponent = hostComponent; // IdeDiff 인스턴스 참조
|
|
234911
234911
|
}
|
|
234912
234912
|
|
|
234913
|
+
// 위젯이 차지할 공간을 텍스트 줄에 할당할지 여부. false로 설정하여 버튼이 줄 사이에 끼어들지 않도록 함.
|
|
234913
234914
|
eq(other) { return false; }
|
|
234914
234915
|
|
|
234916
|
+
// 위젯의 DOM 요소를 생성합니다.
|
|
234915
234917
|
toDOM(view) {
|
|
234916
234918
|
const button = document.createElement("button");
|
|
234917
|
-
|
|
234918
|
-
|
|
234919
|
+
// SVN 기준 명칭으로 변경
|
|
234920
|
+
if (this.isAsisButton) {
|
|
234921
|
+
// ASIS (왼쪽) 에디터에 붙는 버튼: AI가 삭제한 것을 '되돌리기'
|
|
234922
|
+
button.className = "cm-merge-button revert"; // 'revert' 클래스 사용
|
|
234923
|
+
button.textContent = "← 되돌리기";
|
|
234924
|
+
} else {
|
|
234925
|
+
// TOBE (오른쪽) 에디터에 붙는 버튼: AI가 추가한 것을 '적용'
|
|
234926
|
+
button.className = "cm-merge-button accept"; // 'accept' 클래스 사용
|
|
234927
|
+
button.textContent = "→ 적용";
|
|
234928
|
+
}
|
|
234929
|
+
|
|
234919
234930
|
|
|
234931
|
+
// 클릭 이벤트 핸들러
|
|
234920
234932
|
button.addEventListener("click", () => {
|
|
234921
|
-
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS
|
|
234933
|
+
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS 쪽 (되돌리기)' : 'TOBE 쪽 (적용)'}`, this.textToApply);
|
|
234922
234934
|
console.log("대상 에디터:", this.targetEditorView === this.hostComponent.asisEditorView ? "ASIS" : "TOBE");
|
|
234923
234935
|
console.log("대상 범위:", this.diffRange);
|
|
234924
234936
|
|
|
@@ -234931,22 +234943,13 @@ class MergeButtonWidget extends WidgetType {
|
|
|
234931
234943
|
return container;
|
|
234932
234944
|
}
|
|
234933
234945
|
|
|
234946
|
+
// 실제 변경 적용 로직
|
|
234934
234947
|
applyChanges(text, editorView, range) {
|
|
234935
234948
|
if (!editorView || !range) {
|
|
234936
234949
|
console.error("Target editor view or range is undefined.", editorView, range);
|
|
234937
234950
|
return;
|
|
234938
234951
|
}
|
|
234939
234952
|
|
|
234940
|
-
// 특정 줄의 시작 오프셋과 끝 오프셋을 정확히 계산하여 적용해야 합니다.
|
|
234941
|
-
// 현재 로직은 단순 치환이므로 Diff 유형에 따라 복잡해질 수 있습니다.
|
|
234942
|
-
// 예를 들어, TOBE에 삽입된 내용을 ASIS에서 되돌릴 경우, ASIS에서 해당 내용 길이만큼의 공백을 지워야 합니다.
|
|
234943
|
-
// ASIS에서 삭제된 내용을 TOBE에 적용할 경우, TOBE의 해당 위치에 내용을 삽입해야 합니다.
|
|
234944
|
-
|
|
234945
|
-
// CodeMirror의 Doc에서 특정 범위를 가져오는 것은 `state.sliceDoc(from, to)`
|
|
234946
|
-
// `dmp.diff_main`의 결과는 줄 단위로 매핑된 문자열이므로,
|
|
234947
|
-
// 실제 오프셋 계산 및 적용 로직은 더 정교해야 합니다.
|
|
234948
|
-
// 지금은 임시로 range.from, range.to를 사용합니다.
|
|
234949
|
-
|
|
234950
234953
|
editorView.dispatch({
|
|
234951
234954
|
changes: {
|
|
234952
234955
|
from: range.from,
|
|
@@ -234974,9 +234977,14 @@ class IdeDiff extends HTMLElement {
|
|
|
234974
234977
|
_asisScrollHandler = null;
|
|
234975
234978
|
_tobeScrollHandler = null;
|
|
234976
234979
|
|
|
234980
|
+
// MergeButtonWidget에서 접근할 수 있도록 Getter를 공개합니다.
|
|
234977
234981
|
get asisEditorView() {
|
|
234978
234982
|
return this.#asisEditorView;
|
|
234979
|
-
}
|
|
234983
|
+
}
|
|
234984
|
+
get tobeEditorView() {
|
|
234985
|
+
return this.#tobeEditorView;
|
|
234986
|
+
}
|
|
234987
|
+
|
|
234980
234988
|
|
|
234981
234989
|
constructor() {
|
|
234982
234990
|
super();
|
|
@@ -234986,7 +234994,9 @@ class IdeDiff extends HTMLElement {
|
|
|
234986
234994
|
connectedCallback() {
|
|
234987
234995
|
this.shadowRoot.innerHTML = `
|
|
234988
234996
|
<style>
|
|
234997
|
+
/* ninegrid CSS 및 필요한 기본 스타일 임포트 */
|
|
234989
234998
|
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ideDiff.css";
|
|
234999
|
+
/* 새로 만든 ideDiff.css 파일 임포트 */
|
|
234990
235000
|
${ninegrid.getCustomPath(this, "ideDiff.css")}
|
|
234991
235001
|
</style>
|
|
234992
235002
|
|
|
@@ -235057,9 +235067,9 @@ class IdeDiff extends HTMLElement {
|
|
|
235057
235067
|
extensions: [
|
|
235058
235068
|
basicExtensions,
|
|
235059
235069
|
this.#languageCompartment.of(javascript()),
|
|
235060
|
-
EditorState.readOnly.of(true),
|
|
235070
|
+
EditorState.readOnly.of(true), // ASIS는 읽기 전용 유지 (원본 소스)
|
|
235061
235071
|
asisDiffDecorations,
|
|
235062
|
-
asisButtonDecorations,
|
|
235072
|
+
asisButtonDecorations,
|
|
235063
235073
|
EditorView.updateListener.of((update) => {
|
|
235064
235074
|
if (update.view.contentDOM.firstChild && !update.view._initialAsisContentLoaded) {
|
|
235065
235075
|
update.view._initialAsisContentLoaded = true;
|
|
@@ -235077,8 +235087,9 @@ class IdeDiff extends HTMLElement {
|
|
|
235077
235087
|
extensions: [
|
|
235078
235088
|
basicExtensions,
|
|
235079
235089
|
this.#languageCompartment.of(javascript()),
|
|
235090
|
+
// EditorState.readOnly.of(true), // TOBE는 AI 추천 소스이므로 편집 가능 (혹은 ASIS로만 적용)
|
|
235080
235091
|
tobeDiffDecorations,
|
|
235081
|
-
tobeButtonDecorations,
|
|
235092
|
+
tobeButtonDecorations,
|
|
235082
235093
|
EditorView.updateListener.of((update) => {
|
|
235083
235094
|
if (update.view.contentDOM.firstChild && !update.view._initialTobeContentLoaded) {
|
|
235084
235095
|
update.view._initialTobeContentLoaded = true;
|
|
@@ -235144,8 +235155,8 @@ class IdeDiff extends HTMLElement {
|
|
|
235144
235155
|
|
|
235145
235156
|
const asisLineBuilder = new RangeSetBuilder();
|
|
235146
235157
|
const tobeLineBuilder = new RangeSetBuilder();
|
|
235147
|
-
const asisButtonBuilder = new RangeSetBuilder();
|
|
235148
|
-
const tobeButtonBuilder = new RangeSetBuilder();
|
|
235158
|
+
const asisButtonBuilder = new RangeSetBuilder();
|
|
235159
|
+
const tobeButtonBuilder = new RangeSetBuilder();
|
|
235149
235160
|
|
|
235150
235161
|
let asisCursor = 0;
|
|
235151
235162
|
let tobeCursor = 0;
|
|
@@ -235162,7 +235173,7 @@ class IdeDiff extends HTMLElement {
|
|
|
235162
235173
|
const tobeRangeStart = tobeCursor;
|
|
235163
235174
|
|
|
235164
235175
|
switch (op) {
|
|
235165
|
-
case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // TOBE에 추가된 내용 (ASIS에는 없음)
|
|
235176
|
+
case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // TOBE (AI 추천)에 추가된 내용 (ASIS에는 없음)
|
|
235166
235177
|
console.log("DIFF_INSERT (TOBE added):", JSON.stringify(text));
|
|
235167
235178
|
const tobeLines = text.split('\n');
|
|
235168
235179
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
@@ -235172,27 +235183,24 @@ class IdeDiff extends HTMLElement {
|
|
|
235172
235183
|
tobeCursor += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
|
|
235173
235184
|
}
|
|
235174
235185
|
|
|
235175
|
-
// ⭐️ TOBE 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS
|
|
235176
|
-
// TOBE의 해당 Diff가 끝나는 지점에 버튼을 붙이는 것이 자연스럽습니다.
|
|
235177
|
-
// tobeRangeStart는 Diff 청크의 시작 오프셋입니다.
|
|
235178
|
-
// 버튼을 Line Decoration과 같은 위치에 추가하되, 별도의 builder 사용
|
|
235186
|
+
// ⭐️ TOBE (AI 추천) 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS (현재 소스)에 '적용'
|
|
235179
235187
|
tobeButtonBuilder.add(
|
|
235180
|
-
tobeRangeStart,
|
|
235188
|
+
tobeRangeStart,
|
|
235181
235189
|
tobeRangeStart,
|
|
235182
235190
|
Decoration.widget({
|
|
235183
235191
|
widget: new MergeButtonWidget(
|
|
235184
|
-
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
235185
|
-
|
|
235186
|
-
currentInstance.#asisEditorView, // 대상 에디터는 ASIS
|
|
235187
|
-
{ from: asisRangeStart, to: asisRangeStart +
|
|
235192
|
+
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
235193
|
+
text, // AI가 추가한 내용을 ASIS에 '삽입'
|
|
235194
|
+
currentInstance.#asisEditorView, // 대상 에디터는 ASIS (현재 소스)
|
|
235195
|
+
{ from: asisRangeStart, to: asisRangeStart + 0 }, // ASIS에서의 '삽입될' 위치
|
|
235188
235196
|
currentInstance
|
|
235189
235197
|
),
|
|
235190
|
-
side: 1
|
|
235198
|
+
side: 1
|
|
235191
235199
|
})
|
|
235192
235200
|
);
|
|
235193
235201
|
break;
|
|
235194
235202
|
|
|
235195
|
-
case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // ASIS에서 삭제된 내용 (TOBE에는 없음)
|
|
235203
|
+
case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // ASIS (현재 소스)에서 삭제된 내용 (TOBE에는 없음)
|
|
235196
235204
|
console.log("DIFF_DELETE (ASIS deleted):", JSON.stringify(text));
|
|
235197
235205
|
const asisLines = text.split('\n');
|
|
235198
235206
|
for (let i = 0; i < asisLines.length; i++) {
|
|
@@ -235202,19 +235210,19 @@ class IdeDiff extends HTMLElement {
|
|
|
235202
235210
|
asisCursor += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
|
|
235203
235211
|
}
|
|
235204
235212
|
|
|
235205
|
-
// ⭐️ ASIS 에디터에 버튼 추가:
|
|
235213
|
+
// ⭐️ ASIS (현재 소스) 에디터에 버튼 추가: AI가 삭제한 것을 ASIS 기준으로 '되돌리기' (TOBE에서 해당 내용을 제거)
|
|
235206
235214
|
asisButtonBuilder.add(
|
|
235207
|
-
asisRangeStart,
|
|
235215
|
+
asisRangeStart,
|
|
235208
235216
|
asisRangeStart,
|
|
235209
235217
|
Decoration.widget({
|
|
235210
235218
|
widget: new MergeButtonWidget(
|
|
235211
|
-
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
235212
|
-
|
|
235213
|
-
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE
|
|
235214
|
-
{ from: tobeRangeStart, to: tobeRangeStart +
|
|
235219
|
+
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
235220
|
+
"", // TOBE에서 해당 내용을 '제거'하므로 삽입할 텍스트는 없음
|
|
235221
|
+
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE (AI 추천)
|
|
235222
|
+
{ from: tobeRangeStart, to: tobeRangeStart + text.length }, // TOBE에서 '삭제될' 범위
|
|
235215
235223
|
currentInstance
|
|
235216
235224
|
),
|
|
235217
|
-
side: 1
|
|
235225
|
+
side: 1
|
|
235218
235226
|
})
|
|
235219
235227
|
);
|
|
235220
235228
|
break;
|
|
@@ -235229,8 +235237,8 @@ class IdeDiff extends HTMLElement {
|
|
|
235229
235237
|
return {
|
|
235230
235238
|
asisDecorations: asisLineBuilder.finish(),
|
|
235231
235239
|
tobeDecorations: tobeLineBuilder.finish(),
|
|
235232
|
-
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
235233
|
-
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
235240
|
+
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
235241
|
+
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
235234
235242
|
};
|
|
235235
235243
|
};
|
|
235236
235244
|
|
|
@@ -235243,13 +235251,13 @@ class IdeDiff extends HTMLElement {
|
|
|
235243
235251
|
this.#asisEditorView.dispatch({
|
|
235244
235252
|
effects: [
|
|
235245
235253
|
setAsisDecorationsEffect.of(asisDecorations),
|
|
235246
|
-
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
235254
|
+
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
235247
235255
|
]
|
|
235248
235256
|
});
|
|
235249
235257
|
this.#tobeEditorView.dispatch({
|
|
235250
235258
|
effects: [
|
|
235251
235259
|
setTobeDecorationsEffect.of(tobeDecorations),
|
|
235252
|
-
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
235260
|
+
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
235253
235261
|
]
|
|
235254
235262
|
});
|
|
235255
235263
|
};
|
package/dist/bundle.esm.js
CHANGED
|
@@ -234899,22 +234899,34 @@ const tobeButtonDecorations = StateField.define({
|
|
|
234899
234899
|
class MergeButtonWidget extends WidgetType {
|
|
234900
234900
|
constructor(isAsisButton, textToApply, targetEditorView, diffRange, hostComponent) {
|
|
234901
234901
|
super();
|
|
234902
|
-
this.isAsisButton = isAsisButton;
|
|
234903
|
-
this.textToApply = textToApply;
|
|
234904
|
-
this.targetEditorView = targetEditorView;
|
|
234905
|
-
this.diffRange = diffRange;
|
|
234906
|
-
this.hostComponent = hostComponent;
|
|
234902
|
+
this.isAsisButton = isAsisButton; // 이 버튼이 ASIS 에디터에 붙는 버튼인가 (true) TOBE 에디터에 붙는 버튼인가 (false)
|
|
234903
|
+
this.textToApply = textToApply; // 적용할 텍스트
|
|
234904
|
+
this.targetEditorView = targetEditorView; // 텍스트를 적용할 에디터 뷰 (상대편 에디터)
|
|
234905
|
+
this.diffRange = diffRange; // 대상 에디터에서 변경이 일어날 정확한 from/to 오프셋
|
|
234906
|
+
this.hostComponent = hostComponent; // IdeDiff 인스턴스 참조
|
|
234907
234907
|
}
|
|
234908
234908
|
|
|
234909
|
+
// 위젯이 차지할 공간을 텍스트 줄에 할당할지 여부. false로 설정하여 버튼이 줄 사이에 끼어들지 않도록 함.
|
|
234909
234910
|
eq(other) { return false; }
|
|
234910
234911
|
|
|
234912
|
+
// 위젯의 DOM 요소를 생성합니다.
|
|
234911
234913
|
toDOM(view) {
|
|
234912
234914
|
const button = document.createElement("button");
|
|
234913
|
-
|
|
234914
|
-
|
|
234915
|
+
// SVN 기준 명칭으로 변경
|
|
234916
|
+
if (this.isAsisButton) {
|
|
234917
|
+
// ASIS (왼쪽) 에디터에 붙는 버튼: AI가 삭제한 것을 '되돌리기'
|
|
234918
|
+
button.className = "cm-merge-button revert"; // 'revert' 클래스 사용
|
|
234919
|
+
button.textContent = "← 되돌리기";
|
|
234920
|
+
} else {
|
|
234921
|
+
// TOBE (오른쪽) 에디터에 붙는 버튼: AI가 추가한 것을 '적용'
|
|
234922
|
+
button.className = "cm-merge-button accept"; // 'accept' 클래스 사용
|
|
234923
|
+
button.textContent = "→ 적용";
|
|
234924
|
+
}
|
|
234925
|
+
|
|
234915
234926
|
|
|
234927
|
+
// 클릭 이벤트 핸들러
|
|
234916
234928
|
button.addEventListener("click", () => {
|
|
234917
|
-
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS
|
|
234929
|
+
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS 쪽 (되돌리기)' : 'TOBE 쪽 (적용)'}`, this.textToApply);
|
|
234918
234930
|
console.log("대상 에디터:", this.targetEditorView === this.hostComponent.asisEditorView ? "ASIS" : "TOBE");
|
|
234919
234931
|
console.log("대상 범위:", this.diffRange);
|
|
234920
234932
|
|
|
@@ -234927,22 +234939,13 @@ class MergeButtonWidget extends WidgetType {
|
|
|
234927
234939
|
return container;
|
|
234928
234940
|
}
|
|
234929
234941
|
|
|
234942
|
+
// 실제 변경 적용 로직
|
|
234930
234943
|
applyChanges(text, editorView, range) {
|
|
234931
234944
|
if (!editorView || !range) {
|
|
234932
234945
|
console.error("Target editor view or range is undefined.", editorView, range);
|
|
234933
234946
|
return;
|
|
234934
234947
|
}
|
|
234935
234948
|
|
|
234936
|
-
// 특정 줄의 시작 오프셋과 끝 오프셋을 정확히 계산하여 적용해야 합니다.
|
|
234937
|
-
// 현재 로직은 단순 치환이므로 Diff 유형에 따라 복잡해질 수 있습니다.
|
|
234938
|
-
// 예를 들어, TOBE에 삽입된 내용을 ASIS에서 되돌릴 경우, ASIS에서 해당 내용 길이만큼의 공백을 지워야 합니다.
|
|
234939
|
-
// ASIS에서 삭제된 내용을 TOBE에 적용할 경우, TOBE의 해당 위치에 내용을 삽입해야 합니다.
|
|
234940
|
-
|
|
234941
|
-
// CodeMirror의 Doc에서 특정 범위를 가져오는 것은 `state.sliceDoc(from, to)`
|
|
234942
|
-
// `dmp.diff_main`의 결과는 줄 단위로 매핑된 문자열이므로,
|
|
234943
|
-
// 실제 오프셋 계산 및 적용 로직은 더 정교해야 합니다.
|
|
234944
|
-
// 지금은 임시로 range.from, range.to를 사용합니다.
|
|
234945
|
-
|
|
234946
234949
|
editorView.dispatch({
|
|
234947
234950
|
changes: {
|
|
234948
234951
|
from: range.from,
|
|
@@ -234970,9 +234973,14 @@ class IdeDiff extends HTMLElement {
|
|
|
234970
234973
|
_asisScrollHandler = null;
|
|
234971
234974
|
_tobeScrollHandler = null;
|
|
234972
234975
|
|
|
234976
|
+
// MergeButtonWidget에서 접근할 수 있도록 Getter를 공개합니다.
|
|
234973
234977
|
get asisEditorView() {
|
|
234974
234978
|
return this.#asisEditorView;
|
|
234975
|
-
}
|
|
234979
|
+
}
|
|
234980
|
+
get tobeEditorView() {
|
|
234981
|
+
return this.#tobeEditorView;
|
|
234982
|
+
}
|
|
234983
|
+
|
|
234976
234984
|
|
|
234977
234985
|
constructor() {
|
|
234978
234986
|
super();
|
|
@@ -234982,7 +234990,9 @@ class IdeDiff extends HTMLElement {
|
|
|
234982
234990
|
connectedCallback() {
|
|
234983
234991
|
this.shadowRoot.innerHTML = `
|
|
234984
234992
|
<style>
|
|
234993
|
+
/* ninegrid CSS 및 필요한 기본 스타일 임포트 */
|
|
234985
234994
|
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ideDiff.css";
|
|
234995
|
+
/* 새로 만든 ideDiff.css 파일 임포트 */
|
|
234986
234996
|
${ninegrid.getCustomPath(this, "ideDiff.css")}
|
|
234987
234997
|
</style>
|
|
234988
234998
|
|
|
@@ -235053,9 +235063,9 @@ class IdeDiff extends HTMLElement {
|
|
|
235053
235063
|
extensions: [
|
|
235054
235064
|
basicExtensions,
|
|
235055
235065
|
this.#languageCompartment.of(javascript()),
|
|
235056
|
-
EditorState.readOnly.of(true),
|
|
235066
|
+
EditorState.readOnly.of(true), // ASIS는 읽기 전용 유지 (원본 소스)
|
|
235057
235067
|
asisDiffDecorations,
|
|
235058
|
-
asisButtonDecorations,
|
|
235068
|
+
asisButtonDecorations,
|
|
235059
235069
|
EditorView.updateListener.of((update) => {
|
|
235060
235070
|
if (update.view.contentDOM.firstChild && !update.view._initialAsisContentLoaded) {
|
|
235061
235071
|
update.view._initialAsisContentLoaded = true;
|
|
@@ -235073,8 +235083,9 @@ class IdeDiff extends HTMLElement {
|
|
|
235073
235083
|
extensions: [
|
|
235074
235084
|
basicExtensions,
|
|
235075
235085
|
this.#languageCompartment.of(javascript()),
|
|
235086
|
+
// EditorState.readOnly.of(true), // TOBE는 AI 추천 소스이므로 편집 가능 (혹은 ASIS로만 적용)
|
|
235076
235087
|
tobeDiffDecorations,
|
|
235077
|
-
tobeButtonDecorations,
|
|
235088
|
+
tobeButtonDecorations,
|
|
235078
235089
|
EditorView.updateListener.of((update) => {
|
|
235079
235090
|
if (update.view.contentDOM.firstChild && !update.view._initialTobeContentLoaded) {
|
|
235080
235091
|
update.view._initialTobeContentLoaded = true;
|
|
@@ -235140,8 +235151,8 @@ class IdeDiff extends HTMLElement {
|
|
|
235140
235151
|
|
|
235141
235152
|
const asisLineBuilder = new RangeSetBuilder();
|
|
235142
235153
|
const tobeLineBuilder = new RangeSetBuilder();
|
|
235143
|
-
const asisButtonBuilder = new RangeSetBuilder();
|
|
235144
|
-
const tobeButtonBuilder = new RangeSetBuilder();
|
|
235154
|
+
const asisButtonBuilder = new RangeSetBuilder();
|
|
235155
|
+
const tobeButtonBuilder = new RangeSetBuilder();
|
|
235145
235156
|
|
|
235146
235157
|
let asisCursor = 0;
|
|
235147
235158
|
let tobeCursor = 0;
|
|
@@ -235158,7 +235169,7 @@ class IdeDiff extends HTMLElement {
|
|
|
235158
235169
|
const tobeRangeStart = tobeCursor;
|
|
235159
235170
|
|
|
235160
235171
|
switch (op) {
|
|
235161
|
-
case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // TOBE에 추가된 내용 (ASIS에는 없음)
|
|
235172
|
+
case diffMatchPatchExports.diff_match_patch.DIFF_INSERT: // TOBE (AI 추천)에 추가된 내용 (ASIS에는 없음)
|
|
235162
235173
|
console.log("DIFF_INSERT (TOBE added):", JSON.stringify(text));
|
|
235163
235174
|
const tobeLines = text.split('\n');
|
|
235164
235175
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
@@ -235168,27 +235179,24 @@ class IdeDiff extends HTMLElement {
|
|
|
235168
235179
|
tobeCursor += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
|
|
235169
235180
|
}
|
|
235170
235181
|
|
|
235171
|
-
// ⭐️ TOBE 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS
|
|
235172
|
-
// TOBE의 해당 Diff가 끝나는 지점에 버튼을 붙이는 것이 자연스럽습니다.
|
|
235173
|
-
// tobeRangeStart는 Diff 청크의 시작 오프셋입니다.
|
|
235174
|
-
// 버튼을 Line Decoration과 같은 위치에 추가하되, 별도의 builder 사용
|
|
235182
|
+
// ⭐️ TOBE (AI 추천) 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS (현재 소스)에 '적용'
|
|
235175
235183
|
tobeButtonBuilder.add(
|
|
235176
|
-
tobeRangeStart,
|
|
235184
|
+
tobeRangeStart,
|
|
235177
235185
|
tobeRangeStart,
|
|
235178
235186
|
Decoration.widget({
|
|
235179
235187
|
widget: new MergeButtonWidget(
|
|
235180
|
-
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
235181
|
-
|
|
235182
|
-
currentInstance.#asisEditorView, // 대상 에디터는 ASIS
|
|
235183
|
-
{ from: asisRangeStart, to: asisRangeStart +
|
|
235188
|
+
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
235189
|
+
text, // AI가 추가한 내용을 ASIS에 '삽입'
|
|
235190
|
+
currentInstance.#asisEditorView, // 대상 에디터는 ASIS (현재 소스)
|
|
235191
|
+
{ from: asisRangeStart, to: asisRangeStart + 0 }, // ASIS에서의 '삽입될' 위치
|
|
235184
235192
|
currentInstance
|
|
235185
235193
|
),
|
|
235186
|
-
side: 1
|
|
235194
|
+
side: 1
|
|
235187
235195
|
})
|
|
235188
235196
|
);
|
|
235189
235197
|
break;
|
|
235190
235198
|
|
|
235191
|
-
case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // ASIS에서 삭제된 내용 (TOBE에는 없음)
|
|
235199
|
+
case diffMatchPatchExports.diff_match_patch.DIFF_DELETE: // ASIS (현재 소스)에서 삭제된 내용 (TOBE에는 없음)
|
|
235192
235200
|
console.log("DIFF_DELETE (ASIS deleted):", JSON.stringify(text));
|
|
235193
235201
|
const asisLines = text.split('\n');
|
|
235194
235202
|
for (let i = 0; i < asisLines.length; i++) {
|
|
@@ -235198,19 +235206,19 @@ class IdeDiff extends HTMLElement {
|
|
|
235198
235206
|
asisCursor += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
|
|
235199
235207
|
}
|
|
235200
235208
|
|
|
235201
|
-
// ⭐️ ASIS 에디터에 버튼 추가:
|
|
235209
|
+
// ⭐️ ASIS (현재 소스) 에디터에 버튼 추가: AI가 삭제한 것을 ASIS 기준으로 '되돌리기' (TOBE에서 해당 내용을 제거)
|
|
235202
235210
|
asisButtonBuilder.add(
|
|
235203
|
-
asisRangeStart,
|
|
235211
|
+
asisRangeStart,
|
|
235204
235212
|
asisRangeStart,
|
|
235205
235213
|
Decoration.widget({
|
|
235206
235214
|
widget: new MergeButtonWidget(
|
|
235207
|
-
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
235208
|
-
|
|
235209
|
-
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE
|
|
235210
|
-
{ from: tobeRangeStart, to: tobeRangeStart +
|
|
235215
|
+
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
235216
|
+
"", // TOBE에서 해당 내용을 '제거'하므로 삽입할 텍스트는 없음
|
|
235217
|
+
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE (AI 추천)
|
|
235218
|
+
{ from: tobeRangeStart, to: tobeRangeStart + text.length }, // TOBE에서 '삭제될' 범위
|
|
235211
235219
|
currentInstance
|
|
235212
235220
|
),
|
|
235213
|
-
side: 1
|
|
235221
|
+
side: 1
|
|
235214
235222
|
})
|
|
235215
235223
|
);
|
|
235216
235224
|
break;
|
|
@@ -235225,8 +235233,8 @@ class IdeDiff extends HTMLElement {
|
|
|
235225
235233
|
return {
|
|
235226
235234
|
asisDecorations: asisLineBuilder.finish(),
|
|
235227
235235
|
tobeDecorations: tobeLineBuilder.finish(),
|
|
235228
|
-
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
235229
|
-
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
235236
|
+
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
235237
|
+
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
235230
235238
|
};
|
|
235231
235239
|
};
|
|
235232
235240
|
|
|
@@ -235239,13 +235247,13 @@ class IdeDiff extends HTMLElement {
|
|
|
235239
235247
|
this.#asisEditorView.dispatch({
|
|
235240
235248
|
effects: [
|
|
235241
235249
|
setAsisDecorationsEffect.of(asisDecorations),
|
|
235242
|
-
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
235250
|
+
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
235243
235251
|
]
|
|
235244
235252
|
});
|
|
235245
235253
|
this.#tobeEditorView.dispatch({
|
|
235246
235254
|
effects: [
|
|
235247
235255
|
setTobeDecorationsEffect.of(tobeDecorations),
|
|
235248
|
-
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
235256
|
+
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
235249
235257
|
]
|
|
235250
235258
|
});
|
|
235251
235259
|
};
|
|
@@ -99,22 +99,34 @@ const tobeButtonDecorations = StateField.define({
|
|
|
99
99
|
class MergeButtonWidget extends WidgetType {
|
|
100
100
|
constructor(isAsisButton, textToApply, targetEditorView, diffRange, hostComponent) {
|
|
101
101
|
super();
|
|
102
|
-
this.isAsisButton = isAsisButton;
|
|
103
|
-
this.textToApply = textToApply;
|
|
104
|
-
this.targetEditorView = targetEditorView;
|
|
105
|
-
this.diffRange = diffRange;
|
|
106
|
-
this.hostComponent = hostComponent;
|
|
102
|
+
this.isAsisButton = isAsisButton; // 이 버튼이 ASIS 에디터에 붙는 버튼인가 (true) TOBE 에디터에 붙는 버튼인가 (false)
|
|
103
|
+
this.textToApply = textToApply; // 적용할 텍스트
|
|
104
|
+
this.targetEditorView = targetEditorView; // 텍스트를 적용할 에디터 뷰 (상대편 에디터)
|
|
105
|
+
this.diffRange = diffRange; // 대상 에디터에서 변경이 일어날 정확한 from/to 오프셋
|
|
106
|
+
this.hostComponent = hostComponent; // IdeDiff 인스턴스 참조
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
// 위젯이 차지할 공간을 텍스트 줄에 할당할지 여부. false로 설정하여 버튼이 줄 사이에 끼어들지 않도록 함.
|
|
109
110
|
eq(other) { return false; }
|
|
110
111
|
|
|
112
|
+
// 위젯의 DOM 요소를 생성합니다.
|
|
111
113
|
toDOM(view) {
|
|
112
114
|
const button = document.createElement("button");
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
// SVN 기준 명칭으로 변경
|
|
116
|
+
if (this.isAsisButton) {
|
|
117
|
+
// ASIS (왼쪽) 에디터에 붙는 버튼: AI가 삭제한 것을 '되돌리기'
|
|
118
|
+
button.className = "cm-merge-button revert"; // 'revert' 클래스 사용
|
|
119
|
+
button.textContent = "← 되돌리기";
|
|
120
|
+
} else {
|
|
121
|
+
// TOBE (오른쪽) 에디터에 붙는 버튼: AI가 추가한 것을 '적용'
|
|
122
|
+
button.className = "cm-merge-button accept"; // 'accept' 클래스 사용
|
|
123
|
+
button.textContent = "→ 적용";
|
|
124
|
+
}
|
|
125
|
+
|
|
115
126
|
|
|
127
|
+
// 클릭 이벤트 핸들러
|
|
116
128
|
button.addEventListener("click", () => {
|
|
117
|
-
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS
|
|
129
|
+
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS 쪽 (되돌리기)' : 'TOBE 쪽 (적용)'}`, this.textToApply);
|
|
118
130
|
console.log("대상 에디터:", this.targetEditorView === this.hostComponent.asisEditorView ? "ASIS" : "TOBE");
|
|
119
131
|
console.log("대상 범위:", this.diffRange);
|
|
120
132
|
|
|
@@ -127,22 +139,13 @@ class MergeButtonWidget extends WidgetType {
|
|
|
127
139
|
return container;
|
|
128
140
|
}
|
|
129
141
|
|
|
142
|
+
// 실제 변경 적용 로직
|
|
130
143
|
applyChanges(text, editorView, range) {
|
|
131
144
|
if (!editorView || !range) {
|
|
132
145
|
console.error("Target editor view or range is undefined.", editorView, range);
|
|
133
146
|
return;
|
|
134
147
|
}
|
|
135
148
|
|
|
136
|
-
// 특정 줄의 시작 오프셋과 끝 오프셋을 정확히 계산하여 적용해야 합니다.
|
|
137
|
-
// 현재 로직은 단순 치환이므로 Diff 유형에 따라 복잡해질 수 있습니다.
|
|
138
|
-
// 예를 들어, TOBE에 삽입된 내용을 ASIS에서 되돌릴 경우, ASIS에서 해당 내용 길이만큼의 공백을 지워야 합니다.
|
|
139
|
-
// ASIS에서 삭제된 내용을 TOBE에 적용할 경우, TOBE의 해당 위치에 내용을 삽입해야 합니다.
|
|
140
|
-
|
|
141
|
-
// CodeMirror의 Doc에서 특정 범위를 가져오는 것은 `state.sliceDoc(from, to)`
|
|
142
|
-
// `dmp.diff_main`의 결과는 줄 단위로 매핑된 문자열이므로,
|
|
143
|
-
// 실제 오프셋 계산 및 적용 로직은 더 정교해야 합니다.
|
|
144
|
-
// 지금은 임시로 range.from, range.to를 사용합니다.
|
|
145
|
-
|
|
146
149
|
editorView.dispatch({
|
|
147
150
|
changes: {
|
|
148
151
|
from: range.from,
|
|
@@ -170,9 +173,14 @@ export class IdeDiff extends HTMLElement {
|
|
|
170
173
|
_asisScrollHandler = null;
|
|
171
174
|
_tobeScrollHandler = null;
|
|
172
175
|
|
|
176
|
+
// MergeButtonWidget에서 접근할 수 있도록 Getter를 공개합니다.
|
|
173
177
|
get asisEditorView() {
|
|
174
178
|
return this.#asisEditorView;
|
|
175
|
-
}
|
|
179
|
+
}
|
|
180
|
+
get tobeEditorView() {
|
|
181
|
+
return this.#tobeEditorView;
|
|
182
|
+
}
|
|
183
|
+
|
|
176
184
|
|
|
177
185
|
constructor() {
|
|
178
186
|
super();
|
|
@@ -182,7 +190,9 @@ export class IdeDiff extends HTMLElement {
|
|
|
182
190
|
connectedCallback() {
|
|
183
191
|
this.shadowRoot.innerHTML = `
|
|
184
192
|
<style>
|
|
193
|
+
/* ninegrid CSS 및 필요한 기본 스타일 임포트 */
|
|
185
194
|
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ideDiff.css";
|
|
195
|
+
/* 새로 만든 ideDiff.css 파일 임포트 */
|
|
186
196
|
${ninegrid.getCustomPath(this, "ideDiff.css")}
|
|
187
197
|
</style>
|
|
188
198
|
|
|
@@ -253,9 +263,9 @@ export class IdeDiff extends HTMLElement {
|
|
|
253
263
|
extensions: [
|
|
254
264
|
basicExtensions,
|
|
255
265
|
this.#languageCompartment.of(javascript()),
|
|
256
|
-
EditorState.readOnly.of(true),
|
|
266
|
+
EditorState.readOnly.of(true), // ASIS는 읽기 전용 유지 (원본 소스)
|
|
257
267
|
asisDiffDecorations,
|
|
258
|
-
asisButtonDecorations,
|
|
268
|
+
asisButtonDecorations,
|
|
259
269
|
EditorView.updateListener.of((update) => {
|
|
260
270
|
if (update.view.contentDOM.firstChild && !update.view._initialAsisContentLoaded) {
|
|
261
271
|
update.view._initialAsisContentLoaded = true;
|
|
@@ -273,8 +283,9 @@ export class IdeDiff extends HTMLElement {
|
|
|
273
283
|
extensions: [
|
|
274
284
|
basicExtensions,
|
|
275
285
|
this.#languageCompartment.of(javascript()),
|
|
286
|
+
// EditorState.readOnly.of(true), // TOBE는 AI 추천 소스이므로 편집 가능 (혹은 ASIS로만 적용)
|
|
276
287
|
tobeDiffDecorations,
|
|
277
|
-
tobeButtonDecorations,
|
|
288
|
+
tobeButtonDecorations,
|
|
278
289
|
EditorView.updateListener.of((update) => {
|
|
279
290
|
if (update.view.contentDOM.firstChild && !update.view._initialTobeContentLoaded) {
|
|
280
291
|
update.view._initialTobeContentLoaded = true;
|
|
@@ -340,8 +351,8 @@ export class IdeDiff extends HTMLElement {
|
|
|
340
351
|
|
|
341
352
|
const asisLineBuilder = new RangeSetBuilder();
|
|
342
353
|
const tobeLineBuilder = new RangeSetBuilder();
|
|
343
|
-
const asisButtonBuilder = new RangeSetBuilder();
|
|
344
|
-
const tobeButtonBuilder = new RangeSetBuilder();
|
|
354
|
+
const asisButtonBuilder = new RangeSetBuilder();
|
|
355
|
+
const tobeButtonBuilder = new RangeSetBuilder();
|
|
345
356
|
|
|
346
357
|
let asisCursor = 0;
|
|
347
358
|
let tobeCursor = 0;
|
|
@@ -358,7 +369,7 @@ export class IdeDiff extends HTMLElement {
|
|
|
358
369
|
const tobeRangeStart = tobeCursor;
|
|
359
370
|
|
|
360
371
|
switch (op) {
|
|
361
|
-
case diff_match_patch.DIFF_INSERT: // TOBE에 추가된 내용 (ASIS에는 없음)
|
|
372
|
+
case diff_match_patch.DIFF_INSERT: // TOBE (AI 추천)에 추가된 내용 (ASIS에는 없음)
|
|
362
373
|
console.log("DIFF_INSERT (TOBE added):", JSON.stringify(text));
|
|
363
374
|
const tobeLines = text.split('\n');
|
|
364
375
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
@@ -368,27 +379,24 @@ export class IdeDiff extends HTMLElement {
|
|
|
368
379
|
tobeCursor += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
|
|
369
380
|
}
|
|
370
381
|
|
|
371
|
-
// ⭐️ TOBE 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS
|
|
372
|
-
// TOBE의 해당 Diff가 끝나는 지점에 버튼을 붙이는 것이 자연스럽습니다.
|
|
373
|
-
// tobeRangeStart는 Diff 청크의 시작 오프셋입니다.
|
|
374
|
-
// 버튼을 Line Decoration과 같은 위치에 추가하되, 별도의 builder 사용
|
|
382
|
+
// ⭐️ TOBE (AI 추천) 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS (현재 소스)에 '적용'
|
|
375
383
|
tobeButtonBuilder.add(
|
|
376
|
-
tobeRangeStart,
|
|
384
|
+
tobeRangeStart,
|
|
377
385
|
tobeRangeStart,
|
|
378
386
|
Decoration.widget({
|
|
379
387
|
widget: new MergeButtonWidget(
|
|
380
|
-
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
381
|
-
|
|
382
|
-
currentInstance.#asisEditorView, // 대상 에디터는 ASIS
|
|
383
|
-
{ from: asisRangeStart, to: asisRangeStart +
|
|
388
|
+
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
389
|
+
text, // AI가 추가한 내용을 ASIS에 '삽입'
|
|
390
|
+
currentInstance.#asisEditorView, // 대상 에디터는 ASIS (현재 소스)
|
|
391
|
+
{ from: asisRangeStart, to: asisRangeStart + 0 }, // ASIS에서의 '삽입될' 위치
|
|
384
392
|
currentInstance
|
|
385
393
|
),
|
|
386
|
-
side: 1
|
|
394
|
+
side: 1
|
|
387
395
|
})
|
|
388
396
|
);
|
|
389
397
|
break;
|
|
390
398
|
|
|
391
|
-
case diff_match_patch.DIFF_DELETE: // ASIS에서 삭제된 내용 (TOBE에는 없음)
|
|
399
|
+
case diff_match_patch.DIFF_DELETE: // ASIS (현재 소스)에서 삭제된 내용 (TOBE에는 없음)
|
|
392
400
|
console.log("DIFF_DELETE (ASIS deleted):", JSON.stringify(text));
|
|
393
401
|
const asisLines = text.split('\n');
|
|
394
402
|
for (let i = 0; i < asisLines.length; i++) {
|
|
@@ -398,19 +406,19 @@ export class IdeDiff extends HTMLElement {
|
|
|
398
406
|
asisCursor += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
|
|
399
407
|
}
|
|
400
408
|
|
|
401
|
-
// ⭐️ ASIS 에디터에 버튼 추가:
|
|
409
|
+
// ⭐️ ASIS (현재 소스) 에디터에 버튼 추가: AI가 삭제한 것을 ASIS 기준으로 '되돌리기' (TOBE에서 해당 내용을 제거)
|
|
402
410
|
asisButtonBuilder.add(
|
|
403
|
-
asisRangeStart,
|
|
411
|
+
asisRangeStart,
|
|
404
412
|
asisRangeStart,
|
|
405
413
|
Decoration.widget({
|
|
406
414
|
widget: new MergeButtonWidget(
|
|
407
|
-
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
408
|
-
|
|
409
|
-
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE
|
|
410
|
-
{ from: tobeRangeStart, to: tobeRangeStart +
|
|
415
|
+
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
416
|
+
"", // TOBE에서 해당 내용을 '제거'하므로 삽입할 텍스트는 없음
|
|
417
|
+
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE (AI 추천)
|
|
418
|
+
{ from: tobeRangeStart, to: tobeRangeStart + text.length }, // TOBE에서 '삭제될' 범위
|
|
411
419
|
currentInstance
|
|
412
420
|
),
|
|
413
|
-
side: 1
|
|
421
|
+
side: 1
|
|
414
422
|
})
|
|
415
423
|
);
|
|
416
424
|
break;
|
|
@@ -425,8 +433,8 @@ export class IdeDiff extends HTMLElement {
|
|
|
425
433
|
return {
|
|
426
434
|
asisDecorations: asisLineBuilder.finish(),
|
|
427
435
|
tobeDecorations: tobeLineBuilder.finish(),
|
|
428
|
-
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
429
|
-
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
436
|
+
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
437
|
+
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
430
438
|
};
|
|
431
439
|
};
|
|
432
440
|
|
|
@@ -439,13 +447,13 @@ export class IdeDiff extends HTMLElement {
|
|
|
439
447
|
this.#asisEditorView.dispatch({
|
|
440
448
|
effects: [
|
|
441
449
|
setAsisDecorationsEffect.of(asisDecorations),
|
|
442
|
-
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
450
|
+
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
443
451
|
]
|
|
444
452
|
});
|
|
445
453
|
this.#tobeEditorView.dispatch({
|
|
446
454
|
effects: [
|
|
447
455
|
setTobeDecorationsEffect.of(tobeDecorations),
|
|
448
|
-
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
456
|
+
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
449
457
|
]
|
|
450
458
|
});
|
|
451
459
|
};
|
package/package.json
CHANGED
|
@@ -99,22 +99,34 @@ const tobeButtonDecorations = StateField.define({
|
|
|
99
99
|
class MergeButtonWidget extends WidgetType {
|
|
100
100
|
constructor(isAsisButton, textToApply, targetEditorView, diffRange, hostComponent) {
|
|
101
101
|
super();
|
|
102
|
-
this.isAsisButton = isAsisButton;
|
|
103
|
-
this.textToApply = textToApply;
|
|
104
|
-
this.targetEditorView = targetEditorView;
|
|
105
|
-
this.diffRange = diffRange;
|
|
106
|
-
this.hostComponent = hostComponent;
|
|
102
|
+
this.isAsisButton = isAsisButton; // 이 버튼이 ASIS 에디터에 붙는 버튼인가 (true) TOBE 에디터에 붙는 버튼인가 (false)
|
|
103
|
+
this.textToApply = textToApply; // 적용할 텍스트
|
|
104
|
+
this.targetEditorView = targetEditorView; // 텍스트를 적용할 에디터 뷰 (상대편 에디터)
|
|
105
|
+
this.diffRange = diffRange; // 대상 에디터에서 변경이 일어날 정확한 from/to 오프셋
|
|
106
|
+
this.hostComponent = hostComponent; // IdeDiff 인스턴스 참조
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
// 위젯이 차지할 공간을 텍스트 줄에 할당할지 여부. false로 설정하여 버튼이 줄 사이에 끼어들지 않도록 함.
|
|
109
110
|
eq(other) { return false; }
|
|
110
111
|
|
|
112
|
+
// 위젯의 DOM 요소를 생성합니다.
|
|
111
113
|
toDOM(view) {
|
|
112
114
|
const button = document.createElement("button");
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
// SVN 기준 명칭으로 변경
|
|
116
|
+
if (this.isAsisButton) {
|
|
117
|
+
// ASIS (왼쪽) 에디터에 붙는 버튼: AI가 삭제한 것을 '되돌리기'
|
|
118
|
+
button.className = "cm-merge-button revert"; // 'revert' 클래스 사용
|
|
119
|
+
button.textContent = "← 되돌리기";
|
|
120
|
+
} else {
|
|
121
|
+
// TOBE (오른쪽) 에디터에 붙는 버튼: AI가 추가한 것을 '적용'
|
|
122
|
+
button.className = "cm-merge-button accept"; // 'accept' 클래스 사용
|
|
123
|
+
button.textContent = "→ 적용";
|
|
124
|
+
}
|
|
125
|
+
|
|
115
126
|
|
|
127
|
+
// 클릭 이벤트 핸들러
|
|
116
128
|
button.addEventListener("click", () => {
|
|
117
|
-
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS
|
|
129
|
+
console.log(`버튼 클릭: ${this.isAsisButton ? 'ASIS 쪽 (되돌리기)' : 'TOBE 쪽 (적용)'}`, this.textToApply);
|
|
118
130
|
console.log("대상 에디터:", this.targetEditorView === this.hostComponent.asisEditorView ? "ASIS" : "TOBE");
|
|
119
131
|
console.log("대상 범위:", this.diffRange);
|
|
120
132
|
|
|
@@ -127,22 +139,13 @@ class MergeButtonWidget extends WidgetType {
|
|
|
127
139
|
return container;
|
|
128
140
|
}
|
|
129
141
|
|
|
142
|
+
// 실제 변경 적용 로직
|
|
130
143
|
applyChanges(text, editorView, range) {
|
|
131
144
|
if (!editorView || !range) {
|
|
132
145
|
console.error("Target editor view or range is undefined.", editorView, range);
|
|
133
146
|
return;
|
|
134
147
|
}
|
|
135
148
|
|
|
136
|
-
// 특정 줄의 시작 오프셋과 끝 오프셋을 정확히 계산하여 적용해야 합니다.
|
|
137
|
-
// 현재 로직은 단순 치환이므로 Diff 유형에 따라 복잡해질 수 있습니다.
|
|
138
|
-
// 예를 들어, TOBE에 삽입된 내용을 ASIS에서 되돌릴 경우, ASIS에서 해당 내용 길이만큼의 공백을 지워야 합니다.
|
|
139
|
-
// ASIS에서 삭제된 내용을 TOBE에 적용할 경우, TOBE의 해당 위치에 내용을 삽입해야 합니다.
|
|
140
|
-
|
|
141
|
-
// CodeMirror의 Doc에서 특정 범위를 가져오는 것은 `state.sliceDoc(from, to)`
|
|
142
|
-
// `dmp.diff_main`의 결과는 줄 단위로 매핑된 문자열이므로,
|
|
143
|
-
// 실제 오프셋 계산 및 적용 로직은 더 정교해야 합니다.
|
|
144
|
-
// 지금은 임시로 range.from, range.to를 사용합니다.
|
|
145
|
-
|
|
146
149
|
editorView.dispatch({
|
|
147
150
|
changes: {
|
|
148
151
|
from: range.from,
|
|
@@ -170,9 +173,14 @@ export class IdeDiff extends HTMLElement {
|
|
|
170
173
|
_asisScrollHandler = null;
|
|
171
174
|
_tobeScrollHandler = null;
|
|
172
175
|
|
|
176
|
+
// MergeButtonWidget에서 접근할 수 있도록 Getter를 공개합니다.
|
|
173
177
|
get asisEditorView() {
|
|
174
178
|
return this.#asisEditorView;
|
|
175
|
-
}
|
|
179
|
+
}
|
|
180
|
+
get tobeEditorView() {
|
|
181
|
+
return this.#tobeEditorView;
|
|
182
|
+
}
|
|
183
|
+
|
|
176
184
|
|
|
177
185
|
constructor() {
|
|
178
186
|
super();
|
|
@@ -182,7 +190,9 @@ export class IdeDiff extends HTMLElement {
|
|
|
182
190
|
connectedCallback() {
|
|
183
191
|
this.shadowRoot.innerHTML = `
|
|
184
192
|
<style>
|
|
193
|
+
/* ninegrid CSS 및 필요한 기본 스타일 임포트 */
|
|
185
194
|
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ideDiff.css";
|
|
195
|
+
/* 새로 만든 ideDiff.css 파일 임포트 */
|
|
186
196
|
${ninegrid.getCustomPath(this, "ideDiff.css")}
|
|
187
197
|
</style>
|
|
188
198
|
|
|
@@ -253,9 +263,9 @@ export class IdeDiff extends HTMLElement {
|
|
|
253
263
|
extensions: [
|
|
254
264
|
basicExtensions,
|
|
255
265
|
this.#languageCompartment.of(javascript()),
|
|
256
|
-
EditorState.readOnly.of(true),
|
|
266
|
+
EditorState.readOnly.of(true), // ASIS는 읽기 전용 유지 (원본 소스)
|
|
257
267
|
asisDiffDecorations,
|
|
258
|
-
asisButtonDecorations,
|
|
268
|
+
asisButtonDecorations,
|
|
259
269
|
EditorView.updateListener.of((update) => {
|
|
260
270
|
if (update.view.contentDOM.firstChild && !update.view._initialAsisContentLoaded) {
|
|
261
271
|
update.view._initialAsisContentLoaded = true;
|
|
@@ -273,8 +283,9 @@ export class IdeDiff extends HTMLElement {
|
|
|
273
283
|
extensions: [
|
|
274
284
|
basicExtensions,
|
|
275
285
|
this.#languageCompartment.of(javascript()),
|
|
286
|
+
// EditorState.readOnly.of(true), // TOBE는 AI 추천 소스이므로 편집 가능 (혹은 ASIS로만 적용)
|
|
276
287
|
tobeDiffDecorations,
|
|
277
|
-
tobeButtonDecorations,
|
|
288
|
+
tobeButtonDecorations,
|
|
278
289
|
EditorView.updateListener.of((update) => {
|
|
279
290
|
if (update.view.contentDOM.firstChild && !update.view._initialTobeContentLoaded) {
|
|
280
291
|
update.view._initialTobeContentLoaded = true;
|
|
@@ -340,8 +351,8 @@ export class IdeDiff extends HTMLElement {
|
|
|
340
351
|
|
|
341
352
|
const asisLineBuilder = new RangeSetBuilder();
|
|
342
353
|
const tobeLineBuilder = new RangeSetBuilder();
|
|
343
|
-
const asisButtonBuilder = new RangeSetBuilder();
|
|
344
|
-
const tobeButtonBuilder = new RangeSetBuilder();
|
|
354
|
+
const asisButtonBuilder = new RangeSetBuilder();
|
|
355
|
+
const tobeButtonBuilder = new RangeSetBuilder();
|
|
345
356
|
|
|
346
357
|
let asisCursor = 0;
|
|
347
358
|
let tobeCursor = 0;
|
|
@@ -358,7 +369,7 @@ export class IdeDiff extends HTMLElement {
|
|
|
358
369
|
const tobeRangeStart = tobeCursor;
|
|
359
370
|
|
|
360
371
|
switch (op) {
|
|
361
|
-
case diff_match_patch.DIFF_INSERT: // TOBE에 추가된 내용 (ASIS에는 없음)
|
|
372
|
+
case diff_match_patch.DIFF_INSERT: // TOBE (AI 추천)에 추가된 내용 (ASIS에는 없음)
|
|
362
373
|
console.log("DIFF_INSERT (TOBE added):", JSON.stringify(text));
|
|
363
374
|
const tobeLines = text.split('\n');
|
|
364
375
|
for (let i = 0; i < tobeLines.length; i++) {
|
|
@@ -368,27 +379,24 @@ export class IdeDiff extends HTMLElement {
|
|
|
368
379
|
tobeCursor += tobeLines[i].length + (i < tobeLines.length - 1 ? 1 : 0);
|
|
369
380
|
}
|
|
370
381
|
|
|
371
|
-
// ⭐️ TOBE 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS
|
|
372
|
-
// TOBE의 해당 Diff가 끝나는 지점에 버튼을 붙이는 것이 자연스럽습니다.
|
|
373
|
-
// tobeRangeStart는 Diff 청크의 시작 오프셋입니다.
|
|
374
|
-
// 버튼을 Line Decoration과 같은 위치에 추가하되, 별도의 builder 사용
|
|
382
|
+
// ⭐️ TOBE (AI 추천) 에디터에 버튼 추가: TOBE의 추가 내용을 ASIS (현재 소스)에 '적용'
|
|
375
383
|
tobeButtonBuilder.add(
|
|
376
|
-
tobeRangeStart,
|
|
384
|
+
tobeRangeStart,
|
|
377
385
|
tobeRangeStart,
|
|
378
386
|
Decoration.widget({
|
|
379
387
|
widget: new MergeButtonWidget(
|
|
380
|
-
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
381
|
-
|
|
382
|
-
currentInstance.#asisEditorView, // 대상 에디터는 ASIS
|
|
383
|
-
{ from: asisRangeStart, to: asisRangeStart +
|
|
388
|
+
false, // 이 버튼은 TOBE 에디터에 붙는 버튼
|
|
389
|
+
text, // AI가 추가한 내용을 ASIS에 '삽입'
|
|
390
|
+
currentInstance.#asisEditorView, // 대상 에디터는 ASIS (현재 소스)
|
|
391
|
+
{ from: asisRangeStart, to: asisRangeStart + 0 }, // ASIS에서의 '삽입될' 위치
|
|
384
392
|
currentInstance
|
|
385
393
|
),
|
|
386
|
-
side: 1
|
|
394
|
+
side: 1
|
|
387
395
|
})
|
|
388
396
|
);
|
|
389
397
|
break;
|
|
390
398
|
|
|
391
|
-
case diff_match_patch.DIFF_DELETE: // ASIS에서 삭제된 내용 (TOBE에는 없음)
|
|
399
|
+
case diff_match_patch.DIFF_DELETE: // ASIS (현재 소스)에서 삭제된 내용 (TOBE에는 없음)
|
|
392
400
|
console.log("DIFF_DELETE (ASIS deleted):", JSON.stringify(text));
|
|
393
401
|
const asisLines = text.split('\n');
|
|
394
402
|
for (let i = 0; i < asisLines.length; i++) {
|
|
@@ -398,19 +406,19 @@ export class IdeDiff extends HTMLElement {
|
|
|
398
406
|
asisCursor += asisLines[i].length + (i < asisLines.length - 1 ? 1 : 0);
|
|
399
407
|
}
|
|
400
408
|
|
|
401
|
-
// ⭐️ ASIS 에디터에 버튼 추가:
|
|
409
|
+
// ⭐️ ASIS (현재 소스) 에디터에 버튼 추가: AI가 삭제한 것을 ASIS 기준으로 '되돌리기' (TOBE에서 해당 내용을 제거)
|
|
402
410
|
asisButtonBuilder.add(
|
|
403
|
-
asisRangeStart,
|
|
411
|
+
asisRangeStart,
|
|
404
412
|
asisRangeStart,
|
|
405
413
|
Decoration.widget({
|
|
406
414
|
widget: new MergeButtonWidget(
|
|
407
|
-
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
408
|
-
|
|
409
|
-
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE
|
|
410
|
-
{ from: tobeRangeStart, to: tobeRangeStart +
|
|
415
|
+
true, // 이 버튼은 ASIS 에디터에 붙는 버튼
|
|
416
|
+
"", // TOBE에서 해당 내용을 '제거'하므로 삽입할 텍스트는 없음
|
|
417
|
+
currentInstance.#tobeEditorView, // 대상 에디터는 TOBE (AI 추천)
|
|
418
|
+
{ from: tobeRangeStart, to: tobeRangeStart + text.length }, // TOBE에서 '삭제될' 범위
|
|
411
419
|
currentInstance
|
|
412
420
|
),
|
|
413
|
-
side: 1
|
|
421
|
+
side: 1
|
|
414
422
|
})
|
|
415
423
|
);
|
|
416
424
|
break;
|
|
@@ -425,8 +433,8 @@ export class IdeDiff extends HTMLElement {
|
|
|
425
433
|
return {
|
|
426
434
|
asisDecorations: asisLineBuilder.finish(),
|
|
427
435
|
tobeDecorations: tobeLineBuilder.finish(),
|
|
428
|
-
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
429
|
-
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
436
|
+
asisButtonDecorations: asisButtonBuilder.finish(),
|
|
437
|
+
tobeButtonDecorations: tobeButtonBuilder.finish()
|
|
430
438
|
};
|
|
431
439
|
};
|
|
432
440
|
|
|
@@ -439,13 +447,13 @@ export class IdeDiff extends HTMLElement {
|
|
|
439
447
|
this.#asisEditorView.dispatch({
|
|
440
448
|
effects: [
|
|
441
449
|
setAsisDecorationsEffect.of(asisDecorations),
|
|
442
|
-
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
450
|
+
setAsisButtonDecorationsEffect.of(asisButtonDecorations)
|
|
443
451
|
]
|
|
444
452
|
});
|
|
445
453
|
this.#tobeEditorView.dispatch({
|
|
446
454
|
effects: [
|
|
447
455
|
setTobeDecorationsEffect.of(tobeDecorations),
|
|
448
|
-
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
456
|
+
setTobeButtonDecorationsEffect.of(tobeButtonDecorations)
|
|
449
457
|
]
|
|
450
458
|
});
|
|
451
459
|
};
|