ide-assi 0.328.0 → 0.330.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 +409 -24
- package/dist/bundle.esm.js +409 -24
- package/dist/components/ideAssi.js +322 -3
- package/dist/components/ideDiff.js +88 -23
- package/package.json +1 -1
- package/src/components/ideAssi.js +322 -3
- package/src/components/ideDiff.js +88 -23
package/dist/bundle.cjs.js
CHANGED
|
@@ -194116,10 +194116,329 @@ class IdeAssi extends HTMLElement
|
|
|
194116
194116
|
|
|
194117
194117
|
//this.shadowRoot.querySelector('ide-diff-popup').remove();
|
|
194118
194118
|
|
|
194119
|
-
|
|
194120
|
-
|
|
194119
|
+
this.shadowRoot.appendChild(document.createElement('ide-diff-popup'));
|
|
194120
|
+
|
|
194121
194121
|
//setTimeout(() => {
|
|
194122
|
-
|
|
194122
|
+
const src1 = `
|
|
194123
|
+
import React, { useRef, useEffect } from "react";
|
|
194124
|
+
import { api, ai } from "ide-assi";
|
|
194125
|
+
import ninegrid from "ninegrid2";
|
|
194126
|
+
|
|
194127
|
+
const DocManager = () => {
|
|
194128
|
+
const tabRef = useRef(null);
|
|
194129
|
+
const gridRef = useRef(null);
|
|
194130
|
+
|
|
194131
|
+
const selectList = async (params) => {
|
|
194132
|
+
if (!gridRef.current) return;
|
|
194133
|
+
gridRef.current.classList.add("loading");
|
|
194134
|
+
api.post(\`/api/tmpl-a/doc-manager/selectList\`, params).then((res) => {
|
|
194135
|
+
gridRef.current.data.source = res.list;
|
|
194136
|
+
});
|
|
194137
|
+
};
|
|
194138
|
+
|
|
194139
|
+
const handleNaturalLanguageSearch = async () => {
|
|
194140
|
+
const searchTextElement = ninegrid.querySelector("#searchText", tabRef.current);
|
|
194141
|
+
const searchText = searchTextElement ? searchTextElement.value : "";
|
|
194142
|
+
|
|
194143
|
+
if (!gridRef.current) return;
|
|
194144
|
+
gridRef.current.classList.add("loading");
|
|
194145
|
+
|
|
194146
|
+
let params = {};
|
|
194147
|
+
if (searchText) {
|
|
194148
|
+
params = {
|
|
194149
|
+
_whereClause: await ai.generateWhereCause(
|
|
194150
|
+
"tmpla/DocManagerMapper.xml",
|
|
194151
|
+
"selectList",
|
|
194152
|
+
searchText,
|
|
194153
|
+
import.meta.env.VITE_GEMINI_API_KEY
|
|
194154
|
+
),
|
|
194155
|
+
};
|
|
194156
|
+
}
|
|
194157
|
+
selectList(params);
|
|
194158
|
+
};
|
|
194159
|
+
|
|
194160
|
+
const handleClassicSearch = () => {
|
|
194161
|
+
const form2Element = ninegrid.querySelector(".form2", tabRef.current);
|
|
194162
|
+
const params = form2Element ? form2Element.getData() : {};
|
|
194163
|
+
selectList(params);
|
|
194164
|
+
};
|
|
194165
|
+
|
|
194166
|
+
useEffect(() => {
|
|
194167
|
+
selectList({});
|
|
194168
|
+
|
|
194169
|
+
const searchTextElement = ninegrid.querySelector("#searchText", tabRef.current);
|
|
194170
|
+
const searchButton = ninegrid.querySelector(".search", tabRef.current);
|
|
194171
|
+
|
|
194172
|
+
const handleKeyDown = (e) => {
|
|
194173
|
+
if (e.key === "Enter" && !e.isComposing) {
|
|
194174
|
+
handleNaturalLanguageSearch();
|
|
194175
|
+
}
|
|
194176
|
+
};
|
|
194177
|
+
|
|
194178
|
+
const handleClick = () => {
|
|
194179
|
+
handleClassicSearch();
|
|
194180
|
+
};
|
|
194181
|
+
|
|
194182
|
+
if (searchTextElement) {
|
|
194183
|
+
searchTextElement.addEventListener("keydown", handleKeyDown);
|
|
194184
|
+
}
|
|
194185
|
+
if (searchButton) {
|
|
194186
|
+
searchButton.addEventListener("click", handleClick);
|
|
194187
|
+
}
|
|
194188
|
+
|
|
194189
|
+
return () => {
|
|
194190
|
+
if (searchTextElement) {
|
|
194191
|
+
searchTextElement.removeEventListener("keydown", handleKeyDown);
|
|
194192
|
+
}
|
|
194193
|
+
if (searchButton) {
|
|
194194
|
+
searchButton.removeEventListener("click", handleClick);
|
|
194195
|
+
}
|
|
194196
|
+
};
|
|
194197
|
+
}, []);
|
|
194198
|
+
|
|
194199
|
+
return (
|
|
194200
|
+
<div className="wrapper">
|
|
194201
|
+
<nx-collapse target="nx-tab" className="search-collapse"></nx-collapse>
|
|
194202
|
+
<div className="list-wrapper">
|
|
194203
|
+
<nx-tab theme="theme-3" ref={tabRef}>
|
|
194204
|
+
<nx-tab-page caption="자연어 검색">
|
|
194205
|
+
<nx-form className="form1">
|
|
194206
|
+
<input
|
|
194207
|
+
type="text"
|
|
194208
|
+
id="searchText"
|
|
194209
|
+
name="searchText"
|
|
194210
|
+
placeholder="자연어 검색어를 입력하세요 (ex: 작성자가 홍길동인 데이타를 찾아줘)"
|
|
194211
|
+
/>
|
|
194212
|
+
</nx-form>
|
|
194213
|
+
</nx-tab-page>
|
|
194214
|
+
<nx-tab-page caption="클래식 검색">
|
|
194215
|
+
<nx-form className="form2">
|
|
194216
|
+
<label>문서명: <input type="text" name="docNm" /></label>
|
|
194217
|
+
<label>매출액:
|
|
194218
|
+
<input type="number" name="minAmt" placeholder="최소" /> ~
|
|
194219
|
+
<input type="number" name="maxAmt" placeholder="최대" />
|
|
194220
|
+
</label>
|
|
194221
|
+
</nx-form>
|
|
194222
|
+
<button className="search">검색</button>
|
|
194223
|
+
</nx-tab-page>
|
|
194224
|
+
</nx-tab>
|
|
194225
|
+
|
|
194226
|
+
<div className="grid-wrapper">
|
|
194227
|
+
<nine-grid
|
|
194228
|
+
ref={gridRef}
|
|
194229
|
+
caption="문서관리"
|
|
194230
|
+
select-type="row"
|
|
194231
|
+
show-title-bar="true"
|
|
194232
|
+
show-menu-icon="true"
|
|
194233
|
+
show-status-bar="true"
|
|
194234
|
+
enable-fixed-col="true"
|
|
194235
|
+
row-resizable="false"
|
|
194236
|
+
col-movable="true"
|
|
194237
|
+
>
|
|
194238
|
+
<table>
|
|
194239
|
+
<colgroup>
|
|
194240
|
+
<col width="50" fixed="left" background-color="gray" />
|
|
194241
|
+
<col width="100" />
|
|
194242
|
+
<col width="100" />
|
|
194243
|
+
<col width="200" />
|
|
194244
|
+
<col width="120" />
|
|
194245
|
+
<col width="100" />
|
|
194246
|
+
<col width="150" />
|
|
194247
|
+
<col width="150" />
|
|
194248
|
+
</colgroup>
|
|
194249
|
+
<thead>
|
|
194250
|
+
<tr>
|
|
194251
|
+
<th>No.</th>
|
|
194252
|
+
<th>최종수정자</th>
|
|
194253
|
+
<th>문서ID</th>
|
|
194254
|
+
<th>문서명</th>
|
|
194255
|
+
<th>매출액</th>
|
|
194256
|
+
<th>최초등록자</th>
|
|
194257
|
+
<th>최초등록일</th>
|
|
194258
|
+
<th>최종수정일</th>
|
|
194259
|
+
</tr>
|
|
194260
|
+
</thead>
|
|
194261
|
+
<tbody>
|
|
194262
|
+
<tr>
|
|
194263
|
+
<th><ng-row-indicator /></th>
|
|
194264
|
+
<td data-bind="updateUser" text-align="center"></td>
|
|
194265
|
+
<td data-bind="docId" text-align="center"></td>
|
|
194266
|
+
<td data-bind="docNm" text-align="left"></td>
|
|
194267
|
+
<td
|
|
194268
|
+
data-bind="amt"
|
|
194269
|
+
data-expr="data.amt.toLocaleString()"
|
|
194270
|
+
text-align="right"
|
|
194271
|
+
show-icon="true"
|
|
194272
|
+
icon-type="sphere"
|
|
194273
|
+
icon-color="data.amt >= 2000 ? 'red' : 'gray'"
|
|
194274
|
+
></td>
|
|
194275
|
+
<td data-bind="insertUser" text-align="center"></td>
|
|
194276
|
+
<td data-bind="insertDt" text-align="center"></td>
|
|
194277
|
+
<td data-bind="updateDt" text-align="center"></td>
|
|
194278
|
+
</tr>
|
|
194279
|
+
</tbody>
|
|
194280
|
+
</table>
|
|
194281
|
+
</nine-grid>
|
|
194282
|
+
</div>
|
|
194283
|
+
</div>
|
|
194284
|
+
</div>
|
|
194285
|
+
);
|
|
194286
|
+
};
|
|
194287
|
+
|
|
194288
|
+
export default DocManager;
|
|
194289
|
+
`;
|
|
194290
|
+
|
|
194291
|
+
const src2 = `
|
|
194292
|
+
import React, { useRef, useEffect } from 'react';
|
|
194293
|
+
import { api, ai } from "ide-assi";
|
|
194294
|
+
import ninegrid from "ninegrid2";
|
|
194295
|
+
|
|
194296
|
+
const DocManager = () => {
|
|
194297
|
+
const tabRef = useRef(null);
|
|
194298
|
+
const gridRef = useRef(null);
|
|
194299
|
+
|
|
194300
|
+
const toCamelCase = (s) => {
|
|
194301
|
+
return s.toLowerCase().replace(/_([a-z])/g, (g) => g[1].toUpperCase());
|
|
194302
|
+
};
|
|
194303
|
+
|
|
194304
|
+
const tableDefinitions = JSON.parse(\`{"list":[{"columns":[{"COLUMN_NAME":"doc_id","COLUMN_ID":1,"COLUMN_COMMENT":"문서ID"},{"COLUMN_NAME":"doc_nm","COLUMN_ID":2,"COLUMN_COMMENT":"문서명"},{"COLUMN_NAME":"amt","COLUMN_ID":3,"COLUMN_COMMENT":"매출액"},{"COLUMN_NAME":"insert_user","COLUMN_ID":4,"COLUMN_COMMENT":"최초등록자"},{"COLUMN_NAME":"insert_dt","COLUMN_ID":5,"COLUMN_COMMENT":"최초등록일"},{"COLUMN_NAME":"update_user","COLUMN_ID":6,"COLUMN_COMMENT":"최종수정자"},{"COLUMN_NAME":"update_dt","COLUMN_ID":7,"COLUMN_COMMENT":"최종수정일"}],"table":"t_doc"},{"columns":[{"COLUMN_NAME":"file_id","COLUMN_ID":1,"COLUMN_COMMENT":""},{"COLUMN_NAME":"doc_id","COLUMN_ID":2,"COLUMN_COMMENT":""},{"COLUMN_NAME":"download_cnt","COLUMN_ID":3,"COLUMN_COMMENT":""},{"COLUMN_NAME":"file_nm","COLUMN_ID":4,"COLUMN_COMMENT":""},{"COLUMN_NAME":"file_size","COLUMN_ID":5,"COLUMN_COMMENT":""},{"COLUMN_NAME":"file_contents","COLUMN_ID":6,"COLUMN_COMMENT":""},{"COLUMN_NAME":"insert_user","COLUMN_ID":7,"COLUMN_COMMENT":""},{"COLUMN_NAME":"insert_dt","COLUMN_ID":8,"COLUMN_COMMENT":""}],"table":"t_doc_file"},{"columns":[{"COLUMN_NAME":"file_id","COLUMN_ID":1,"COLUMN_COMMENT":""},{"COLUMN_NAME":"page","COLUMN_ID":2,"COLUMN_COMMENT":""},{"COLUMN_NAME":"text","COLUMN_ID":3,"COLUMN_COMMENT":""},{"COLUMN_NAME":"image","COLUMN_ID":4,"COLUMN_COMMENT":""},{"COLUMN_NAME":"vector_text","COLUMN_ID":5,"COLUMN_COMMENT":""},{"COLUMN_NAME":"vector_image","COLUMN_ID":6,"COLUMN_COMMENT":""}],"table":"t_doc_file_page"}]}\`);
|
|
194305
|
+
const mainTableColumns = tableDefinitions.list[0].columns;
|
|
194306
|
+
|
|
194307
|
+
const selectList = async (formData = {}) => {
|
|
194308
|
+
if (!gridRef.current) return;
|
|
194309
|
+
|
|
194310
|
+
gridRef.current.classList.add("loading");
|
|
194311
|
+
/**
|
|
194312
|
+
const form1Data = ninegrid.querySelector(".form1", tabRef.current).getData();
|
|
194313
|
+
const form2Data = ninegrid.querySelector(".form2", tabRef.current).getData();
|
|
194314
|
+
const formData = { ...form1Data, ...form2Data };
|
|
194315
|
+
|
|
194316
|
+
const formData1 = {
|
|
194317
|
+
"_whereClause": await getWhere(),
|
|
194318
|
+
}
|
|
194319
|
+
console.log(formData1);
|
|
194320
|
+
*/
|
|
194321
|
+
|
|
194322
|
+
console.log(formData);
|
|
194323
|
+
|
|
194324
|
+
api.post(\`/api/tmpl-a/doc-manager/selectList\`, formData).then((res) => {
|
|
194325
|
+
gridRef.current.data.source = res.list;
|
|
194326
|
+
});
|
|
194327
|
+
};
|
|
194328
|
+
|
|
194329
|
+
const getWhere = async () => {
|
|
194330
|
+
return await ai.generateWhereCause("tmpla/DocManagerMapper.xml", "selectList", ninegrid.querySelector("#searchText", tabRef.current).value, import.meta.env.VITE_GEMINI_API_KEY);
|
|
194331
|
+
};
|
|
194332
|
+
|
|
194333
|
+
useEffect(() => {
|
|
194334
|
+
selectList();
|
|
194335
|
+
|
|
194336
|
+
const searchTextInput = ninegrid.querySelector("#searchText", tabRef.current);
|
|
194337
|
+
const searchButton = ninegrid.querySelector(".search", tabRef.current);
|
|
194338
|
+
|
|
194339
|
+
const handleSearchTextKeydown = (e) => {
|
|
194340
|
+
if (e.key === 'Enter' && !e.isComposing) {
|
|
194341
|
+
|
|
194342
|
+
//getWhere();
|
|
194343
|
+
getWhere().then(res => {
|
|
194344
|
+
selectList({"_whereClause":res});
|
|
194345
|
+
});
|
|
194346
|
+
}
|
|
194347
|
+
};
|
|
194348
|
+
|
|
194349
|
+
const handleSearchButtonClick = () => {
|
|
194350
|
+
selectList(ninegrid.querySelector(".form2", tabRef.current).getData());
|
|
194351
|
+
};
|
|
194352
|
+
|
|
194353
|
+
if (searchTextInput) {
|
|
194354
|
+
searchTextInput.addEventListener('keydown', handleSearchTextKeydown);
|
|
194355
|
+
}
|
|
194356
|
+
if (searchButton) {
|
|
194357
|
+
searchButton.addEventListener('click', handleSearchButtonClick);
|
|
194358
|
+
}
|
|
194359
|
+
|
|
194360
|
+
return () => {
|
|
194361
|
+
if (searchTextInput) {
|
|
194362
|
+
searchTextInput.removeEventListener('keydown', handleSearchTextKeydown);
|
|
194363
|
+
}
|
|
194364
|
+
if (searchButton) {
|
|
194365
|
+
searchButton.removeEventListener('click', handleSearchButtonClick);
|
|
194366
|
+
}
|
|
194367
|
+
};
|
|
194368
|
+
}, []);
|
|
194369
|
+
|
|
194370
|
+
return (
|
|
194371
|
+
<div className="wrapper">
|
|
194372
|
+
<nx-collapse target="nx-tab" className="search-collapse"></nx-collapse>
|
|
194373
|
+
<div className="list-wrapper">
|
|
194374
|
+
<nx-tab theme="theme-3" ref={tabRef}>
|
|
194375
|
+
<nx-tab-page caption="자연어 검색">
|
|
194376
|
+
<nx-form className="form1">
|
|
194377
|
+
<input type="text" id="searchText" name="searchText"
|
|
194378
|
+
placeholder="자연어 검색어를 입력하세요 (ex: 작성자가 홍길동인 데이타를 찾아줘)"/>
|
|
194379
|
+
</nx-form>
|
|
194380
|
+
</nx-tab-page>
|
|
194381
|
+
<nx-tab-page caption="클래식 검색">
|
|
194382
|
+
<nx-form className="form2">
|
|
194383
|
+
<label>문서ID: <input type="text" name="docId"/></label>
|
|
194384
|
+
<label>문서명: <input type="text" name="docNm"/></label>
|
|
194385
|
+
<label>매출액: <input type="number" name="amt"/></label>
|
|
194386
|
+
<label>최초등록자: <input type="text" name="insertUser"/></label>
|
|
194387
|
+
<label>최초등록일: <input type="text" name="insertDt"/></label>
|
|
194388
|
+
<label>최종수정자: <input type="text" name="updateUser"/></label>
|
|
194389
|
+
<label>최종수정일: <input type="text" name="updateDt"/></label>
|
|
194390
|
+
</nx-form>
|
|
194391
|
+
<button className="search">검색</button>
|
|
194392
|
+
</nx-tab-page>
|
|
194393
|
+
</nx-tab>
|
|
194394
|
+
|
|
194395
|
+
<div className="grid-wrapper">
|
|
194396
|
+
<nine-grid
|
|
194397
|
+
ref={gridRef}
|
|
194398
|
+
caption="문서관리"
|
|
194399
|
+
select-type="row"
|
|
194400
|
+
show-title-bar="true"
|
|
194401
|
+
show-menu-icon="true"
|
|
194402
|
+
show-status-bar="true"
|
|
194403
|
+
enable-fixed-col="true"
|
|
194404
|
+
row-resizable="false"
|
|
194405
|
+
col-movable="true"
|
|
194406
|
+
>
|
|
194407
|
+
<table>
|
|
194408
|
+
<colgroup>
|
|
194409
|
+
<col width="50" fixed="left" background-color="gray"/>
|
|
194410
|
+
{mainTableColumns.map((col) => (
|
|
194411
|
+
<col key={col.COLUMN_ID} width="150"/>
|
|
194412
|
+
))}
|
|
194413
|
+
</colgroup>
|
|
194414
|
+
<thead>
|
|
194415
|
+
<tr>
|
|
194416
|
+
<th>No.</th>
|
|
194417
|
+
{mainTableColumns.map((col) => (
|
|
194418
|
+
<th key={col.COLUMN_ID}>{col.COLUMN_COMMENT || col.COLUMN_NAME}</th>
|
|
194419
|
+
))}
|
|
194420
|
+
</tr>
|
|
194421
|
+
</thead>
|
|
194422
|
+
<tbody>
|
|
194423
|
+
<tr>
|
|
194424
|
+
<th><ng-row-indicator/></th>
|
|
194425
|
+
{mainTableColumns.map((col) => (
|
|
194426
|
+
<td key={col.COLUMN_ID} data-bind={toCamelCase(col.COLUMN_NAME)}></td>
|
|
194427
|
+
))}
|
|
194428
|
+
</tr>
|
|
194429
|
+
</tbody>
|
|
194430
|
+
</table>
|
|
194431
|
+
</nine-grid>
|
|
194432
|
+
</div>
|
|
194433
|
+
</div>
|
|
194434
|
+
</div>
|
|
194435
|
+
);
|
|
194436
|
+
};
|
|
194437
|
+
|
|
194438
|
+
export default DocManager;
|
|
194439
|
+
`;
|
|
194440
|
+
|
|
194441
|
+
this.shadowRoot.querySelector("ide-diff-popup").popup(src1, src2);
|
|
194123
194442
|
|
|
194124
194443
|
return;
|
|
194125
194444
|
}
|
|
@@ -196577,26 +196896,59 @@ class IdeDiff extends HTMLElement
|
|
|
196577
196896
|
{
|
|
196578
196897
|
#asisSrc;
|
|
196579
196898
|
#tobeSrc;
|
|
196899
|
+
#asisPre; // <pre> 요소 참조 저장
|
|
196900
|
+
#tobePre; // <pre> 요소 참조 저장
|
|
196580
196901
|
|
|
196581
196902
|
constructor() {
|
|
196582
196903
|
super();
|
|
196583
196904
|
this.attachShadow({ mode: 'open' });
|
|
196584
196905
|
}
|
|
196585
|
-
|
|
196586
|
-
connectedCallback() {
|
|
196587
196906
|
|
|
196907
|
+
connectedCallback() {
|
|
196588
196908
|
this.shadowRoot.innerHTML = `
|
|
196589
|
-
|
|
196590
|
-
|
|
196591
|
-
|
|
196592
|
-
|
|
196593
|
-
|
|
196594
|
-
|
|
196595
|
-
|
|
196596
|
-
|
|
196597
|
-
|
|
196598
|
-
|
|
196599
|
-
|
|
196909
|
+
<style>
|
|
196910
|
+
/* 전역 또는 컴포넌트 스코프에서 box-sizing: border-box; 적용 권장 */
|
|
196911
|
+
* {
|
|
196912
|
+
box-sizing: border-box;
|
|
196913
|
+
}
|
|
196914
|
+
@import "https://cdn.jsdelivr.net/npm/ninegrid@${ninegrid.version}/dist/css/ideDiff.css";
|
|
196915
|
+
${ninegrid.getCustomPath(this,"ideDiff.css")}
|
|
196916
|
+
|
|
196917
|
+
/* 추가 CSS (필요 시) */
|
|
196918
|
+
.wrapper {
|
|
196919
|
+
display: flex; /* 스플리터와 함께 작동하도록 flex 컨테이너 */
|
|
196920
|
+
height: 100%; /* 부모의 높이를 채우도록 설정 */
|
|
196921
|
+
overflow: hidden; /* 내부 스크롤을 위해 overflow 처리 */
|
|
196922
|
+
}
|
|
196923
|
+
.panel {
|
|
196924
|
+
flex: 1; /* 패널들이 남은 공간을 채우도록 */
|
|
196925
|
+
overflow: auto; /* 각 패널 내부에서 스크롤 가능하게 */
|
|
196926
|
+
position: relative; /* 자식 요소의 absolute 포지셔닝 기준 */
|
|
196927
|
+
}
|
|
196928
|
+
.panel pre {
|
|
196929
|
+
margin: 0; /* pre 태그의 기본 마진 제거 */
|
|
196930
|
+
padding: 10px; /* 코드 가독성을 위한 내부 패딩 */
|
|
196931
|
+
white-space: pre-wrap; /* 긴 줄 자동 줄바꿈 */
|
|
196932
|
+
word-break: break-all; /* 단어가 길어도 강제 줄바꿈 */
|
|
196933
|
+
/* white-space: pre-wrap; 을 사용하면 가로 스크롤 없이 줄바꿈됩니다.
|
|
196934
|
+
만약 가로 스크롤을 원하면 white-space: pre; 로 두고 overflow-x: auto; 를 panel에 추가 */
|
|
196935
|
+
}
|
|
196936
|
+
ins {
|
|
196937
|
+
background-color: #d4edda; /* 삽입된 텍스트 배경색 (예시) */
|
|
196938
|
+
text-decoration: none; /* 밑줄 제거 (일반적으로) */
|
|
196939
|
+
}
|
|
196940
|
+
del {
|
|
196941
|
+
background-color: #f8d7da; /* 삭제된 텍스트 배경색 (예시) */
|
|
196942
|
+
text-decoration: none; /* 취소선 제거 (일반적으로) */
|
|
196943
|
+
}
|
|
196944
|
+
</style>
|
|
196945
|
+
|
|
196946
|
+
<div class="wrapper">
|
|
196947
|
+
<div class="panel asis"><pre></pre></div>
|
|
196948
|
+
<nx-splitter></nx-splitter>
|
|
196949
|
+
<div class="panel tobe"><pre></pre></div>
|
|
196950
|
+
</div>
|
|
196951
|
+
`;
|
|
196600
196952
|
|
|
196601
196953
|
requestAnimationFrame(() => {
|
|
196602
196954
|
this.#init();
|
|
@@ -196604,38 +196956,71 @@ class IdeDiff extends HTMLElement
|
|
|
196604
196956
|
};
|
|
196605
196957
|
|
|
196606
196958
|
#init = () => {
|
|
196959
|
+
// 패널 pre 요소에 대한 참조를 저장
|
|
196960
|
+
this.#asisPre = this.shadowRoot.querySelector('.asis pre');
|
|
196961
|
+
this.#tobePre = this.shadowRoot.querySelector('.tobe pre');
|
|
196962
|
+
|
|
196963
|
+
// 스크롤 동기화 이벤트 리스너 추가
|
|
196964
|
+
this.#asisPre.addEventListener('scroll', this.#syncScroll);
|
|
196965
|
+
this.#tobePre.addEventListener('scroll', this.#syncScroll);
|
|
196966
|
+
};
|
|
196967
|
+
|
|
196968
|
+
#syncScroll = (e) => {
|
|
196969
|
+
// 스크롤 이벤트 발생 시 다른 패널의 스크롤 위치를 동기화
|
|
196970
|
+
// `e.target`이 스크롤 이벤트를 발생시킨 요소
|
|
196971
|
+
if (e.target === this.#asisPre) {
|
|
196972
|
+
this.#tobePre.scrollTop = this.#asisPre.scrollTop;
|
|
196973
|
+
} else if (e.target === this.#tobePre) {
|
|
196974
|
+
this.#asisPre.scrollTop = this.#tobePre.scrollTop;
|
|
196975
|
+
}
|
|
196607
196976
|
};
|
|
196608
196977
|
|
|
196609
196978
|
#renderDiff = () => {
|
|
196610
196979
|
const dmp = new diffMatchPatchExports.diff_match_patch();
|
|
196611
|
-
|
|
196980
|
+
// 공백 문자를 정규화하여 diff 정확도 향상 (선택 사항)
|
|
196981
|
+
const cleanedAsisSrc = this.#asisSrc.replace(/\r\n/g, '\n');
|
|
196982
|
+
const cleanedTobeSrc = this.#tobeSrc.replace(/\r\n/g, '\n');
|
|
196983
|
+
|
|
196984
|
+
const diffs = dmp.diff_main(cleanedAsisSrc, cleanedTobeSrc);
|
|
196612
196985
|
dmp.diff_cleanupSemantic(diffs);
|
|
196613
196986
|
|
|
196614
196987
|
const asisHtml = [];
|
|
196615
196988
|
const tobeHtml = [];
|
|
196616
196989
|
|
|
196617
196990
|
for (const [op, text] of diffs) {
|
|
196991
|
+
// HTML 이스케이프 처리: <, >, & 등 특수문자 변환
|
|
196992
|
+
const escapedText = text
|
|
196993
|
+
.replace(/&/g, '&')
|
|
196994
|
+
.replace(/</g, '<')
|
|
196995
|
+
.replace(/>/g, '>');
|
|
196996
|
+
|
|
196618
196997
|
switch (op) {
|
|
196619
196998
|
case diffMatchPatchExports.diff_match_patch.DIFF_INSERT:
|
|
196620
|
-
tobeHtml.push(`<ins>${
|
|
196999
|
+
tobeHtml.push(`<ins>${escapedText}</ins>`);
|
|
197000
|
+
// 삭제된 내용이 없으므로 asis에는 공백을 유지 (줄 맞춤 고려)
|
|
197001
|
+
// 만약 줄 단위로 완벽하게 매칭하려면 여기에 다른 로직 필요
|
|
196621
197002
|
break;
|
|
196622
197003
|
case diffMatchPatchExports.diff_match_patch.DIFF_DELETE:
|
|
196623
|
-
asisHtml.push(`<del>${
|
|
197004
|
+
asisHtml.push(`<del>${escapedText}</del>`);
|
|
197005
|
+
// 추가된 내용이 없으므로 tobe에는 공백을 유지
|
|
196624
197006
|
break;
|
|
196625
197007
|
case diffMatchPatchExports.diff_match_patch.DIFF_EQUAL:
|
|
196626
|
-
asisHtml.push(`<span>${
|
|
196627
|
-
tobeHtml.push(`<span>${
|
|
197008
|
+
asisHtml.push(`<span>${escapedText}</span>`);
|
|
197009
|
+
tobeHtml.push(`<span>${escapedText}</span>`);
|
|
196628
197010
|
break;
|
|
196629
197011
|
}
|
|
196630
197012
|
}
|
|
196631
197013
|
|
|
196632
|
-
|
|
196633
|
-
|
|
197014
|
+
// 중요한 부분: `innerHTML` 대신 `textContent`를 사용하면
|
|
197015
|
+
// `diff-match-patch`의 출력에 따라 HTML 태그가 그대로 보이므로,
|
|
197016
|
+
// 여기서는 `innerHTML`을 사용하되, 텍스트는 `escapedText`로 처리하여 XSS 방지.
|
|
197017
|
+
this.#asisPre.innerHTML = asisHtml.join('');
|
|
197018
|
+
this.#tobePre.innerHTML = tobeHtml.join('');
|
|
196634
197019
|
};
|
|
196635
197020
|
|
|
196636
197021
|
|
|
196637
197022
|
initialize = (src1, src2) => {
|
|
196638
|
-
console.log(src1, src2);
|
|
197023
|
+
console.log("Initializing IdeDiff with sources:", src1, src2);
|
|
196639
197024
|
|
|
196640
197025
|
this.#asisSrc = src1;
|
|
196641
197026
|
this.#tobeSrc = src2;
|