@kodocagent/cli 0.4.0 → 0.4.1
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/index.js +1216 -105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9739,9 +9739,9 @@ var require_load = __commonJS({
|
|
|
9739
9739
|
var require_lib3 = __commonJS({
|
|
9740
9740
|
"../../node_modules/.pnpm/jszip@3.10.1/node_modules/jszip/lib/index.js"(exports, module) {
|
|
9741
9741
|
"use strict";
|
|
9742
|
-
function
|
|
9743
|
-
if (!(this instanceof
|
|
9744
|
-
return new
|
|
9742
|
+
function JSZip5() {
|
|
9743
|
+
if (!(this instanceof JSZip5)) {
|
|
9744
|
+
return new JSZip5();
|
|
9745
9745
|
}
|
|
9746
9746
|
if (arguments.length) {
|
|
9747
9747
|
throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
|
|
@@ -9750,7 +9750,7 @@ var require_lib3 = __commonJS({
|
|
|
9750
9750
|
this.comment = null;
|
|
9751
9751
|
this.root = "";
|
|
9752
9752
|
this.clone = function() {
|
|
9753
|
-
var newObj = new
|
|
9753
|
+
var newObj = new JSZip5();
|
|
9754
9754
|
for (var i in this) {
|
|
9755
9755
|
if (typeof this[i] !== "function") {
|
|
9756
9756
|
newObj[i] = this[i];
|
|
@@ -9759,16 +9759,16 @@ var require_lib3 = __commonJS({
|
|
|
9759
9759
|
return newObj;
|
|
9760
9760
|
};
|
|
9761
9761
|
}
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
return new
|
|
9762
|
+
JSZip5.prototype = require_object();
|
|
9763
|
+
JSZip5.prototype.loadAsync = require_load();
|
|
9764
|
+
JSZip5.support = require_support();
|
|
9765
|
+
JSZip5.defaults = require_defaults();
|
|
9766
|
+
JSZip5.version = "3.10.1";
|
|
9767
|
+
JSZip5.loadAsync = function(content, options) {
|
|
9768
|
+
return new JSZip5().loadAsync(content, options);
|
|
9769
9769
|
};
|
|
9770
|
-
|
|
9771
|
-
module.exports =
|
|
9770
|
+
JSZip5.external = require_external();
|
|
9771
|
+
module.exports = JSZip5;
|
|
9772
9772
|
}
|
|
9773
9773
|
});
|
|
9774
9774
|
|
|
@@ -10803,6 +10803,7 @@ import { copyFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile3,
|
|
|
10803
10803
|
import { basename as basename2, dirname as dirname22, extname, join as join22 } from "path";
|
|
10804
10804
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
10805
10805
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
10806
|
+
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
10806
10807
|
import { createTwoFilesPatch } from "diff";
|
|
10807
10808
|
import { readFile as readFile22 } from "fs/promises";
|
|
10808
10809
|
import { blocksToMarkdown, compare } from "@clazic/kordoc";
|
|
@@ -10833,26 +10834,35 @@ import {
|
|
|
10833
10834
|
} from "docx";
|
|
10834
10835
|
import { readFile as readFile6 } from "fs/promises";
|
|
10835
10836
|
import { extname as extname6 } from "path";
|
|
10836
|
-
import {
|
|
10837
|
+
import { parse as parse2 } from "@clazic/kordoc";
|
|
10837
10838
|
import { z as z6 } from "zod";
|
|
10838
10839
|
import { readFile as readFile7 } from "fs/promises";
|
|
10839
10840
|
import { extname as extname7 } from "path";
|
|
10840
|
-
import
|
|
10841
|
+
import { extractFormFields, markdownToHwpx as markdownToHwpx2, parse as parse3 } from "@clazic/kordoc";
|
|
10842
|
+
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
10841
10843
|
import { z as z7 } from "zod";
|
|
10842
|
-
import { readFile as readFile8
|
|
10844
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
10843
10845
|
import { extname as extname8 } from "path";
|
|
10844
|
-
import
|
|
10846
|
+
import ExcelJS from "exceljs";
|
|
10845
10847
|
import { z as z8 } from "zod";
|
|
10846
|
-
import { readFile as
|
|
10848
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
10849
|
+
import { extname as extname9 } from "path";
|
|
10850
|
+
import { parse as parse4 } from "@clazic/kordoc";
|
|
10847
10851
|
import { z as z9 } from "zod";
|
|
10852
|
+
import { readFile as readFile10, stat as stat3 } from "fs/promises";
|
|
10853
|
+
import { extname as extname10 } from "path";
|
|
10854
|
+
import { parse as parse5 } from "@clazic/kordoc";
|
|
10855
|
+
import { z as z10 } from "zod";
|
|
10856
|
+
import { readFile as fsReadFile, stat as stat4 } from "fs/promises";
|
|
10857
|
+
import { z as z11 } from "zod";
|
|
10848
10858
|
import { stat as stat5 } from "fs/promises";
|
|
10849
|
-
import { extname as
|
|
10859
|
+
import { extname as extname11 } from "path";
|
|
10850
10860
|
import { markdownToHwpx as markdownToHwpx3 } from "@clazic/kordoc";
|
|
10851
|
-
import { z as
|
|
10861
|
+
import { z as z12 } from "zod";
|
|
10852
10862
|
import { stat as stat6 } from "fs/promises";
|
|
10853
|
-
import { extname as
|
|
10863
|
+
import { extname as extname12 } from "path";
|
|
10854
10864
|
import ExcelJS2 from "exceljs";
|
|
10855
|
-
import { z as
|
|
10865
|
+
import { z as z13 } from "zod";
|
|
10856
10866
|
async function resolveSafePath(cwd, p) {
|
|
10857
10867
|
const normalizedCwd = normalize(cwd).normalize("NFC");
|
|
10858
10868
|
const normalizedP = p.normalize("NFC");
|
|
@@ -11236,7 +11246,7 @@ function applyFormObjectEdits(xml, edits) {
|
|
|
11236
11246
|
};
|
|
11237
11247
|
continue;
|
|
11238
11248
|
}
|
|
11239
|
-
const
|
|
11249
|
+
const _openTag = xml.slice(pos, openTagEnd + 1);
|
|
11240
11250
|
const closeTag = `</${xmlTag}>`;
|
|
11241
11251
|
const closeIdx = xml.indexOf(closeTag, openTagEnd);
|
|
11242
11252
|
if (closeIdx < 0) {
|
|
@@ -11433,7 +11443,7 @@ function replaceAttrInOpenTag(xml, tagStart, tagEnd, attr, newValue) {
|
|
|
11433
11443
|
text: newValue
|
|
11434
11444
|
};
|
|
11435
11445
|
}
|
|
11436
|
-
function replaceEditText(
|
|
11446
|
+
function replaceEditText(_xml, pos, elementContent, newText) {
|
|
11437
11447
|
const escaped = escapeXml(newText);
|
|
11438
11448
|
const selfCloseRe = /<hp:text\s*\/>/;
|
|
11439
11449
|
const scm = selfCloseRe.exec(elementContent);
|
|
@@ -11837,39 +11847,22 @@ var listFilesTool = {
|
|
|
11837
11847
|
return lines.join("\n") + truncateNotice;
|
|
11838
11848
|
}
|
|
11839
11849
|
};
|
|
11840
|
-
var
|
|
11841
|
-
tableIndex: z4.number().int().nonnegative().describe(
|
|
11842
|
-
"0-based \uD45C \uC778\uB371\uC2A4. read_document\uB85C \uC77D\uC740 kordoc \uBE14\uB85D \uC21C\uC11C\uC640 \uB3D9\uC77C (\uC911\uCCA9 \uD45C\uB294 \uBCC4\uB3C4 \uCE74\uC6B4\uD305 \uC548 \uD568)"
|
|
11843
|
-
),
|
|
11844
|
-
row: z4.number().int().nonnegative().describe("\uC140\uC758 rowAddr (0-based, <hp:cellAddr rowAddr=..> \uAC12)"),
|
|
11845
|
-
col: z4.number().int().nonnegative().describe("\uC140\uC758 colAddr (0-based, <hp:cellAddr colAddr=..> \uAC12)"),
|
|
11846
|
-
newText: z4.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
|
|
11847
|
-
expectedText: z4.string().optional().describe(
|
|
11848
|
-
"\uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8 (\uC548\uC804 \uAC80\uC99D\uC6A9). \uC81C\uACF5 \uC2DC \uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC218\uC815\uD558\uC9C0 \uC54A\uC74C. \uC798\uBABB\uB41C \uC140 \uC218\uC815\uC744 \uBC29\uC9C0\uD558\uB824\uBA74 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uD558\uC138\uC694."
|
|
11849
|
-
),
|
|
11850
|
-
// label 필드 없음 — 좌표 모드 전용
|
|
11851
|
-
label: z4.undefined().optional()
|
|
11852
|
-
});
|
|
11853
|
-
var labelCellEditItemSchema = z4.object({
|
|
11854
|
-
label: z4.string().describe(
|
|
11855
|
-
"\uB808\uC774\uBE14 \uC140\uC758 \uD14D\uC2A4\uD2B8 (\uD2B8\uB9BC \uBE44\uAD50). \uC774 \uC140\uC758 \uC778\uC811 \uBC29\uD5A5(direction)\uC5D0 \uC788\uB294 \uC140\uC5D0 newText\uB97C \uAE30\uB85D\uD568."
|
|
11856
|
-
),
|
|
11857
|
-
direction: z4.enum(["right", "below"]).optional().default("right").describe(
|
|
11858
|
-
"\uB808\uC774\uBE14 \uAE30\uC900 \uB300\uC0C1 \uC140 \uBC29\uD5A5. right(\uAE30\uBCF8): \uC624\uB978\uCABD \uC140, below: \uC544\uB798 \uC140. \uBCD1\uD569 \uC140\uC740 span\uC744 \uACE0\uB824\uD558\uC5EC \uB300\uC0C1 \uC140 \uC8FC\uC18C\uB97C \uACC4\uC0B0\uD569\uB2C8\uB2E4."
|
|
11859
|
-
),
|
|
11850
|
+
var cellEditItemSchema = z4.object({
|
|
11860
11851
|
tableIndex: z4.number().int().nonnegative().optional().describe(
|
|
11861
|
-
"\
|
|
11852
|
+
"\uC88C\uD45C \uBAA8\uB4DC: 0-based \uD45C \uC778\uB371\uC2A4(read_document\uC758 kordoc \uBE14\uB85D \uC21C\uC11C, \uC911\uCCA9\uD45C \uC81C\uC678). \uB808\uC774\uBE14 \uBAA8\uB4DC\uC5D0\uC120 \uD0D0\uC0C9 \uBC94\uC704 \uC81C\uD55C\uC6A9(\uC120\uD0DD)."
|
|
11853
|
+
),
|
|
11854
|
+
row: z4.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 rowAddr (0-based)"),
|
|
11855
|
+
col: z4.number().int().nonnegative().optional().describe("\uC88C\uD45C \uBAA8\uB4DC: \uC140\uC758 colAddr (0-based)"),
|
|
11856
|
+
label: z4.string().optional().describe(
|
|
11857
|
+
"\uB808\uC774\uBE14 \uBAA8\uB4DC: \uAE30\uC900 \uC140 \uD14D\uC2A4\uD2B8(\uD2B8\uB9BC \uBE44\uAD50). \uC774 \uC140\uC758 direction \uBC29\uD5A5 \uC778\uC811 \uC140\uC5D0 newText\uB97C \uAE30\uB85D. \uC88C\uD45C \uBAA8\uB4DC\uBA74 \uC0DD\uB7B5."
|
|
11862
11858
|
),
|
|
11859
|
+
direction: z4.enum(["right", "below"]).optional().describe("\uB808\uC774\uBE14 \uBAA8\uB4DC \uBC29\uD5A5. \uAE30\uBCF8 right(\uC624\uB978\uCABD \uC140), below(\uC544\uB798 \uC140). \uBCD1\uD569 span \uACE0\uB824."),
|
|
11863
11860
|
newText: z4.string().describe("\uC140\uC5D0 \uC4F8 \uC0C8 \uD14D\uC2A4\uD2B8"),
|
|
11864
11861
|
expectedText: z4.string().optional().describe(
|
|
11865
|
-
"\uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8
|
|
11866
|
-
)
|
|
11867
|
-
|
|
11868
|
-
|
|
11869
|
-
col: z4.undefined().optional()
|
|
11870
|
-
});
|
|
11871
|
-
var cellEditItemSchema = z4.union([coordinateCellEditItemSchema, labelCellEditItemSchema]).describe(
|
|
11872
|
-
"\uD3B8\uC9D1 \uD56D\uBAA9: \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label+direction) \uC911 \uD558\uB098 \uC0AC\uC6A9. \uB450 \uBAA8\uB4DC\uB97C \uB3D9\uC2DC\uC5D0 \uC9C0\uC815\uD558\uAC70\uB098 \uB458 \uB2E4 \uC0DD\uB7B5\uD558\uBA74 \uC720\uD6A8\uC131 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD569\uB2C8\uB2E4."
|
|
11862
|
+
"\uD604\uC7AC \uC140 \uD14D\uC2A4\uD2B8(\uC548\uC804 \uAC80\uC99D\uC6A9). \uBD88\uC77C\uCE58 \uC2DC \uC218\uC815\uD558\uC9C0 \uC54A\uC74C. \uC798\uBABB\uB41C \uC140 \uC218\uC815 \uBC29\uC9C0\uB97C \uC704\uD574 \uAD8C\uC7A5."
|
|
11863
|
+
)
|
|
11864
|
+
}).describe(
|
|
11865
|
+
"\uD3B8\uC9D1 \uD56D\uBAA9. \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label[+direction]) \uC911 \uD558\uB098\uB97C \uC0AC\uC6A9\uD558\uC138\uC694. \uB458 \uB2E4 \uC9C0\uC815\uD558\uAC70\uB098 \uB458 \uB2E4 \uC0DD\uB7B5\uD558\uBA74 \uC624\uB958\uC785\uB2C8\uB2E4."
|
|
11873
11866
|
);
|
|
11874
11867
|
var proposeCellEditSchema = z4.object({
|
|
11875
11868
|
path: z4.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
@@ -12337,7 +12330,11 @@ var proposeCellEditTool = {
|
|
|
12337
12330
|
for (let i = 0; i < input.edits.length; i++) {
|
|
12338
12331
|
const e = input.edits[i];
|
|
12339
12332
|
if (!e) continue;
|
|
12340
|
-
if (
|
|
12333
|
+
if (e.label !== void 0 && (e.row !== void 0 || e.col !== void 0)) {
|
|
12334
|
+
resolveErrors.push(
|
|
12335
|
+
`\uD3B8\uC9D1 #${i + 1}: label\uACFC row/col\uC744 \uB3D9\uC2DC\uC5D0 \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label) \uC911 \uD558\uB098\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694.`
|
|
12336
|
+
);
|
|
12337
|
+
} else if (e.label !== void 0) {
|
|
12341
12338
|
const direction = e.direction ?? "right";
|
|
12342
12339
|
const resolved = resolveLabelAcrossSections(e.label, direction, e.tableIndex);
|
|
12343
12340
|
if ("error" in resolved) {
|
|
@@ -12352,15 +12349,18 @@ var proposeCellEditTool = {
|
|
|
12352
12349
|
label: e.label
|
|
12353
12350
|
});
|
|
12354
12351
|
}
|
|
12355
|
-
} else {
|
|
12356
|
-
const coord = e;
|
|
12352
|
+
} else if (e.tableIndex !== void 0 && e.row !== void 0 && e.col !== void 0) {
|
|
12357
12353
|
resolvedEdits.push({
|
|
12358
|
-
tableIndex:
|
|
12359
|
-
row:
|
|
12360
|
-
col:
|
|
12361
|
-
newText:
|
|
12362
|
-
expectedText:
|
|
12354
|
+
tableIndex: e.tableIndex,
|
|
12355
|
+
row: e.row,
|
|
12356
|
+
col: e.col,
|
|
12357
|
+
newText: e.newText,
|
|
12358
|
+
expectedText: e.expectedText
|
|
12363
12359
|
});
|
|
12360
|
+
} else {
|
|
12361
|
+
resolveErrors.push(
|
|
12362
|
+
`\uD3B8\uC9D1 #${i + 1}: \uC88C\uD45C \uBAA8\uB4DC(tableIndex+row+col) \uB610\uB294 \uB808\uC774\uBE14 \uBAA8\uB4DC(label) \uC911 \uD558\uB098\uB97C \uC644\uC804\uD788 \uC9C0\uC815\uD558\uC138\uC694. read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`
|
|
12363
|
+
);
|
|
12364
12364
|
}
|
|
12365
12365
|
}
|
|
12366
12366
|
if (resolveErrors.length > 0) {
|
|
@@ -12631,11 +12631,245 @@ ${diff}`;
|
|
|
12631
12631
|
};
|
|
12632
12632
|
}
|
|
12633
12633
|
};
|
|
12634
|
-
var
|
|
12635
|
-
path: z6.string().describe("\
|
|
12636
|
-
|
|
12634
|
+
var proposeFindReplaceSchema = z6.object({
|
|
12635
|
+
path: z6.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
12636
|
+
find: z6.string().min(1).describe("\uCC3E\uC744 \uD14D\uC2A4\uD2B8"),
|
|
12637
|
+
replace: z6.string().describe("\uBC14\uAFC0 \uD14D\uC2A4\uD2B8"),
|
|
12638
|
+
caseSensitive: z6.boolean().optional().default(false).describe("\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84 (\uAE30\uBCF8\uAC12: false)"),
|
|
12639
|
+
all: z6.boolean().optional().default(true).describe("\uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uAD50\uCCB4\uD560\uC9C0 \uC5EC\uBD80 (\uAE30\uBCF8\uAC12: true). false\uC774\uBA74 \uCCAB \uBC88\uC9F8 \uB9E4\uCE58\uB9CC \uAD50\uCCB4"),
|
|
12637
12640
|
summary: z6.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12638
12641
|
});
|
|
12642
|
+
function escapeXml3(text3) {
|
|
12643
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12644
|
+
}
|
|
12645
|
+
function replaceInSectionXml(xml, find, replace, caseSensitive, replaceAll, alreadyReplaced) {
|
|
12646
|
+
if (!replaceAll && alreadyReplaced > 0) {
|
|
12647
|
+
return { xml, count: 0 };
|
|
12648
|
+
}
|
|
12649
|
+
const escapedFind = escapeXml3(find);
|
|
12650
|
+
const escapedReplace = escapeXml3(replace);
|
|
12651
|
+
if (escapedFind.length === 0) {
|
|
12652
|
+
return { xml, count: 0 };
|
|
12653
|
+
}
|
|
12654
|
+
const normFind = caseSensitive ? escapedFind : escapedFind.toLowerCase();
|
|
12655
|
+
const normReplace = escapedReplace;
|
|
12656
|
+
let totalCount = 0;
|
|
12657
|
+
let result = xml;
|
|
12658
|
+
const tNodeRe = /<hp:t>([\s\S]*?)<\/hp:t>/g;
|
|
12659
|
+
let offset = 0;
|
|
12660
|
+
let m = tNodeRe.exec(xml);
|
|
12661
|
+
while (m !== null) {
|
|
12662
|
+
const content = m[1];
|
|
12663
|
+
const nodeResult = replaceInContent(
|
|
12664
|
+
content,
|
|
12665
|
+
normFind,
|
|
12666
|
+
normReplace,
|
|
12667
|
+
caseSensitive,
|
|
12668
|
+
replaceAll,
|
|
12669
|
+
alreadyReplaced + totalCount
|
|
12670
|
+
);
|
|
12671
|
+
if (nodeResult.count > 0) {
|
|
12672
|
+
const openTagLen = "<hp:t>".length;
|
|
12673
|
+
const contentStart = m.index + offset + openTagLen;
|
|
12674
|
+
const contentEnd = contentStart + content.length;
|
|
12675
|
+
result = result.substring(0, contentStart) + nodeResult.text + result.substring(contentEnd);
|
|
12676
|
+
offset += nodeResult.text.length - content.length;
|
|
12677
|
+
totalCount += nodeResult.count;
|
|
12678
|
+
if (!replaceAll && alreadyReplaced + totalCount >= 1) {
|
|
12679
|
+
break;
|
|
12680
|
+
}
|
|
12681
|
+
}
|
|
12682
|
+
m = tNodeRe.exec(xml);
|
|
12683
|
+
}
|
|
12684
|
+
return { xml: result, count: totalCount };
|
|
12685
|
+
}
|
|
12686
|
+
function replaceInContent(content, normFind, escapedReplace, caseSensitive, replaceAll, alreadyReplaced) {
|
|
12687
|
+
const searchIn = caseSensitive ? content : content.toLowerCase();
|
|
12688
|
+
const findLen = normFind.length;
|
|
12689
|
+
if (findLen === 0) return { text: content, count: 0 };
|
|
12690
|
+
let result = "";
|
|
12691
|
+
let pos = 0;
|
|
12692
|
+
let count = 0;
|
|
12693
|
+
while (pos <= content.length) {
|
|
12694
|
+
if (!replaceAll && alreadyReplaced + count >= 1) {
|
|
12695
|
+
result += content.substring(pos);
|
|
12696
|
+
break;
|
|
12697
|
+
}
|
|
12698
|
+
const idx = searchIn.indexOf(normFind, pos);
|
|
12699
|
+
if (idx === -1) {
|
|
12700
|
+
result += content.substring(pos);
|
|
12701
|
+
break;
|
|
12702
|
+
}
|
|
12703
|
+
result += content.substring(pos, idx) + escapedReplace;
|
|
12704
|
+
pos = idx + findLen;
|
|
12705
|
+
count++;
|
|
12706
|
+
}
|
|
12707
|
+
return { text: result, count };
|
|
12708
|
+
}
|
|
12709
|
+
function countOccurrences(str, sub) {
|
|
12710
|
+
if (sub.length === 0) return 0;
|
|
12711
|
+
let count = 0;
|
|
12712
|
+
let pos = 0;
|
|
12713
|
+
while (true) {
|
|
12714
|
+
const idx = str.indexOf(sub, pos);
|
|
12715
|
+
if (idx === -1) break;
|
|
12716
|
+
count++;
|
|
12717
|
+
pos = idx + sub.length;
|
|
12718
|
+
}
|
|
12719
|
+
return count;
|
|
12720
|
+
}
|
|
12721
|
+
async function applyFindReplaceToHwpx(hwpxBuffer, find, replace, caseSensitive, replaceAll) {
|
|
12722
|
+
const zip = await import_jszip3.default.loadAsync(hwpxBuffer);
|
|
12723
|
+
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
12724
|
+
const sectionXmls = [];
|
|
12725
|
+
for (const sf of sectionFiles) {
|
|
12726
|
+
const entry = zip.file(sf);
|
|
12727
|
+
const xml = entry ? await entry.async("string") : "";
|
|
12728
|
+
sectionXmls.push(xml);
|
|
12729
|
+
}
|
|
12730
|
+
let totalCount = 0;
|
|
12731
|
+
const newSectionXmls = [];
|
|
12732
|
+
for (let si = 0; si < sectionFiles.length; si++) {
|
|
12733
|
+
const srcXml = sectionXmls[si] ?? "";
|
|
12734
|
+
const { xml: newXml, count } = replaceInSectionXml(
|
|
12735
|
+
srcXml,
|
|
12736
|
+
find,
|
|
12737
|
+
replace,
|
|
12738
|
+
caseSensitive,
|
|
12739
|
+
replaceAll,
|
|
12740
|
+
totalCount
|
|
12741
|
+
);
|
|
12742
|
+
newSectionXmls.push(newXml);
|
|
12743
|
+
totalCount += count;
|
|
12744
|
+
if (!replaceAll && totalCount >= 1) {
|
|
12745
|
+
for (let j = si + 1; j < sectionFiles.length; j++) {
|
|
12746
|
+
newSectionXmls.push(sectionXmls[j] ?? "");
|
|
12747
|
+
}
|
|
12748
|
+
break;
|
|
12749
|
+
}
|
|
12750
|
+
}
|
|
12751
|
+
if (totalCount === 0) {
|
|
12752
|
+
return { buffer: hwpxBuffer, count: 0 };
|
|
12753
|
+
}
|
|
12754
|
+
const out = new import_jszip3.default();
|
|
12755
|
+
const mimetypeEntry = zip.file("mimetype");
|
|
12756
|
+
if (mimetypeEntry) {
|
|
12757
|
+
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
12758
|
+
}
|
|
12759
|
+
for (const [name, entry] of Object.entries(zip.files)) {
|
|
12760
|
+
if (name === "mimetype" || entry.dir) continue;
|
|
12761
|
+
const sectionIdx = sectionFiles.indexOf(name);
|
|
12762
|
+
if (sectionIdx >= 0) {
|
|
12763
|
+
out.file(name, newSectionXmls[sectionIdx] ?? "");
|
|
12764
|
+
} else {
|
|
12765
|
+
out.file(name, await entry.async("uint8array"));
|
|
12766
|
+
}
|
|
12767
|
+
}
|
|
12768
|
+
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
12769
|
+
return {
|
|
12770
|
+
buffer: new Uint8Array(buf),
|
|
12771
|
+
count: totalCount
|
|
12772
|
+
};
|
|
12773
|
+
}
|
|
12774
|
+
var proposeFindReplaceTool = {
|
|
12775
|
+
name: "propose_find_replace",
|
|
12776
|
+
description: "HWPX \uBB38\uC11C \uC804\uCCB4\uC5D0\uC11C \uD14D\uC2A4\uD2B8\uB97C \uCC3E\uC544 \uBC14\uAFC9\uB2C8\uB2E4. \uBCF8\uBB38\xB7\uD45C\xB7\uBA38\uB9AC\uB9D0\xB7\uAF2C\uB9AC\uB9D0 \uB4F1 \uBAA8\uB4E0 \uC139\uC158\uC744 \uB300\uC0C1\uC73C\uB85C \uBB38\uC11C XML\uC744 \uC9C1\uC811 \uD328\uCE58\uD569\uB2C8\uB2E4(rhwp \uC5D4\uC9C4 \uBBF8\uC0AC\uC6A9). \uC774\uBBF8\uC9C0\xB7\uD45C\xB7\uB808\uC774\uC544\uC6C3 \uB4F1 \uBB38\uC11C \uAD6C\uC870\uB97C \uC644\uC804\uD788 \uBCF4\uC874\uD569\uB2C8\uB2E4. .hwpx \uC804\uC6A9\uC785\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC73C\uBA70, \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694. all:true(\uAE30\uBCF8\uAC12)\uC774\uBA74 \uBAA8\uB4E0 \uD56D\uBAA9\uC744 \uAD50\uCCB4\uD558\uACE0, all:false\uC774\uBA74 \uCCAB \uBC88\uC9F8 \uB9E4\uCE58\uB9CC \uAD50\uCCB4\uD569\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uAC00 \uC5EC\uB7EC \uC11C\uC2DD \uB7F0\uC5D0 \uB098\uB258\uC5B4 \uC788\uC73C\uBA74 \uACBD\uACC4\uB97C \uAC00\uB85C\uC9C0\uB974\uB294 \uD328\uD134\uC740 \uAD50\uCCB4\uB418\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4(\uC11C\uC2DD\uC774 \uB098\uB25C \uD14D\uC2A4\uD2B8). \uCC3E\uC744 \uD14D\uC2A4\uD2B8\uAC00 \uC5C6\uC73C\uBA74 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uC9C0 \uC54A\uACE0 \uC624\uB958\uB97C \uBC18\uD658\uD569\uB2C8\uB2E4. \uBCC0\uACBD \uC0AC\uD56D\uC740 diff \uBBF8\uB9AC\uBCF4\uAE30\uC640 \uD568\uAED8 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC740 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
|
|
12777
|
+
inputSchema: proposeFindReplaceSchema,
|
|
12778
|
+
requiresApproval: true,
|
|
12779
|
+
propose: async ({
|
|
12780
|
+
input,
|
|
12781
|
+
ctx
|
|
12782
|
+
}) => {
|
|
12783
|
+
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12784
|
+
const ext = extname6(safePath).toLowerCase();
|
|
12785
|
+
if (ext === ".hwp") {
|
|
12786
|
+
return "\uC624\uB958: propose_find_replace\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 XML \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
12787
|
+
}
|
|
12788
|
+
if (ext !== ".hwpx") {
|
|
12789
|
+
return `\uC624\uB958: propose_find_replace\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}. .hwpx \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.`;
|
|
12790
|
+
}
|
|
12791
|
+
let originalBuf;
|
|
12792
|
+
try {
|
|
12793
|
+
originalBuf = await readFile6(safePath);
|
|
12794
|
+
} catch {
|
|
12795
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
12796
|
+
}
|
|
12797
|
+
if (originalBuf[0] !== 80 || originalBuf[1] !== 75) {
|
|
12798
|
+
return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC77C \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
12799
|
+
}
|
|
12800
|
+
const originalBytes = new Uint8Array(
|
|
12801
|
+
originalBuf.buffer,
|
|
12802
|
+
originalBuf.byteOffset,
|
|
12803
|
+
originalBuf.byteLength
|
|
12804
|
+
);
|
|
12805
|
+
let newBytes;
|
|
12806
|
+
let replacedCount;
|
|
12807
|
+
try {
|
|
12808
|
+
const result = await applyFindReplaceToHwpx(
|
|
12809
|
+
originalBytes,
|
|
12810
|
+
input.find,
|
|
12811
|
+
input.replace,
|
|
12812
|
+
input.caseSensitive ?? false,
|
|
12813
|
+
input.all ?? true
|
|
12814
|
+
);
|
|
12815
|
+
newBytes = result.buffer;
|
|
12816
|
+
replacedCount = result.count;
|
|
12817
|
+
} catch (e) {
|
|
12818
|
+
return `\uC624\uB958: \uCE58\uD658 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. ${String(e)}`;
|
|
12819
|
+
}
|
|
12820
|
+
if (replacedCount === 0) {
|
|
12821
|
+
const caseNote = input.caseSensitive ? " (\uB300\uC18C\uBB38\uC790 \uAD6C\uBD84)" : "";
|
|
12822
|
+
return `\uC624\uB958: \uCC3E\uC744 \uD14D\uC2A4\uD2B8\uB97C \uBB38\uC11C\uC5D0\uC11C \uBC1C\uACAC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: "${input.find}"${caseNote}. read_document\uB85C \uBB38\uC11C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uACE0 \uC815\uD655\uD55C \uD14D\uC2A4\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694.`;
|
|
12823
|
+
}
|
|
12824
|
+
const warnings = [];
|
|
12825
|
+
try {
|
|
12826
|
+
const exportedResult = await parse2(newBytes.buffer);
|
|
12827
|
+
if (exportedResult.success) {
|
|
12828
|
+
const exportedMd = exportedResult.markdown;
|
|
12829
|
+
const normAfter = input.caseSensitive ?? false ? exportedMd : exportedMd.toLowerCase();
|
|
12830
|
+
const normFind = input.caseSensitive ?? false ? input.find : input.find.toLowerCase();
|
|
12831
|
+
const normReplace = input.caseSensitive ?? false ? input.replace : input.replace.toLowerCase();
|
|
12832
|
+
if (!normReplace.includes(normFind)) {
|
|
12833
|
+
const remaining = countOccurrences(normAfter, normFind);
|
|
12834
|
+
if (remaining > 0) {
|
|
12835
|
+
warnings.push(
|
|
12836
|
+
`\uC77C\uBD80 "${input.find}"(${remaining}\uACF3)\uC774 \uAD50\uCCB4\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uAC00 \uC5EC\uB7EC \uC11C\uC2DD \uB7F0\uC5D0 \uB098\uB258\uC5B4 \uC788\uC5B4 \uACBD\uACC4\uB97C \uAC00\uB85C\uC9C0\uB974\uB294 \uD328\uD134\uC740 \uAD50\uCCB4\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uC11C\uC2DD\uC774 \uB098\uB25C \uD14D\uC2A4\uD2B8). \uC774\uBBF8 \uAD50\uCCB4\uB41C ${replacedCount}\uACF3\uC740 \uC815\uC0C1 \uBC18\uC601\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`
|
|
12837
|
+
);
|
|
12838
|
+
}
|
|
12839
|
+
}
|
|
12840
|
+
}
|
|
12841
|
+
} catch {
|
|
12842
|
+
}
|
|
12843
|
+
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
12844
|
+
const allLabel = input.all ?? true ? `${replacedCount}\uACF3` : "\uCCAB \uBC88\uC9F8 1\uACF3";
|
|
12845
|
+
const diff = `\uCC3E\uAE30: "${input.find}" \u2192 \uBC14\uAFB8\uAE30: "${input.replace}" (${allLabel} \uAD50\uCCB4\uB428)`;
|
|
12846
|
+
const stagedPath = await stageFile(ctx.sessionId, outputPath, newBytes);
|
|
12847
|
+
const proposalId = crypto.randomUUID();
|
|
12848
|
+
return {
|
|
12849
|
+
proposal: {
|
|
12850
|
+
id: proposalId,
|
|
12851
|
+
kind: "find-replace",
|
|
12852
|
+
targetPath: outputPath,
|
|
12853
|
+
stagedPath,
|
|
12854
|
+
summary: input.summary,
|
|
12855
|
+
diff,
|
|
12856
|
+
warnings,
|
|
12857
|
+
willConvertFormat
|
|
12858
|
+
},
|
|
12859
|
+
commit: async () => {
|
|
12860
|
+
const backupPath = await backupFile(safePath);
|
|
12861
|
+
await commitStaged(stagedPath, outputPath);
|
|
12862
|
+
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
12863
|
+
return `\uC800\uC7A5 \uC644\uB8CC: ${outputPath}${backupInfo}`;
|
|
12864
|
+
}
|
|
12865
|
+
};
|
|
12866
|
+
}
|
|
12867
|
+
};
|
|
12868
|
+
var proposeFormFillSchema = z7.object({
|
|
12869
|
+
path: z7.string().describe("\uC591\uC2DD \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
12870
|
+
fields: z7.record(z7.string(), z7.string()).describe("\uCC44\uC6B8 \uD544\uB4DC \uB9E4\uD551: { \uB77C\uBCA8: \uAC12 }. read_document\uB85C \uBA3C\uC800 \uD544\uB4DC \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694"),
|
|
12871
|
+
summary: z7.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12872
|
+
});
|
|
12639
12873
|
var proposeFormFillTool = {
|
|
12640
12874
|
name: "propose_form_fill",
|
|
12641
12875
|
description: "HWPX/HWP \uC591\uC2DD \uBB38\uC11C\uC758 \uD544\uB4DC\uB97C \uCC44\uC6C1\uB2C8\uB2E4. \uBC18\uB4DC\uC2DC read_document\uB85C \uD604\uC7AC \uD544\uB4DC \uBAA9\uB85D\uC744 \uBA3C\uC800 \uD655\uC778\uD55C \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694. \uBCC0\uACBD \uC0AC\uD56D\uC740 diff \uBBF8\uB9AC\uBCF4\uAE30\uC640 \uD568\uAED8 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC740 \uD6C4\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
|
|
@@ -12646,17 +12880,17 @@ var proposeFormFillTool = {
|
|
|
12646
12880
|
ctx
|
|
12647
12881
|
}) => {
|
|
12648
12882
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12649
|
-
const ext =
|
|
12883
|
+
const ext = extname7(safePath).toLowerCase();
|
|
12650
12884
|
if (ext !== ".hwpx" && ext !== ".hwp") {
|
|
12651
12885
|
return `\uC624\uB958: propose_form_fill\uC740 .hwp/.hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}. .docx \uD30C\uC77C\uC740 propose_edit\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
|
|
12652
12886
|
}
|
|
12653
12887
|
let originalBuffer;
|
|
12654
12888
|
try {
|
|
12655
|
-
originalBuffer = await
|
|
12889
|
+
originalBuffer = await readFile7(safePath);
|
|
12656
12890
|
} catch {
|
|
12657
12891
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
12658
12892
|
}
|
|
12659
|
-
const parseResult = await
|
|
12893
|
+
const parseResult = await parse3(originalBuffer.buffer);
|
|
12660
12894
|
if (!parseResult.success) {
|
|
12661
12895
|
const msg = kordocErrorMessage(
|
|
12662
12896
|
parseResult.code,
|
|
@@ -12720,16 +12954,16 @@ var proposeFormFillTool = {
|
|
|
12720
12954
|
};
|
|
12721
12955
|
}
|
|
12722
12956
|
};
|
|
12723
|
-
var proposeSheetEditSchema =
|
|
12724
|
-
path:
|
|
12725
|
-
updates:
|
|
12726
|
-
|
|
12727
|
-
sheet:
|
|
12728
|
-
cell:
|
|
12729
|
-
value:
|
|
12957
|
+
var proposeSheetEditSchema = z8.object({
|
|
12958
|
+
path: z8.string().describe("\uC218\uC815\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
12959
|
+
updates: z8.array(
|
|
12960
|
+
z8.object({
|
|
12961
|
+
sheet: z8.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
|
|
12962
|
+
cell: z8.string().describe("\uC140 \uC8FC\uC18C (\uC608: A1, B3)"),
|
|
12963
|
+
value: z8.union([z8.string(), z8.number()]).describe("\uC0C8 \uAC12")
|
|
12730
12964
|
})
|
|
12731
12965
|
).min(1).describe("\uC218\uC815\uD560 \uC140 \uBAA9\uB85D. read_document\uB85C \uC6D0\uBCF8 \uC2DC\uD2B8 \uAD6C\uC870\uB97C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694"),
|
|
12732
|
-
summary:
|
|
12966
|
+
summary: z8.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
12733
12967
|
});
|
|
12734
12968
|
var proposeSheetEditTool = {
|
|
12735
12969
|
name: "propose_sheet_edit",
|
|
@@ -12741,13 +12975,13 @@ var proposeSheetEditTool = {
|
|
|
12741
12975
|
ctx
|
|
12742
12976
|
}) => {
|
|
12743
12977
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
12744
|
-
const ext =
|
|
12978
|
+
const ext = extname8(safePath).toLowerCase();
|
|
12745
12979
|
if (ext !== ".xlsx" && ext !== ".xls") {
|
|
12746
12980
|
return `\uC624\uB958: propose_sheet_edit\uC740 .xlsx/.xls \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C: ${ext}.`;
|
|
12747
12981
|
}
|
|
12748
12982
|
let originalBuffer;
|
|
12749
12983
|
try {
|
|
12750
|
-
originalBuffer = await
|
|
12984
|
+
originalBuffer = await readFile8(safePath);
|
|
12751
12985
|
} catch {
|
|
12752
12986
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
12753
12987
|
}
|
|
@@ -12807,6 +13041,881 @@ var proposeSheetEditTool = {
|
|
|
12807
13041
|
};
|
|
12808
13042
|
}
|
|
12809
13043
|
};
|
|
13044
|
+
function detectStructuralLoss(beforeBlocks, afterBlocks) {
|
|
13045
|
+
const LABELS = {
|
|
13046
|
+
paragraph: "\uB2E8\uB77D",
|
|
13047
|
+
table: "\uD45C",
|
|
13048
|
+
heading: "\uC81C\uBAA9",
|
|
13049
|
+
list: "\uBAA9\uB85D",
|
|
13050
|
+
image: "\uC774\uBBF8\uC9C0",
|
|
13051
|
+
separator: "\uAD6C\uBD84\uC120"
|
|
13052
|
+
};
|
|
13053
|
+
function histogram(blocks) {
|
|
13054
|
+
const h = {};
|
|
13055
|
+
for (const b of blocks) {
|
|
13056
|
+
h[b.type] = (h[b.type] ?? 0) + 1;
|
|
13057
|
+
}
|
|
13058
|
+
return h;
|
|
13059
|
+
}
|
|
13060
|
+
const beforeH = histogram(beforeBlocks);
|
|
13061
|
+
const afterH = histogram(afterBlocks);
|
|
13062
|
+
const dropParts = [];
|
|
13063
|
+
for (const [type, beforeCount] of Object.entries(beforeH)) {
|
|
13064
|
+
const afterCount = afterH[type] ?? 0;
|
|
13065
|
+
if (afterCount < beforeCount) {
|
|
13066
|
+
const label = LABELS[type] ?? type;
|
|
13067
|
+
dropParts.push(`${label} ${beforeCount}\u2192${afterCount}`);
|
|
13068
|
+
}
|
|
13069
|
+
}
|
|
13070
|
+
if (dropParts.length > 0) {
|
|
13071
|
+
return { lost: true, detail: dropParts.join(", ") };
|
|
13072
|
+
}
|
|
13073
|
+
return { lost: false, detail: "" };
|
|
13074
|
+
}
|
|
13075
|
+
var insertRowOpSchema = z9.object({
|
|
13076
|
+
type: z9.literal("insertRow"),
|
|
13077
|
+
row: z9.number().int().nonnegative().describe("\uAE30\uC900 \uD589 \uC778\uB371\uC2A4 (0-based)"),
|
|
13078
|
+
position: z9.enum(["above", "below"]).describe("\uC0BD\uC785 \uC704\uCE58: above=row \uC704\uC5D0, below=row \uC544\uB798\uC5D0")
|
|
13079
|
+
});
|
|
13080
|
+
var deleteRowOpSchema = z9.object({
|
|
13081
|
+
type: z9.literal("deleteRow"),
|
|
13082
|
+
row: z9.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uD589 \uC778\uB371\uC2A4 (0-based)")
|
|
13083
|
+
});
|
|
13084
|
+
var insertColumnOpSchema = z9.object({
|
|
13085
|
+
type: z9.literal("insertColumn"),
|
|
13086
|
+
col: z9.number().int().nonnegative().describe("\uAE30\uC900 \uC5F4 \uC778\uB371\uC2A4 (0-based)"),
|
|
13087
|
+
position: z9.enum(["left", "right"]).describe("\uC0BD\uC785 \uC704\uCE58: left=col \uC67C\uCABD\uC5D0, right=col \uC624\uB978\uCABD\uC5D0")
|
|
13088
|
+
});
|
|
13089
|
+
var deleteColumnOpSchema = z9.object({
|
|
13090
|
+
type: z9.literal("deleteColumn"),
|
|
13091
|
+
col: z9.number().int().nonnegative().describe("\uC0AD\uC81C\uD560 \uC5F4 \uC778\uB371\uC2A4 (0-based)")
|
|
13092
|
+
});
|
|
13093
|
+
var mergeCellsOpSchema = z9.object({
|
|
13094
|
+
type: z9.literal("mergeCells"),
|
|
13095
|
+
startRow: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uD589 (0-based)"),
|
|
13096
|
+
startCol: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uC2DC\uC791 \uC5F4 (0-based)"),
|
|
13097
|
+
endRow: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uD589 (0-based, \uD3EC\uD568)"),
|
|
13098
|
+
endCol: z9.number().int().nonnegative().describe("\uBCD1\uD569 \uB05D \uC5F4 (0-based, \uD3EC\uD568)")
|
|
13099
|
+
});
|
|
13100
|
+
var operationSchema = z9.discriminatedUnion("type", [
|
|
13101
|
+
insertRowOpSchema,
|
|
13102
|
+
deleteRowOpSchema,
|
|
13103
|
+
insertColumnOpSchema,
|
|
13104
|
+
deleteColumnOpSchema,
|
|
13105
|
+
mergeCellsOpSchema
|
|
13106
|
+
]).describe("\uD45C \uAD6C\uC870 \uC5F0\uC0B0. \uB098\uC911 \uC5F0\uC0B0\uC758 row/col \uC778\uB371\uC2A4\uB294 \uC55E \uC5F0\uC0B0 \uC801\uC6A9 \uD6C4 \uC0C1\uD0DC\uB97C \uAE30\uC900\uC73C\uB85C \uD55C\uB2E4.");
|
|
13107
|
+
var proposeTableStructureSchema = z9.object({
|
|
13108
|
+
path: z9.string().describe("\uC218\uC815\uD560 .hwpx \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13109
|
+
anchor: z9.string().min(1).describe(
|
|
13110
|
+
"\uB300\uC0C1 \uD45C\uB97C \uC2DD\uBCC4\uD558\uB294 \uC575\uCEE4 \uD14D\uC2A4\uD2B8. \uD45C \uC548\uC5D0\uB9CC \uC788\uB294 \uB3C5\uD2B9\uD55C \uC140 \uD14D\uC2A4\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694. (\uBD80\uBD84 \uC77C\uCE58, \uACF5\uBC31 \uD2B8\uB9BC) \u2014 read_document\uB85C \uD655\uC778 \uD6C4 \uC0AC\uC6A9 \uAD8C\uC7A5."
|
|
13111
|
+
),
|
|
13112
|
+
operations: z9.array(operationSchema).min(1).describe(
|
|
13113
|
+
"\uC801\uC6A9\uD560 \uD45C \uAD6C\uC870 \uC5F0\uC0B0 \uBAA9\uB85D (\uC21C\uC11C\uB300\uB85C \uC2E4\uD589). \uAC01 \uC5F0\uC0B0\uC740 \uC774\uC804 \uC5F0\uC0B0\uC774 \uC801\uC6A9\uB41C \uD6C4\uC758 \uD45C \uC0C1\uD0DC \uAE30\uC900\uC73C\uB85C row/col\uC744 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4."
|
|
13114
|
+
),
|
|
13115
|
+
summary: z9.string().describe("\uBCC0\uACBD \uC694\uC57D (\uD55C\uAD6D\uC5B4 1-2\uBB38\uC7A5)")
|
|
13116
|
+
});
|
|
13117
|
+
function tokenizeHwpxXml2(xml) {
|
|
13118
|
+
const tokens = [];
|
|
13119
|
+
const re = /<hp:tbl[\s>]|<\/hp:tbl>|<hp:tr[\s>]|<\/hp:tr>|<hp:tc[\s>]|<\/hp:tc>|<hp:t\/>|<hp:t>|<\/hp:t>|<hp:cellAddr[^/>]*|<hp:cellSpan[^/>]*|<hp:cellSz[^/>]*/g;
|
|
13120
|
+
let m = re.exec(xml);
|
|
13121
|
+
while (m !== null) {
|
|
13122
|
+
const raw = m[0];
|
|
13123
|
+
const pos = m.index;
|
|
13124
|
+
if (raw.startsWith("<hp:tbl")) {
|
|
13125
|
+
tokens.push({ kind: "tbl_open", pos, end: pos + raw.length });
|
|
13126
|
+
} else if (raw === "</hp:tbl>") {
|
|
13127
|
+
tokens.push({ kind: "tbl_close", pos, end: pos + raw.length });
|
|
13128
|
+
} else if (raw.startsWith("<hp:tr")) {
|
|
13129
|
+
tokens.push({ kind: "tr_open", pos, end: pos + raw.length });
|
|
13130
|
+
} else if (raw === "</hp:tr>") {
|
|
13131
|
+
tokens.push({ kind: "tr_close", pos, end: pos + raw.length });
|
|
13132
|
+
} else if (raw.startsWith("<hp:tc")) {
|
|
13133
|
+
tokens.push({ kind: "tc_open", pos, end: pos + raw.length });
|
|
13134
|
+
} else if (raw === "</hp:tc>") {
|
|
13135
|
+
tokens.push({ kind: "tc_close", pos, end: pos + raw.length });
|
|
13136
|
+
} else if (raw === "<hp:t/>") {
|
|
13137
|
+
tokens.push({ kind: "t_empty", pos, end: pos + raw.length });
|
|
13138
|
+
} else if (raw === "<hp:t>") {
|
|
13139
|
+
tokens.push({ kind: "t_open", pos, end: pos + raw.length });
|
|
13140
|
+
} else if (raw === "</hp:t>") {
|
|
13141
|
+
tokens.push({ kind: "t_close", pos, end: pos + raw.length });
|
|
13142
|
+
} else if (raw.startsWith("<hp:cellAddr")) {
|
|
13143
|
+
const colM = raw.match(/colAddr="(\d+)"/);
|
|
13144
|
+
const rowM = raw.match(/rowAddr="(\d+)"/);
|
|
13145
|
+
if (colM && rowM) {
|
|
13146
|
+
const selfClose = xml.indexOf("/>", pos);
|
|
13147
|
+
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
13148
|
+
tokens.push({
|
|
13149
|
+
kind: "cell_addr",
|
|
13150
|
+
pos,
|
|
13151
|
+
end,
|
|
13152
|
+
colAddr: Number(colM[1]),
|
|
13153
|
+
rowAddr: Number(rowM[1])
|
|
13154
|
+
});
|
|
13155
|
+
}
|
|
13156
|
+
} else if (raw.startsWith("<hp:cellSpan")) {
|
|
13157
|
+
const colM = raw.match(/colSpan="(\d+)"/);
|
|
13158
|
+
const rowM = raw.match(/rowSpan="(\d+)"/);
|
|
13159
|
+
const selfClose = xml.indexOf("/>", pos);
|
|
13160
|
+
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
13161
|
+
tokens.push({
|
|
13162
|
+
kind: "cell_span",
|
|
13163
|
+
pos,
|
|
13164
|
+
end,
|
|
13165
|
+
colSpan: colM ? Number(colM[1]) : 1,
|
|
13166
|
+
rowSpan: rowM ? Number(rowM[1]) : 1
|
|
13167
|
+
});
|
|
13168
|
+
} else if (raw.startsWith("<hp:cellSz")) {
|
|
13169
|
+
const selfClose = xml.indexOf("/>", pos);
|
|
13170
|
+
const end = selfClose >= 0 ? selfClose + 2 : pos + raw.length;
|
|
13171
|
+
tokens.push({ kind: "cell_sz", pos, end });
|
|
13172
|
+
}
|
|
13173
|
+
m = re.exec(xml);
|
|
13174
|
+
}
|
|
13175
|
+
return tokens;
|
|
13176
|
+
}
|
|
13177
|
+
function findAllTopLevelTableRanges(tokens) {
|
|
13178
|
+
const ranges = [];
|
|
13179
|
+
let depth = 0;
|
|
13180
|
+
let topCount = 0;
|
|
13181
|
+
let startPos = -1;
|
|
13182
|
+
for (const tok of tokens) {
|
|
13183
|
+
if (tok.kind === "tbl_open") {
|
|
13184
|
+
if (depth === 0) {
|
|
13185
|
+
startPos = tok.pos;
|
|
13186
|
+
}
|
|
13187
|
+
depth++;
|
|
13188
|
+
} else if (tok.kind === "tbl_close") {
|
|
13189
|
+
depth--;
|
|
13190
|
+
if (depth === 0 && startPos >= 0) {
|
|
13191
|
+
ranges.push({ start: startPos, end: tok.end, index: topCount });
|
|
13192
|
+
topCount++;
|
|
13193
|
+
startPos = -1;
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
13196
|
+
}
|
|
13197
|
+
return ranges;
|
|
13198
|
+
}
|
|
13199
|
+
function collectTableText(xml, tokens, tblStart, tblEnd) {
|
|
13200
|
+
const parts = [];
|
|
13201
|
+
const tblTokens = tokens.filter((t) => t.pos >= tblStart && t.pos < tblEnd);
|
|
13202
|
+
for (const tok of tblTokens) {
|
|
13203
|
+
if (tok.kind === "t_open") {
|
|
13204
|
+
const closePos = xml.indexOf("</hp:t>", tok.end);
|
|
13205
|
+
if (closePos >= 0 && closePos < tblEnd) {
|
|
13206
|
+
const raw = xml.substring(tok.end, closePos);
|
|
13207
|
+
parts.push(raw.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"));
|
|
13208
|
+
}
|
|
13209
|
+
}
|
|
13210
|
+
}
|
|
13211
|
+
return parts.join("");
|
|
13212
|
+
}
|
|
13213
|
+
function parseTableRows(_xml, tokens, tblStart, tblEnd) {
|
|
13214
|
+
const tblTokens = tokens.filter((t) => t.pos > tblStart && t.pos < tblEnd);
|
|
13215
|
+
const rows = [];
|
|
13216
|
+
const trStack = [];
|
|
13217
|
+
const tcStack = [];
|
|
13218
|
+
let innerTblDepth = 0;
|
|
13219
|
+
let currentRowCells = [];
|
|
13220
|
+
for (const tok of tblTokens) {
|
|
13221
|
+
if (tok.kind === "tbl_open") {
|
|
13222
|
+
innerTblDepth++;
|
|
13223
|
+
} else if (tok.kind === "tbl_close") {
|
|
13224
|
+
innerTblDepth--;
|
|
13225
|
+
} else if (innerTblDepth > 0) {
|
|
13226
|
+
} else if (tok.kind === "tr_open") {
|
|
13227
|
+
trStack.push({ pos: tok.pos });
|
|
13228
|
+
currentRowCells = [];
|
|
13229
|
+
} else if (tok.kind === "tr_close") {
|
|
13230
|
+
const trEntry = trStack.pop();
|
|
13231
|
+
if (trEntry !== void 0) {
|
|
13232
|
+
rows.push({ trStart: trEntry.pos, trEnd: tok.end, cells: currentRowCells });
|
|
13233
|
+
currentRowCells = [];
|
|
13234
|
+
}
|
|
13235
|
+
} else if (tok.kind === "tc_open") {
|
|
13236
|
+
tcStack.push({ pos: tok.pos });
|
|
13237
|
+
} else if (tok.kind === "tc_close") {
|
|
13238
|
+
const tcEntry = tcStack.pop();
|
|
13239
|
+
if (tcEntry !== void 0) {
|
|
13240
|
+
const tcTokens = tblTokens.filter(
|
|
13241
|
+
(t) => t.pos >= tcEntry.pos && t.pos < tok.end && innerTblDepth === 0
|
|
13242
|
+
);
|
|
13243
|
+
let colAddr = -1;
|
|
13244
|
+
let rowAddr = -1;
|
|
13245
|
+
let colSpan = 1;
|
|
13246
|
+
let rowSpan = 1;
|
|
13247
|
+
for (const t of tcTokens) {
|
|
13248
|
+
if (t.kind === "cell_addr" && t.colAddr !== void 0 && t.rowAddr !== void 0) {
|
|
13249
|
+
colAddr = t.colAddr;
|
|
13250
|
+
rowAddr = t.rowAddr;
|
|
13251
|
+
} else if (t.kind === "cell_span" && t.colSpan !== void 0 && t.rowSpan !== void 0) {
|
|
13252
|
+
colSpan = t.colSpan;
|
|
13253
|
+
rowSpan = t.rowSpan;
|
|
13254
|
+
}
|
|
13255
|
+
}
|
|
13256
|
+
if (colAddr >= 0 && rowAddr >= 0) {
|
|
13257
|
+
currentRowCells.push({
|
|
13258
|
+
colAddr,
|
|
13259
|
+
rowAddr,
|
|
13260
|
+
colSpan,
|
|
13261
|
+
rowSpan,
|
|
13262
|
+
tcStart: tcEntry.pos,
|
|
13263
|
+
tcEnd: tok.end
|
|
13264
|
+
});
|
|
13265
|
+
}
|
|
13266
|
+
}
|
|
13267
|
+
}
|
|
13268
|
+
}
|
|
13269
|
+
return rows;
|
|
13270
|
+
}
|
|
13271
|
+
function parseTblDimensions(xml, tblStart) {
|
|
13272
|
+
const tagEnd = xml.indexOf(">", tblStart);
|
|
13273
|
+
const tagStr = xml.substring(tblStart, tagEnd + 1);
|
|
13274
|
+
const rowM = tagStr.match(/rowCnt="(\d+)"/);
|
|
13275
|
+
const colM = tagStr.match(/colCnt="(\d+)"/);
|
|
13276
|
+
return {
|
|
13277
|
+
rowCnt: rowM ? Number(rowM[1]) : 0,
|
|
13278
|
+
colCnt: colM ? Number(colM[1]) : 0
|
|
13279
|
+
};
|
|
13280
|
+
}
|
|
13281
|
+
function updateTblAttr(tblBlock, attr, newVal) {
|
|
13282
|
+
return tblBlock.replace(new RegExp(`(${attr}=")\\d+(")`), `$1${newVal}$2`);
|
|
13283
|
+
}
|
|
13284
|
+
function clearCellText(tcXml) {
|
|
13285
|
+
let first = true;
|
|
13286
|
+
return tcXml.replace(/<hp:t>([\s\S]*?)<\/hp:t>/g, (_match, _content) => {
|
|
13287
|
+
if (first) {
|
|
13288
|
+
first = false;
|
|
13289
|
+
return "<hp:t/>";
|
|
13290
|
+
}
|
|
13291
|
+
return "<hp:t/>";
|
|
13292
|
+
});
|
|
13293
|
+
}
|
|
13294
|
+
function setCellRowAddr(tcXml, newRow) {
|
|
13295
|
+
return tcXml.replace(/(rowAddr=")(\d+)(")/, `$1${newRow}$3`);
|
|
13296
|
+
}
|
|
13297
|
+
function setCellColAddr(tcXml, newCol) {
|
|
13298
|
+
return tcXml.replace(/(colAddr=")(\d+)(")/, `$1${newCol}$3`);
|
|
13299
|
+
}
|
|
13300
|
+
function resetCellSpan(tcXml) {
|
|
13301
|
+
return tcXml.replace(/(colSpan=")(\d+)(")/, `$11$3`).replace(/(rowSpan=")(\d+)(")/, `$11$3`);
|
|
13302
|
+
}
|
|
13303
|
+
function setCellSpan(tcXml, colSpan, rowSpan) {
|
|
13304
|
+
return tcXml.replace(/(colSpan=")(\d+)(")/, `$1${colSpan}$3`).replace(/(rowSpan=")(\d+)(")/, `$1${rowSpan}$3`);
|
|
13305
|
+
}
|
|
13306
|
+
function insertRowInTbl(tblXml, row, below) {
|
|
13307
|
+
const tokens = tokenizeHwpxXml2(tblXml);
|
|
13308
|
+
const tblStart = 0;
|
|
13309
|
+
const tblEnd = tblXml.length;
|
|
13310
|
+
const rows = parseTableRows(tblXml, tokens, tblStart, tblEnd);
|
|
13311
|
+
const dims = parseTblDimensions(tblXml, tblStart);
|
|
13312
|
+
if (rows.length === 0) {
|
|
13313
|
+
return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC5B4 \uC0BD\uC785\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
13314
|
+
}
|
|
13315
|
+
const targetRow = rows.find((r) => r.cells.some((c) => c.rowAddr === row));
|
|
13316
|
+
if (!targetRow) {
|
|
13317
|
+
return {
|
|
13318
|
+
ok: false,
|
|
13319
|
+
error: `\uD589 ${row}\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD45C\uC5D0 rowAddr ${row}\uC778 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
13320
|
+
};
|
|
13321
|
+
}
|
|
13322
|
+
const newRowAddr = below ? row + 1 : row;
|
|
13323
|
+
for (const r of rows) {
|
|
13324
|
+
for (const c of r.cells) {
|
|
13325
|
+
if (c.rowSpan > 1) {
|
|
13326
|
+
const spanEnd = c.rowAddr + c.rowSpan - 1;
|
|
13327
|
+
if (c.rowAddr < newRowAddr && newRowAddr <= spanEnd) {
|
|
13328
|
+
return {
|
|
13329
|
+
ok: false,
|
|
13330
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 rowSpan=${c.rowSpan}\uC774 \uC0BD\uC785 \uC704\uCE58 \uD589 ${newRowAddr}\uC744 \uAC00\uB85C\uC9C0\uB985\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13331
|
+
};
|
|
13332
|
+
}
|
|
13333
|
+
}
|
|
13334
|
+
}
|
|
13335
|
+
}
|
|
13336
|
+
const trOffset = targetRow.trStart;
|
|
13337
|
+
let modifiedTr = tblXml.substring(targetRow.trStart, targetRow.trEnd);
|
|
13338
|
+
const sortedCells = [...targetRow.cells].sort((a, b) => b.tcStart - a.tcStart);
|
|
13339
|
+
for (const cell of sortedCells) {
|
|
13340
|
+
const tcLocalStart = cell.tcStart - trOffset;
|
|
13341
|
+
const tcLocalEnd = cell.tcEnd - trOffset;
|
|
13342
|
+
let tcXml = modifiedTr.substring(tcLocalStart, tcLocalEnd);
|
|
13343
|
+
tcXml = setCellRowAddr(tcXml, newRowAddr);
|
|
13344
|
+
tcXml = resetCellSpan(tcXml);
|
|
13345
|
+
tcXml = clearCellText(tcXml);
|
|
13346
|
+
modifiedTr = modifiedTr.substring(0, tcLocalStart) + tcXml + modifiedTr.substring(tcLocalEnd);
|
|
13347
|
+
}
|
|
13348
|
+
let result = tblXml;
|
|
13349
|
+
const allTokens = tokenizeHwpxXml2(result);
|
|
13350
|
+
const addrPatches = [];
|
|
13351
|
+
for (const tok of allTokens) {
|
|
13352
|
+
if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr >= newRowAddr) {
|
|
13353
|
+
addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.rowAddr + 1 });
|
|
13354
|
+
}
|
|
13355
|
+
}
|
|
13356
|
+
addrPatches.sort((a, b) => b.pos - a.pos);
|
|
13357
|
+
for (const p of addrPatches) {
|
|
13358
|
+
const oldTag = result.substring(p.pos, p.end);
|
|
13359
|
+
const newTag = oldTag.replace(/(rowAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
13360
|
+
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
13361
|
+
}
|
|
13362
|
+
const resultTokens = tokenizeHwpxXml2(result);
|
|
13363
|
+
const resultRows = parseTableRows(result, resultTokens, 0, result.length);
|
|
13364
|
+
let insertAfterPos;
|
|
13365
|
+
let insertBeforePos;
|
|
13366
|
+
if (below) {
|
|
13367
|
+
const refRow = resultRows.find((r) => r.cells.some((c) => c.rowAddr === row));
|
|
13368
|
+
if (!refRow) {
|
|
13369
|
+
return { ok: false, error: "\uC0BD\uC785 \uAE30\uC900 \uD589\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958)." };
|
|
13370
|
+
}
|
|
13371
|
+
insertAfterPos = refRow.trEnd;
|
|
13372
|
+
} else {
|
|
13373
|
+
const refRow = resultRows.find((r) => r.cells.some((c) => c.rowAddr === row + 1));
|
|
13374
|
+
if (!refRow) {
|
|
13375
|
+
const firstRow = resultRows[0];
|
|
13376
|
+
if (!firstRow) {
|
|
13377
|
+
return { ok: false, error: "\uC0BD\uC785 \uC704\uCE58\uB97C \uACB0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958)." };
|
|
13378
|
+
}
|
|
13379
|
+
insertBeforePos = firstRow.trStart;
|
|
13380
|
+
} else {
|
|
13381
|
+
insertBeforePos = refRow.trStart;
|
|
13382
|
+
}
|
|
13383
|
+
insertAfterPos = -1;
|
|
13384
|
+
}
|
|
13385
|
+
if (below) {
|
|
13386
|
+
result = result.substring(0, insertAfterPos) + modifiedTr + result.substring(insertAfterPos);
|
|
13387
|
+
} else {
|
|
13388
|
+
result = result.substring(0, insertBeforePos) + modifiedTr + result.substring(insertBeforePos);
|
|
13389
|
+
}
|
|
13390
|
+
result = updateTblAttr(result, "rowCnt", dims.rowCnt + 1);
|
|
13391
|
+
return { ok: true, xml: result };
|
|
13392
|
+
}
|
|
13393
|
+
function deleteRowInTbl(tblXml, row) {
|
|
13394
|
+
const tokens = tokenizeHwpxXml2(tblXml);
|
|
13395
|
+
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
13396
|
+
const dims = parseTblDimensions(tblXml, 0);
|
|
13397
|
+
if (rows.length === 0) {
|
|
13398
|
+
return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
13399
|
+
}
|
|
13400
|
+
const targetRow = rows.find((r) => r.cells.some((c) => c.rowAddr === row));
|
|
13401
|
+
if (!targetRow) {
|
|
13402
|
+
return {
|
|
13403
|
+
ok: false,
|
|
13404
|
+
error: `\uD589 ${row}\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD45C\uC5D0 rowAddr ${row}\uC778 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
13405
|
+
};
|
|
13406
|
+
}
|
|
13407
|
+
for (const cell of targetRow.cells) {
|
|
13408
|
+
if (cell.rowSpan > 1) {
|
|
13409
|
+
return {
|
|
13410
|
+
ok: false,
|
|
13411
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uD589 ${row}\uC758 \uC140 (\uC5F4 ${cell.colAddr})\uC5D0 rowSpan=${cell.rowSpan} \uBCD1\uD569\uC774 \uC788\uC2B5\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13412
|
+
};
|
|
13413
|
+
}
|
|
13414
|
+
}
|
|
13415
|
+
for (const r of rows) {
|
|
13416
|
+
if (r.trStart === targetRow.trStart) continue;
|
|
13417
|
+
for (const c of r.cells) {
|
|
13418
|
+
if (c.rowSpan > 1) {
|
|
13419
|
+
const spanEnd = c.rowAddr + c.rowSpan - 1;
|
|
13420
|
+
if (c.rowAddr < row && row <= spanEnd) {
|
|
13421
|
+
return {
|
|
13422
|
+
ok: false,
|
|
13423
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB2E4\uB978 \uD589 \uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 rowSpan=${c.rowSpan}\uC774 \uC0AD\uC81C\uD560 \uD589 ${row}\uC744 \uD3EC\uD568\uD569\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13424
|
+
};
|
|
13425
|
+
}
|
|
13426
|
+
}
|
|
13427
|
+
}
|
|
13428
|
+
}
|
|
13429
|
+
let result = tblXml;
|
|
13430
|
+
result = result.substring(0, targetRow.trStart) + result.substring(targetRow.trEnd);
|
|
13431
|
+
const afterTokens = tokenizeHwpxXml2(result);
|
|
13432
|
+
const addrPatches = [];
|
|
13433
|
+
for (const tok of afterTokens) {
|
|
13434
|
+
if (tok.kind === "cell_addr" && tok.rowAddr !== void 0 && tok.rowAddr > row) {
|
|
13435
|
+
addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.rowAddr - 1 });
|
|
13436
|
+
}
|
|
13437
|
+
}
|
|
13438
|
+
addrPatches.sort((a, b) => b.pos - a.pos);
|
|
13439
|
+
for (const p of addrPatches) {
|
|
13440
|
+
const oldTag = result.substring(p.pos, p.end);
|
|
13441
|
+
const newTag = oldTag.replace(/(rowAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
13442
|
+
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
13443
|
+
}
|
|
13444
|
+
result = updateTblAttr(result, "rowCnt", dims.rowCnt - 1);
|
|
13445
|
+
return { ok: true, xml: result };
|
|
13446
|
+
}
|
|
13447
|
+
function insertColumnInTbl(tblXml, col, right) {
|
|
13448
|
+
const tokens = tokenizeHwpxXml2(tblXml);
|
|
13449
|
+
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
13450
|
+
const dims = parseTblDimensions(tblXml, 0);
|
|
13451
|
+
if (rows.length === 0) {
|
|
13452
|
+
return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC5B4 \uC5F4\uC744 \uC0BD\uC785\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
13453
|
+
}
|
|
13454
|
+
const newColAddr = right ? col + 1 : col;
|
|
13455
|
+
for (const r of rows) {
|
|
13456
|
+
for (const c of r.cells) {
|
|
13457
|
+
if (c.colSpan > 1) {
|
|
13458
|
+
const spanEnd = c.colAddr + c.colSpan - 1;
|
|
13459
|
+
if (c.colAddr < newColAddr && newColAddr <= spanEnd) {
|
|
13460
|
+
return {
|
|
13461
|
+
ok: false,
|
|
13462
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 colSpan=${c.colSpan}\uC774 \uC0BD\uC785 \uC704\uCE58 \uC5F4 ${newColAddr}\uC744 \uAC00\uB85C\uC9C0\uB985\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13463
|
+
};
|
|
13464
|
+
}
|
|
13465
|
+
}
|
|
13466
|
+
}
|
|
13467
|
+
}
|
|
13468
|
+
let result = tblXml;
|
|
13469
|
+
const allTokens = tokenizeHwpxXml2(result);
|
|
13470
|
+
const addrPatches = [];
|
|
13471
|
+
for (const tok of allTokens) {
|
|
13472
|
+
if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr >= newColAddr) {
|
|
13473
|
+
addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.colAddr + 1 });
|
|
13474
|
+
}
|
|
13475
|
+
}
|
|
13476
|
+
addrPatches.sort((a, b) => b.pos - a.pos);
|
|
13477
|
+
for (const p of addrPatches) {
|
|
13478
|
+
const oldTag = result.substring(p.pos, p.end);
|
|
13479
|
+
const newTag = oldTag.replace(/(colAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
13480
|
+
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
13481
|
+
}
|
|
13482
|
+
const resultTokens2 = tokenizeHwpxXml2(result);
|
|
13483
|
+
const resultRows = parseTableRows(result, resultTokens2, 0, result.length);
|
|
13484
|
+
const sortedRows = [...resultRows].sort((a, b) => b.trStart - a.trStart);
|
|
13485
|
+
for (const r of sortedRows) {
|
|
13486
|
+
const refRowAddr = r.cells[0]?.rowAddr ?? 0;
|
|
13487
|
+
const cloneSourceCell = r.cells.find((c) => c.colAddr === col + 1);
|
|
13488
|
+
const anyCell = cloneSourceCell ?? r.cells[0];
|
|
13489
|
+
if (!anyCell) continue;
|
|
13490
|
+
let newTcXml = result.substring(anyCell.tcStart, anyCell.tcEnd);
|
|
13491
|
+
newTcXml = setCellColAddr(newTcXml, newColAddr);
|
|
13492
|
+
newTcXml = setCellRowAddr(newTcXml, refRowAddr);
|
|
13493
|
+
newTcXml = resetCellSpan(newTcXml);
|
|
13494
|
+
newTcXml = clearCellText(newTcXml);
|
|
13495
|
+
const refCell = r.cells.find((c) => c.colAddr === col + 1);
|
|
13496
|
+
let insertPos;
|
|
13497
|
+
if (refCell) {
|
|
13498
|
+
insertPos = refCell.tcStart;
|
|
13499
|
+
} else {
|
|
13500
|
+
const lastCell = r.cells[r.cells.length - 1];
|
|
13501
|
+
insertPos = lastCell ? lastCell.tcEnd : r.trEnd - "</hp:tr>".length;
|
|
13502
|
+
}
|
|
13503
|
+
result = result.substring(0, insertPos) + newTcXml + result.substring(insertPos);
|
|
13504
|
+
}
|
|
13505
|
+
result = updateTblAttr(result, "colCnt", dims.colCnt + 1);
|
|
13506
|
+
return { ok: true, xml: result };
|
|
13507
|
+
}
|
|
13508
|
+
function deleteColumnInTbl(tblXml, col) {
|
|
13509
|
+
const tokens = tokenizeHwpxXml2(tblXml);
|
|
13510
|
+
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
13511
|
+
const dims = parseTblDimensions(tblXml, 0);
|
|
13512
|
+
if (rows.length === 0) {
|
|
13513
|
+
return { ok: false, error: "\uD45C\uC5D0 \uD589\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." };
|
|
13514
|
+
}
|
|
13515
|
+
for (const r of rows) {
|
|
13516
|
+
const cell = r.cells.find((c) => c.colAddr === col);
|
|
13517
|
+
if (cell && cell.colSpan > 1) {
|
|
13518
|
+
return {
|
|
13519
|
+
ok: false,
|
|
13520
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC5F4 ${col}\uC758 \uC140 (\uD589 ${cell.rowAddr})\uC5D0 colSpan=${cell.colSpan} \uBCD1\uD569\uC774 \uC788\uC2B5\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13521
|
+
};
|
|
13522
|
+
}
|
|
13523
|
+
for (const c of r.cells) {
|
|
13524
|
+
if (c.colAddr !== col && c.colSpan > 1) {
|
|
13525
|
+
const spanEnd = c.colAddr + c.colSpan - 1;
|
|
13526
|
+
if (c.colAddr < col && col <= spanEnd) {
|
|
13527
|
+
return {
|
|
13528
|
+
ok: false,
|
|
13529
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 colSpan=${c.colSpan}\uC774 \uC0AD\uC81C\uD560 \uC5F4 ${col}\uC744 \uD3EC\uD568\uD569\uB2C8\uB2E4). \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13530
|
+
};
|
|
13531
|
+
}
|
|
13532
|
+
}
|
|
13533
|
+
}
|
|
13534
|
+
}
|
|
13535
|
+
let result = tblXml;
|
|
13536
|
+
const sortedRows = [...rows].sort((a, b) => b.trStart - a.trStart);
|
|
13537
|
+
for (const r of sortedRows) {
|
|
13538
|
+
const cell = r.cells.find((c) => c.colAddr === col);
|
|
13539
|
+
if (cell) {
|
|
13540
|
+
result = result.substring(0, cell.tcStart) + result.substring(cell.tcEnd);
|
|
13541
|
+
}
|
|
13542
|
+
}
|
|
13543
|
+
const afterTokens = tokenizeHwpxXml2(result);
|
|
13544
|
+
const addrPatches = [];
|
|
13545
|
+
for (const tok of afterTokens) {
|
|
13546
|
+
if (tok.kind === "cell_addr" && tok.colAddr !== void 0 && tok.colAddr > col) {
|
|
13547
|
+
addrPatches.push({ pos: tok.pos, end: tok.end, newAddr: tok.colAddr - 1 });
|
|
13548
|
+
}
|
|
13549
|
+
}
|
|
13550
|
+
addrPatches.sort((a, b) => b.pos - a.pos);
|
|
13551
|
+
for (const p of addrPatches) {
|
|
13552
|
+
const oldTag = result.substring(p.pos, p.end);
|
|
13553
|
+
const newTag = oldTag.replace(/(colAddr=")(\d+)(")/, `$1${p.newAddr}$3`);
|
|
13554
|
+
result = result.substring(0, p.pos) + newTag + result.substring(p.end);
|
|
13555
|
+
}
|
|
13556
|
+
result = updateTblAttr(result, "colCnt", dims.colCnt - 1);
|
|
13557
|
+
return { ok: true, xml: result };
|
|
13558
|
+
}
|
|
13559
|
+
function mergeCellsInTbl(tblXml, startRow, startCol, endRow, endCol) {
|
|
13560
|
+
if (startRow === endRow && startCol === endCol) {
|
|
13561
|
+
return { ok: false, error: "\uBCD1\uD569 \uBC94\uC704\uAC00 \uB2E8\uC77C \uC140\uC785\uB2C8\uB2E4. \uCD5C\uC18C 2\uAC1C \uC774\uC0C1\uC758 \uC140\uC744 \uC9C0\uC815\uD558\uC138\uC694." };
|
|
13562
|
+
}
|
|
13563
|
+
if (startRow > endRow || startCol > endCol) {
|
|
13564
|
+
return { ok: false, error: "\uBCD1\uD569 \uBC94\uC704\uAC00 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (start > end)." };
|
|
13565
|
+
}
|
|
13566
|
+
const tokens = tokenizeHwpxXml2(tblXml);
|
|
13567
|
+
const rows = parseTableRows(tblXml, tokens, 0, tblXml.length);
|
|
13568
|
+
const rangeCells = [];
|
|
13569
|
+
for (const r of rows) {
|
|
13570
|
+
for (const c of r.cells) {
|
|
13571
|
+
if (c.rowAddr >= startRow && c.rowAddr <= endRow && c.colAddr >= startCol && c.colAddr <= endCol) {
|
|
13572
|
+
rangeCells.push(c);
|
|
13573
|
+
}
|
|
13574
|
+
}
|
|
13575
|
+
}
|
|
13576
|
+
if (rangeCells.length === 0) {
|
|
13577
|
+
return {
|
|
13578
|
+
ok: false,
|
|
13579
|
+
error: `\uBCD1\uD569 \uBC94\uC704 (${startRow},${startCol})~(${endRow},${endCol})\uC5D0 \uC140\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
13580
|
+
};
|
|
13581
|
+
}
|
|
13582
|
+
for (const c of rangeCells) {
|
|
13583
|
+
if (c.colSpan > 1 || c.rowSpan > 1) {
|
|
13584
|
+
return {
|
|
13585
|
+
ok: false,
|
|
13586
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC774 \uC774\uBBF8 colSpan=${c.colSpan}, rowSpan=${c.rowSpan}\uC73C\uB85C \uBCD1\uD569\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4). \uAE30\uC874 \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13587
|
+
};
|
|
13588
|
+
}
|
|
13589
|
+
}
|
|
13590
|
+
for (const r of rows) {
|
|
13591
|
+
for (const c of r.cells) {
|
|
13592
|
+
if (c.colSpan <= 1 && c.rowSpan <= 1) continue;
|
|
13593
|
+
const inRange = c.rowAddr >= startRow && c.rowAddr <= endRow && c.colAddr >= startCol && c.colAddr <= endCol;
|
|
13594
|
+
if (inRange) continue;
|
|
13595
|
+
const colEnd = c.colAddr + c.colSpan - 1;
|
|
13596
|
+
const rowEnd = c.rowAddr + c.rowSpan - 1;
|
|
13597
|
+
if (c.colAddr <= endCol && colEnd >= startCol && c.rowAddr <= endRow && rowEnd >= startRow) {
|
|
13598
|
+
return {
|
|
13599
|
+
ok: false,
|
|
13600
|
+
error: `\uC774 \uD45C\uC758 \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uACB9\uCCD0 \uC548\uC804\uD558\uAC8C \uCC98\uB9AC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC140 (\uD589 ${c.rowAddr}, \uC5F4 ${c.colAddr})\uC758 span\uC774 \uBCD1\uD569 \uBC94\uC704\uC640 \uACB9\uCE69\uB2C8\uB2E4). \uAE30\uC874 \uBCD1\uD569\uC744 \uD574\uC81C\uD55C \uD6C4 \uC2DC\uB3C4\uD558\uC138\uC694.`
|
|
13601
|
+
};
|
|
13602
|
+
}
|
|
13603
|
+
}
|
|
13604
|
+
}
|
|
13605
|
+
const colSpan = endCol - startCol + 1;
|
|
13606
|
+
const rowSpan = endRow - startRow + 1;
|
|
13607
|
+
const topLeftCell = rangeCells.find((c) => c.rowAddr === startRow && c.colAddr === startCol);
|
|
13608
|
+
if (!topLeftCell) {
|
|
13609
|
+
return {
|
|
13610
|
+
ok: false,
|
|
13611
|
+
error: `\uBCD1\uD569 \uBC94\uC704\uC758 \uC88C\uC0C1\uB2E8 \uC140 (\uD589 ${startRow}, \uC5F4 ${startCol})\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
13612
|
+
};
|
|
13613
|
+
}
|
|
13614
|
+
const cellsToRemove = rangeCells.filter(
|
|
13615
|
+
(c) => !(c.rowAddr === startRow && c.colAddr === startCol)
|
|
13616
|
+
);
|
|
13617
|
+
let result = tblXml;
|
|
13618
|
+
const sortedRemove = [...cellsToRemove].sort((a, b) => b.tcStart - a.tcStart);
|
|
13619
|
+
for (const c of sortedRemove) {
|
|
13620
|
+
result = result.substring(0, c.tcStart) + result.substring(c.tcEnd);
|
|
13621
|
+
}
|
|
13622
|
+
const afterTokens = tokenizeHwpxXml2(result);
|
|
13623
|
+
const afterRows = parseTableRows(result, afterTokens, 0, result.length);
|
|
13624
|
+
const afterTopLeft = afterRows.flatMap((r) => r.cells).find((c) => c.rowAddr === startRow && c.colAddr === startCol);
|
|
13625
|
+
if (!afterTopLeft) {
|
|
13626
|
+
return { ok: false, error: "\uBCD1\uD569 \uD6C4 \uC88C\uC0C1\uB2E8 \uC140\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uB0B4\uBD80 \uC624\uB958)." };
|
|
13627
|
+
}
|
|
13628
|
+
let tcXml = result.substring(afterTopLeft.tcStart, afterTopLeft.tcEnd);
|
|
13629
|
+
tcXml = setCellSpan(tcXml, colSpan, rowSpan);
|
|
13630
|
+
result = result.substring(0, afterTopLeft.tcStart) + tcXml + result.substring(afterTopLeft.tcEnd);
|
|
13631
|
+
return { ok: true, xml: result };
|
|
13632
|
+
}
|
|
13633
|
+
function findTableByAnchorInXml(xml, anchor) {
|
|
13634
|
+
const tokens = tokenizeHwpxXml2(xml);
|
|
13635
|
+
const allRanges = findAllTopLevelTableRanges(tokens);
|
|
13636
|
+
const trimmedAnchor = anchor.trim();
|
|
13637
|
+
const matched = [];
|
|
13638
|
+
for (const r of allRanges) {
|
|
13639
|
+
const text3 = collectTableText(xml, tokens, r.start, r.end);
|
|
13640
|
+
if (text3.includes(trimmedAnchor)) {
|
|
13641
|
+
matched.push(r);
|
|
13642
|
+
}
|
|
13643
|
+
}
|
|
13644
|
+
if (matched.length === 0) {
|
|
13645
|
+
return {
|
|
13646
|
+
error: `anchor\uB97C \uD3EC\uD568\uD55C \uD45C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: "${anchor}". read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uACE0 \uD45C \uC548\uC5D0 \uC788\uB294 \uB3C5\uD2B9\uD55C \uD14D\uC2A4\uD2B8\uB97C anchor\uB85C \uC9C0\uC815\uD558\uC138\uC694.`
|
|
13647
|
+
};
|
|
13648
|
+
}
|
|
13649
|
+
if (matched.length > 1) {
|
|
13650
|
+
return {
|
|
13651
|
+
error: `anchor "${anchor}"\uC774(\uAC00) ${matched.length}\uAC1C\uC758 \uD45C\uC5D0\uC11C \uBC1C\uACAC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB354 \uAD6C\uCCB4\uC801\uC778 anchor \uD14D\uC2A4\uD2B8\uB97C \uC0AC\uC6A9\uD558\uC5EC \uD45C\uB97C \uD55C \uAC1C\uB9CC \uC120\uD0DD\uD560 \uC218 \uC788\uB3C4\uB85D \uD558\uC138\uC694.`
|
|
13652
|
+
};
|
|
13653
|
+
}
|
|
13654
|
+
return { range: matched[0], sectionXml: xml };
|
|
13655
|
+
}
|
|
13656
|
+
function computeExpectedDelta(ops) {
|
|
13657
|
+
let rowDelta = 0;
|
|
13658
|
+
let colDelta = 0;
|
|
13659
|
+
for (const op of ops) {
|
|
13660
|
+
if (op.type === "mergeCells") return null;
|
|
13661
|
+
if (op.type === "insertRow") rowDelta++;
|
|
13662
|
+
else if (op.type === "deleteRow") rowDelta--;
|
|
13663
|
+
else if (op.type === "insertColumn") colDelta++;
|
|
13664
|
+
else if (op.type === "deleteColumn") colDelta--;
|
|
13665
|
+
}
|
|
13666
|
+
return { rowDelta, colDelta };
|
|
13667
|
+
}
|
|
13668
|
+
function getTblDims(tblXml) {
|
|
13669
|
+
return parseTblDimensions(tblXml, 0);
|
|
13670
|
+
}
|
|
13671
|
+
async function applyOpsToHwpx(hwpxBuffer, anchor, operations) {
|
|
13672
|
+
const zip = await import_jszip4.default.loadAsync(hwpxBuffer);
|
|
13673
|
+
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
13674
|
+
let targetSectionIdx = -1;
|
|
13675
|
+
let targetRange = null;
|
|
13676
|
+
const sectionXmls = [];
|
|
13677
|
+
for (let si = 0; si < sectionFiles.length; si++) {
|
|
13678
|
+
const entry = zip.file(sectionFiles[si] ?? "");
|
|
13679
|
+
const xml = entry ? await entry.async("string") : "";
|
|
13680
|
+
sectionXmls.push(xml);
|
|
13681
|
+
if (targetSectionIdx === -1) {
|
|
13682
|
+
const found = findTableByAnchorInXml(xml, anchor);
|
|
13683
|
+
if ("range" in found) {
|
|
13684
|
+
targetSectionIdx = si;
|
|
13685
|
+
targetRange = found.range;
|
|
13686
|
+
} else if (found.error.includes("\uC774(\uAC00)") && found.error.includes("\uAC1C\uC758 \uD45C\uC5D0\uC11C")) {
|
|
13687
|
+
return { ok: false, error: `\uC624\uB958: ${found.error}` };
|
|
13688
|
+
}
|
|
13689
|
+
}
|
|
13690
|
+
}
|
|
13691
|
+
for (let si = sectionXmls.length; si < sectionFiles.length; si++) {
|
|
13692
|
+
const entry = zip.file(sectionFiles[si] ?? "");
|
|
13693
|
+
const xml = entry ? await entry.async("string") : "";
|
|
13694
|
+
sectionXmls.push(xml);
|
|
13695
|
+
}
|
|
13696
|
+
if (targetSectionIdx === -1 || targetRange === null) {
|
|
13697
|
+
return {
|
|
13698
|
+
ok: false,
|
|
13699
|
+
error: `\uC624\uB958: anchor\uB97C \uD3EC\uD568\uD55C \uD45C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: "${anchor}". read_document\uB85C \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uACE0 \uD45C \uC548\uC5D0 \uC788\uB294 \uB3C5\uD2B9\uD55C \uD14D\uC2A4\uD2B8\uB97C anchor\uB85C \uC9C0\uC815\uD558\uC138\uC694.`
|
|
13700
|
+
};
|
|
13701
|
+
}
|
|
13702
|
+
const sectionXml = sectionXmls[targetSectionIdx] ?? "";
|
|
13703
|
+
let tblBlock = sectionXml.substring(targetRange.start, targetRange.end);
|
|
13704
|
+
const beforeDims = getTblDims(tblBlock);
|
|
13705
|
+
for (let i = 0; i < operations.length; i++) {
|
|
13706
|
+
const op = operations[i];
|
|
13707
|
+
if (!op) continue;
|
|
13708
|
+
let opResult;
|
|
13709
|
+
if (op.type === "insertRow") {
|
|
13710
|
+
opResult = insertRowInTbl(tblBlock, op.row, op.position === "below");
|
|
13711
|
+
} else if (op.type === "deleteRow") {
|
|
13712
|
+
opResult = deleteRowInTbl(tblBlock, op.row);
|
|
13713
|
+
} else if (op.type === "insertColumn") {
|
|
13714
|
+
opResult = insertColumnInTbl(tblBlock, op.col, op.position === "right");
|
|
13715
|
+
} else if (op.type === "deleteColumn") {
|
|
13716
|
+
opResult = deleteColumnInTbl(tblBlock, op.col);
|
|
13717
|
+
} else {
|
|
13718
|
+
opResult = mergeCellsInTbl(tblBlock, op.startRow, op.startCol, op.endRow, op.endCol);
|
|
13719
|
+
}
|
|
13720
|
+
if (!opResult.ok) {
|
|
13721
|
+
return {
|
|
13722
|
+
ok: false,
|
|
13723
|
+
error: `\uC624\uB958: \uC5F0\uC0B0 #${i + 1} (${op.type}) \uC2E4\uD328. \uD30C\uC77C\uC744 \uBCC0\uACBD\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC6D0\uC778: ${opResult.error}`
|
|
13724
|
+
};
|
|
13725
|
+
}
|
|
13726
|
+
tblBlock = opResult.xml;
|
|
13727
|
+
}
|
|
13728
|
+
const afterDims = getTblDims(tblBlock);
|
|
13729
|
+
const newSectionXml = sectionXml.substring(0, targetRange.start) + tblBlock + sectionXml.substring(targetRange.end);
|
|
13730
|
+
sectionXmls[targetSectionIdx] = newSectionXml;
|
|
13731
|
+
const out = new import_jszip4.default();
|
|
13732
|
+
const mimetypeEntry = zip.file("mimetype");
|
|
13733
|
+
if (mimetypeEntry) {
|
|
13734
|
+
out.file("mimetype", await mimetypeEntry.async("uint8array"), { compression: "STORE" });
|
|
13735
|
+
}
|
|
13736
|
+
for (const [name, entry] of Object.entries(zip.files)) {
|
|
13737
|
+
if (name === "mimetype" || entry.dir) continue;
|
|
13738
|
+
const sectionIdx = sectionFiles.indexOf(name);
|
|
13739
|
+
if (sectionIdx >= 0) {
|
|
13740
|
+
out.file(name, sectionXmls[sectionIdx] ?? "");
|
|
13741
|
+
} else {
|
|
13742
|
+
out.file(name, await entry.async("uint8array"));
|
|
13743
|
+
}
|
|
13744
|
+
}
|
|
13745
|
+
const buf = await out.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
|
|
13746
|
+
return {
|
|
13747
|
+
ok: true,
|
|
13748
|
+
buffer: new Uint8Array(buf),
|
|
13749
|
+
beforeDims,
|
|
13750
|
+
afterDims,
|
|
13751
|
+
anchorTableIndex: targetRange.index
|
|
13752
|
+
};
|
|
13753
|
+
}
|
|
13754
|
+
async function verifyOutputDims(newBytes, anchor, expectedRowCnt, expectedColCnt) {
|
|
13755
|
+
const zip = await import_jszip4.default.loadAsync(newBytes);
|
|
13756
|
+
const sectionFiles = Object.keys(zip.files).filter((name) => /^Contents\/section\d+\.xml$/.test(name)).sort();
|
|
13757
|
+
for (const sf of sectionFiles) {
|
|
13758
|
+
const entry = zip.file(sf);
|
|
13759
|
+
const xml = entry ? await entry.async("string") : "";
|
|
13760
|
+
const tokens = tokenizeHwpxXml2(xml);
|
|
13761
|
+
const allRanges = findAllTopLevelTableRanges(tokens);
|
|
13762
|
+
const trimmedAnchor = anchor.trim();
|
|
13763
|
+
for (const r of allRanges) {
|
|
13764
|
+
const text3 = collectTableText(xml, tokens, r.start, r.end);
|
|
13765
|
+
if (text3.includes(trimmedAnchor)) {
|
|
13766
|
+
const tblBlock = xml.substring(r.start, r.end);
|
|
13767
|
+
const dims = getTblDims(tblBlock);
|
|
13768
|
+
if (dims.rowCnt !== expectedRowCnt || dims.colCnt !== expectedColCnt) {
|
|
13769
|
+
return {
|
|
13770
|
+
ok: false,
|
|
13771
|
+
error: `\uC790\uAE30\uAC80\uC99D \uC2E4\uD328 \u2014 \uC608\uC0C1 \uCE58\uC218 (${expectedRowCnt}\uD589 \xD7 ${expectedColCnt}\uC5F4)\uC640 \uC2E4\uC81C \uCE58\uC218 (${dims.rowCnt}\uD589 \xD7 ${dims.colCnt}\uC5F4)\uAC00 \uB2E4\uB985\uB2C8\uB2E4.`
|
|
13772
|
+
};
|
|
13773
|
+
}
|
|
13774
|
+
return { ok: true };
|
|
13775
|
+
}
|
|
13776
|
+
}
|
|
13777
|
+
}
|
|
13778
|
+
return { ok: true };
|
|
13779
|
+
}
|
|
13780
|
+
var proposeTableStructureTool = {
|
|
13781
|
+
name: "propose_table_structure",
|
|
13782
|
+
description: "HWPX \uBB38\uC11C\uC5D0\uC11C anchor \uD14D\uC2A4\uD2B8\uB85C \uC2DD\uBCC4\uB41C \uD45C\uC758 \uAD6C\uC870(\uD589/\uC5F4/\uC140 \uBCD1\uD569)\uB97C \uC218\uC815\uD569\uB2C8\uB2E4. ZIP XML \uC9C1\uC811 \uD328\uCE58 \uBC29\uC2DD\uC73C\uB85C \uB3D9\uC791\uD569\uB2C8\uB2E4(rhwp \uBBF8\uC0AC\uC6A9). \uC774\uBBF8\uC9C0\xB7\uC911\uCCA9\uD45C \uB4F1 \uBCF5\uC7A1\uD55C \uBB38\uC11C\uB3C4 \uAD6C\uC870 \uC190\uC2E4 \uC5C6\uC774 \uC548\uC804\uD558\uAC8C \uD3B8\uC9D1 \uAC00\uB2A5\uD569\uB2C8\uB2E4. anchor\uB294 \uB300\uC0C1 \uD45C \uC548\uC5D0\uB9CC \uC788\uB294 \uB3C5\uD2B9\uD55C \uC140 \uD14D\uC2A4\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694 \u2014 \uBA3C\uC800 read_document\uB85C \uBB38\uC11C\uB97C \uC77D\uC5B4 \uD45C \uB0B4\uC6A9\uC744 \uD655\uC778\uD558\uC138\uC694. \uC5F0\uC0B0\uC740 \uC9C0\uC815\uB41C \uC21C\uC11C\uB300\uB85C \uC801\uC6A9\uB418\uBA70, \uB098\uC911 \uC5F0\uC0B0\uC758 row/col \uC778\uB371\uC2A4\uB294 \uC55E \uC5F0\uC0B0 \uC801\uC6A9 \uD6C4\uC758 \uD45C \uC0C1\uD0DC\uB97C \uAE30\uC900\uC73C\uB85C \uD569\uB2C8\uB2E4. .hwpx \uC804\uC6A9\uC785\uB2C8\uB2E4 (.hwp\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC73C\uBA70, Hancom\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uC0AC\uC6A9\uD558\uC138\uC694). \uAE30\uC874 \uBCD1\uD569 \uC140\uACFC \uAD50\uCC28\uD558\uB294 \uC5F0\uC0B0\uC740 \uC548\uC804\uC744 \uC704\uD574 \uAC70\uBD80\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uBCC0\uACBD \uC0AC\uD56D\uC740 \uC790\uAE30\uAC80\uC99D \uAC8C\uC774\uD2B8\uB97C \uD1B5\uACFC\uD55C \uD6C4 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uC544\uC57C\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4.",
|
|
13783
|
+
inputSchema: proposeTableStructureSchema,
|
|
13784
|
+
requiresApproval: true,
|
|
13785
|
+
propose: async ({
|
|
13786
|
+
input,
|
|
13787
|
+
ctx
|
|
13788
|
+
}) => {
|
|
13789
|
+
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13790
|
+
const ext = extname9(safePath).toLowerCase();
|
|
13791
|
+
if (ext === ".hwp") {
|
|
13792
|
+
return "\uC624\uB958: propose_table_structure\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. .hwp(\uAD6C\uD615 OLE \uBC14\uC774\uB108\uB9AC)\uB294 XML \uC9C1\uC811 \uD3B8\uC9D1\uC774 \uBD88\uAC00\uD569\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C '\uB2E4\uB978 \uC774\uB984\uC73C\uB85C \uC800\uC7A5 \u2192 .hwpx'\uB85C \uC800\uC7A5\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
13793
|
+
}
|
|
13794
|
+
if (ext !== ".hwpx") {
|
|
13795
|
+
return `\uC624\uB958: propose_table_structure\uB294 .hwpx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD30C\uC77C \uD655\uC7A5\uC790: ${ext}. .hwpx \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.`;
|
|
13796
|
+
}
|
|
13797
|
+
let originalBuf;
|
|
13798
|
+
try {
|
|
13799
|
+
originalBuf = await readFile9(safePath);
|
|
13800
|
+
} catch {
|
|
13801
|
+
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.path}. \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.`;
|
|
13802
|
+
}
|
|
13803
|
+
if (originalBuf[0] !== 80 || originalBuf[1] !== 75) {
|
|
13804
|
+
return "\uC624\uB958: \uD30C\uC77C\uC774 \uC720\uD6A8\uD55C .hwpx(ZIP) \uD3EC\uB9F7\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uD30C\uC77C\uC774 \uC190\uC0C1\uB418\uC5C8\uAC70\uB098 \uAD6C\uD615 .hwp(OLE \uBC14\uC774\uB108\uB9AC) \uD3EC\uB9F7\uC785\uB2C8\uB2E4. \uD55C\uAE00 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C .hwpx\uB85C \uC800\uC7A5 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
13805
|
+
}
|
|
13806
|
+
const originalBytes = new Uint8Array(
|
|
13807
|
+
originalBuf.buffer,
|
|
13808
|
+
originalBuf.byteOffset,
|
|
13809
|
+
originalBuf.byteLength
|
|
13810
|
+
);
|
|
13811
|
+
let originalBlocks = null;
|
|
13812
|
+
try {
|
|
13813
|
+
const origResult = await parse4(originalBuf.buffer);
|
|
13814
|
+
if (origResult.success) {
|
|
13815
|
+
originalBlocks = origResult.blocks;
|
|
13816
|
+
}
|
|
13817
|
+
} catch {
|
|
13818
|
+
}
|
|
13819
|
+
let applyResult;
|
|
13820
|
+
try {
|
|
13821
|
+
applyResult = await applyOpsToHwpx(originalBytes, input.anchor, input.operations);
|
|
13822
|
+
} catch (e) {
|
|
13823
|
+
return `\uC624\uB958: \uD45C \uAD6C\uC870 \uD3B8\uC9D1 \uC911 \uC608\uC678\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. ${String(e)}`;
|
|
13824
|
+
}
|
|
13825
|
+
if (!applyResult.ok) {
|
|
13826
|
+
return applyResult.error;
|
|
13827
|
+
}
|
|
13828
|
+
const { buffer: newBytes, beforeDims, afterDims } = applyResult;
|
|
13829
|
+
const warnings = [];
|
|
13830
|
+
let exportedMd = "";
|
|
13831
|
+
let kordocOk = false;
|
|
13832
|
+
let exportedBlocks = null;
|
|
13833
|
+
try {
|
|
13834
|
+
const exportedResult = await parse4(newBytes.buffer);
|
|
13835
|
+
if (exportedResult.success) {
|
|
13836
|
+
exportedMd = exportedResult.markdown;
|
|
13837
|
+
exportedBlocks = exportedResult.blocks;
|
|
13838
|
+
kordocOk = true;
|
|
13839
|
+
}
|
|
13840
|
+
} catch {
|
|
13841
|
+
}
|
|
13842
|
+
if (!kordocOk) {
|
|
13843
|
+
return `\uC624\uB958: \uB0B4\uBCF4\uB0B8 \uBB38\uC11C\uB97C kordoc\uC73C\uB85C \uC7AC\uD30C\uC2F1\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uAC00 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC73C\uBBC0\uB85C \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
|
|
13844
|
+
}
|
|
13845
|
+
if (originalBlocks !== null && exportedBlocks !== null) {
|
|
13846
|
+
const lossResult = detectStructuralLoss(originalBlocks, exportedBlocks);
|
|
13847
|
+
if (lossResult.lost) {
|
|
13848
|
+
return `\uC624\uB958: XML \uD3B8\uC9D1 \uD6C4 \uAD6C\uC870 \uC190\uC2E4\uC774 \uAC10\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4(${lossResult.detail}). XML \uD328\uCE58\uC5D0 \uBC84\uADF8\uAC00 \uC788\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
|
|
13849
|
+
}
|
|
13850
|
+
}
|
|
13851
|
+
const hasDeleteOp = input.operations.some(
|
|
13852
|
+
(op) => op.type === "deleteRow" || op.type === "deleteColumn"
|
|
13853
|
+
);
|
|
13854
|
+
const anchorInExported = exportedMd.includes(input.anchor.trim());
|
|
13855
|
+
if (!anchorInExported) {
|
|
13856
|
+
if (!hasDeleteOp) {
|
|
13857
|
+
return `\uC624\uB958: \uC790\uAE30\uAC80\uC99D \uC2E4\uD328 \u2014 \uB0B4\uBCF4\uB0B8 \uBB38\uC11C\uC5D0\uC11C anchor "${input.anchor}"\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD45C \uAD6C\uC870\uAC00 \uC190\uC0C1\uB418\uC5C8\uC744 \uC218 \uC788\uC73C\uBBC0\uB85C \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
|
|
13858
|
+
}
|
|
13859
|
+
warnings.push(
|
|
13860
|
+
`\uC790\uAE30\uAC80\uC99D: \uB0B4\uBCF4\uB0B8 \uBB38\uC11C\uC5D0\uC11C anchor "${input.anchor}"\uAC00 \uBCF4\uC774\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC0AD\uC81C \uC5F0\uC0B0\uC73C\uB85C anchor\uAC00 \uD3EC\uD568\uB41C \uD589/\uC5F4\uC774 \uC81C\uAC70\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uACB0\uACFC\uB97C \uC9C1\uC811 \uD655\uC778\uD558\uC138\uC694.`
|
|
13861
|
+
);
|
|
13862
|
+
}
|
|
13863
|
+
const delta = computeExpectedDelta(input.operations);
|
|
13864
|
+
if (delta !== null) {
|
|
13865
|
+
const expectedRowCnt = beforeDims.rowCnt + delta.rowDelta;
|
|
13866
|
+
const expectedColCnt = beforeDims.colCnt + delta.colDelta;
|
|
13867
|
+
if (afterDims.rowCnt !== expectedRowCnt || afterDims.colCnt !== expectedColCnt) {
|
|
13868
|
+
return `\uC624\uB958: \uC790\uAE30\uAC80\uC99D \uC2E4\uD328 \u2014 \uC608\uC0C1 \uCE58\uC218 (${expectedRowCnt}\uD589 \xD7 ${expectedColCnt}\uC5F4)\uC640 \uC2E4\uC81C \uCE58\uC218 (${afterDims.rowCnt}\uD589 \xD7 ${afterDims.colCnt}\uC5F4)\uAC00 \uB2E4\uB985\uB2C8\uB2E4. \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
|
|
13869
|
+
}
|
|
13870
|
+
if (anchorInExported) {
|
|
13871
|
+
const dimVerify = await verifyOutputDims(
|
|
13872
|
+
newBytes,
|
|
13873
|
+
input.anchor,
|
|
13874
|
+
expectedRowCnt,
|
|
13875
|
+
expectedColCnt
|
|
13876
|
+
);
|
|
13877
|
+
if (!dimVerify.ok) {
|
|
13878
|
+
return `\uC624\uB958: ${dimVerify.error} \uD30C\uC77C\uC744 \uC800\uC7A5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`;
|
|
13879
|
+
}
|
|
13880
|
+
}
|
|
13881
|
+
}
|
|
13882
|
+
const { outputPath, willConvertFormat } = resolveOutputPath(safePath);
|
|
13883
|
+
const opDescriptions = input.operations.map((op, i) => {
|
|
13884
|
+
if (op.type === "insertRow")
|
|
13885
|
+
return ` ${i + 1}. \uD589 ${op.row} ${op.position === "below" ? "\uC544\uB798" : "\uC704"}\uC5D0 \uD589 \uC0BD\uC785`;
|
|
13886
|
+
if (op.type === "deleteRow") return ` ${i + 1}. \uD589 ${op.row} \uC0AD\uC81C`;
|
|
13887
|
+
if (op.type === "insertColumn")
|
|
13888
|
+
return ` ${i + 1}. \uC5F4 ${op.col} ${op.position === "right" ? "\uC624\uB978\uCABD" : "\uC67C\uCABD"}\uC5D0 \uC5F4 \uC0BD\uC785`;
|
|
13889
|
+
if (op.type === "deleteColumn") return ` ${i + 1}. \uC5F4 ${op.col} \uC0AD\uC81C`;
|
|
13890
|
+
return ` ${i + 1}. (${op.startRow},${op.startCol})~(${op.endRow},${op.endCol}) \uC140 \uBCD1\uD569`;
|
|
13891
|
+
});
|
|
13892
|
+
const diff = `anchor: "${input.anchor}" (\uD45C \uC778\uB371\uC2A4 ${applyResult.anchorTableIndex})
|
|
13893
|
+
\uC774\uC804: ${beforeDims.rowCnt}\uD589 \xD7 ${beforeDims.colCnt}\uC5F4
|
|
13894
|
+
\uC774\uD6C4: ${afterDims.rowCnt}\uD589 \xD7 ${afterDims.colCnt}\uC5F4
|
|
13895
|
+
\uC5F0\uC0B0 (${input.operations.length}\uAC1C):
|
|
13896
|
+
${opDescriptions.join("\n")}`;
|
|
13897
|
+
const stagedPath = await stageFile(ctx.sessionId, outputPath, newBytes);
|
|
13898
|
+
const proposalId = crypto.randomUUID();
|
|
13899
|
+
return {
|
|
13900
|
+
proposal: {
|
|
13901
|
+
id: proposalId,
|
|
13902
|
+
kind: "table-structure",
|
|
13903
|
+
targetPath: outputPath,
|
|
13904
|
+
stagedPath,
|
|
13905
|
+
summary: input.summary,
|
|
13906
|
+
diff,
|
|
13907
|
+
warnings,
|
|
13908
|
+
willConvertFormat
|
|
13909
|
+
},
|
|
13910
|
+
commit: async () => {
|
|
13911
|
+
const backupPath = await backupFile(safePath);
|
|
13912
|
+
await commitStaged(stagedPath, outputPath);
|
|
13913
|
+
const backupInfo = backupPath ? ` (\uBC31\uC5C5: ${backupPath})` : "";
|
|
13914
|
+
return `\uC800\uC7A5 \uC644\uB8CC: ${outputPath}${backupInfo}`;
|
|
13915
|
+
}
|
|
13916
|
+
};
|
|
13917
|
+
}
|
|
13918
|
+
};
|
|
12810
13919
|
var MAX_MARKDOWN_LENGTH2 = 8e4;
|
|
12811
13920
|
var MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
|
|
12812
13921
|
var PLAIN_TEXT_EXTS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text"]);
|
|
@@ -12883,11 +13992,11 @@ function searchExcerpts(markdown, query) {
|
|
|
12883
13992
|
}
|
|
12884
13993
|
return result;
|
|
12885
13994
|
}
|
|
12886
|
-
var readDocumentSchema =
|
|
12887
|
-
path:
|
|
12888
|
-
pages:
|
|
12889
|
-
outline:
|
|
12890
|
-
search:
|
|
13995
|
+
var readDocumentSchema = z10.object({
|
|
13996
|
+
path: z10.string().describe("\uC77D\uC744 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
13997
|
+
pages: z10.string().optional().describe('\uC77D\uC744 \uD398\uC774\uC9C0 \uBC94\uC704 (\uC608: "1-3", "1,3,5") \u2014 \uBBF8\uC9C0\uC815 \uC2DC \uC804\uCCB4'),
|
|
13998
|
+
outline: z10.boolean().optional().describe("\uD5E4\uB529(\uC81C\uBAA9) \uAD6C\uC870\uB9CC \uBC18\uD658\uD574 \uBB38\uC11C \uAC1C\uC694 \uD30C\uC545 (\uB300\uD615 \uBB38\uC11C \uD0D0\uC0C9\uC6A9)"),
|
|
13999
|
+
search: z10.string().optional().describe("\uD0A4\uC6CC\uB4DC\uAC00 \uD3EC\uD568\uB41C \uBD80\uBD84\uACFC \uC8FC\uBCC0 \uB9E5\uB77D\uB9CC \uBC18\uD658 (\uB300\uD615 \uBB38\uC11C\uC5D0\uC11C \uD544\uC694\uD55C \uBD80\uBD84\uB9CC \uC77D\uAE30)")
|
|
12891
14000
|
});
|
|
12892
14001
|
function applyReadMode(body, outline, search) {
|
|
12893
14002
|
if (outline === true) {
|
|
@@ -12916,11 +14025,11 @@ var readDocumentTool = {
|
|
|
12916
14025
|
const msg = e instanceof Error ? e.message : String(e);
|
|
12917
14026
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
12918
14027
|
}
|
|
12919
|
-
const ext =
|
|
14028
|
+
const ext = extname10(safePath).toLowerCase();
|
|
12920
14029
|
if (PLAIN_TEXT_EXTS.has(ext)) {
|
|
12921
14030
|
let raw;
|
|
12922
14031
|
try {
|
|
12923
|
-
raw = await
|
|
14032
|
+
raw = await readFile10(safePath, "utf-8");
|
|
12924
14033
|
} catch (e) {
|
|
12925
14034
|
const msg = e instanceof Error ? e.message : String(e);
|
|
12926
14035
|
return `\uC624\uB958: \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`;
|
|
@@ -12944,7 +14053,7 @@ var readDocumentTool = {
|
|
|
12944
14053
|
\u26A0\uFE0F \uB0B4\uC6A9\uC774 \uB108\uBB34 \uAE38\uC5B4 \uC57D 80,000\uC790\uC5D0\uC11C \uC798\uB838\uC2B5\uB2C8\uB2E4. (\uD3C9\uBB38 \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774\uB77C \uD398\uC774\uC9C0 \uB2E8\uC704 \uBD84\uD560 \uC77D\uAE30\uB294 \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)` : "";
|
|
12945
14054
|
return `${meta2}${body2}${truncationNotice2}`;
|
|
12946
14055
|
}
|
|
12947
|
-
const result = await
|
|
14056
|
+
const result = await parse5(safePath, input.pages ? { pages: input.pages } : void 0);
|
|
12948
14057
|
if (!result.success) {
|
|
12949
14058
|
const msg = kordocErrorMessage(result.code, `\uBB38\uC11C\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${result.error}`);
|
|
12950
14059
|
return `\uC624\uB958: ${msg}`;
|
|
@@ -13020,8 +14129,8 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
13020
14129
|
".csv",
|
|
13021
14130
|
".log"
|
|
13022
14131
|
]);
|
|
13023
|
-
var readFileSchema =
|
|
13024
|
-
path:
|
|
14132
|
+
var readFileSchema = z11.object({
|
|
14133
|
+
path: z11.string().describe("\uC77D\uC744 \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)")
|
|
13025
14134
|
});
|
|
13026
14135
|
var readFileTool = {
|
|
13027
14136
|
name: "read_file",
|
|
@@ -13045,8 +14154,8 @@ var readFileTool = {
|
|
|
13045
14154
|
if (info.size > MAX_SIZE) {
|
|
13046
14155
|
return `\uC624\uB958: \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${Math.round(info.size / 1024)}KB). \uCD5C\uB300 256KB\uAE4C\uC9C0 \uC77D\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`;
|
|
13047
14156
|
}
|
|
13048
|
-
const { extname:
|
|
13049
|
-
const ext =
|
|
14157
|
+
const { extname: extname13 } = await import("path");
|
|
14158
|
+
const ext = extname13(safePath).toLowerCase();
|
|
13050
14159
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
13051
14160
|
return `\uC624\uB958: '${ext}' \uD615\uC2DD\uC740 \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uB85C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uC11C \uD30C\uC77C\uC774\uB77C\uBA74 read_document \uD234\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
|
|
13052
14161
|
}
|
|
@@ -13061,9 +14170,9 @@ var readFileTool = {
|
|
|
13061
14170
|
}
|
|
13062
14171
|
};
|
|
13063
14172
|
var MAX_PREVIEW_CHARS = 1e4;
|
|
13064
|
-
var writeNewDocumentSchema =
|
|
13065
|
-
path:
|
|
13066
|
-
markdown:
|
|
14173
|
+
var writeNewDocumentSchema = z12.object({
|
|
14174
|
+
path: z12.string().describe("\uC0DD\uC131\uD560 \uBB38\uC11C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
14175
|
+
markdown: z12.string().describe("\uC0C8 \uBB38\uC11C \uB0B4\uC6A9 (\uB9C8\uD06C\uB2E4\uC6B4 \uD615\uC2DD)")
|
|
13067
14176
|
});
|
|
13068
14177
|
var writeNewDocumentTool = {
|
|
13069
14178
|
name: "write_new_document",
|
|
@@ -13075,7 +14184,7 @@ var writeNewDocumentTool = {
|
|
|
13075
14184
|
ctx
|
|
13076
14185
|
}) => {
|
|
13077
14186
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13078
|
-
const ext =
|
|
14187
|
+
const ext = extname11(safePath).toLowerCase();
|
|
13079
14188
|
try {
|
|
13080
14189
|
await stat5(safePath);
|
|
13081
14190
|
return `\uC624\uB958: \uD30C\uC77C\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${input.path}. \uAE30\uC874 \uD30C\uC77C\uC744 \uC218\uC815\uD558\uB824\uBA74 propose_edit\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`;
|
|
@@ -13125,12 +14234,12 @@ ${preview}`,
|
|
|
13125
14234
|
};
|
|
13126
14235
|
}
|
|
13127
14236
|
};
|
|
13128
|
-
var writeNewSpreadsheetSchema =
|
|
13129
|
-
path:
|
|
13130
|
-
sheets:
|
|
13131
|
-
|
|
13132
|
-
name:
|
|
13133
|
-
rows:
|
|
14237
|
+
var writeNewSpreadsheetSchema = z13.object({
|
|
14238
|
+
path: z13.string().describe("\uC0DD\uC131\uD560 XLSX \uD30C\uC77C \uACBD\uB85C (cwd \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C \uB610\uB294 \uC808\uB300 \uACBD\uB85C)"),
|
|
14239
|
+
sheets: z13.array(
|
|
14240
|
+
z13.object({
|
|
14241
|
+
name: z13.string().describe("\uC2DC\uD2B8 \uC774\uB984"),
|
|
14242
|
+
rows: z13.array(z13.array(z13.string())).describe("\uD589 \uB370\uC774\uD130 \uBC30\uC5F4 (\uAC01 \uD589\uC740 \uC140 \uAC12 \uBC30\uC5F4)")
|
|
13134
14243
|
})
|
|
13135
14244
|
).min(1).describe("\uC0DD\uC131\uD560 \uC2DC\uD2B8 \uBAA9\uB85D")
|
|
13136
14245
|
});
|
|
@@ -13144,7 +14253,7 @@ var writeNewSpreadsheetTool = {
|
|
|
13144
14253
|
ctx
|
|
13145
14254
|
}) => {
|
|
13146
14255
|
const safePath = await resolveSafePath(ctx.cwd, input.path);
|
|
13147
|
-
const ext =
|
|
14256
|
+
const ext = extname12(safePath).toLowerCase();
|
|
13148
14257
|
if (ext !== ".xlsx") {
|
|
13149
14258
|
return `\uC624\uB958: write_new_spreadsheet\uC740 .xlsx \uD30C\uC77C\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4. \uD604\uC7AC \uD655\uC7A5\uC790: ${ext}.`;
|
|
13150
14259
|
}
|
|
@@ -13206,7 +14315,9 @@ function createDocTools(_ctx) {
|
|
|
13206
14315
|
proposeEditTool,
|
|
13207
14316
|
proposeFormFillTool,
|
|
13208
14317
|
proposeCellEditTool,
|
|
14318
|
+
proposeFindReplaceTool,
|
|
13209
14319
|
proposeSheetEditTool,
|
|
14320
|
+
proposeTableStructureTool,
|
|
13210
14321
|
listFormObjectsTool,
|
|
13211
14322
|
proposeFormObjectTool,
|
|
13212
14323
|
writeNewDocumentTool,
|
|
@@ -14014,10 +15125,10 @@ async function runOnboarding() {
|
|
|
14014
15125
|
|
|
14015
15126
|
// src/update.ts
|
|
14016
15127
|
import { spawn } from "child_process";
|
|
14017
|
-
import { mkdir as mkdir4, readFile as
|
|
15128
|
+
import { mkdir as mkdir4, readFile as readFile11, writeFile as writeFile3 } from "fs/promises";
|
|
14018
15129
|
import { dirname as dirname3 } from "path";
|
|
14019
15130
|
function compareSemver(a, b) {
|
|
14020
|
-
const
|
|
15131
|
+
const parse6 = (v) => {
|
|
14021
15132
|
const clean = v.replace(/^v/, "").split("-")[0] ?? "";
|
|
14022
15133
|
const parts = clean.split(".").map((p) => {
|
|
14023
15134
|
const n = parseInt(p, 10);
|
|
@@ -14025,8 +15136,8 @@ function compareSemver(a, b) {
|
|
|
14025
15136
|
});
|
|
14026
15137
|
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
14027
15138
|
};
|
|
14028
|
-
const [aMaj, aMin, aPat] =
|
|
14029
|
-
const [bMaj, bMin, bPat] =
|
|
15139
|
+
const [aMaj, aMin, aPat] = parse6(a);
|
|
15140
|
+
const [bMaj, bMin, bPat] = parse6(b);
|
|
14030
15141
|
if (aMaj !== bMaj) return aMaj < bMaj ? -1 : 1;
|
|
14031
15142
|
if (aMin !== bMin) return aMin < bMin ? -1 : 1;
|
|
14032
15143
|
if (aPat !== bPat) return aPat < bPat ? -1 : 1;
|
|
@@ -14035,7 +15146,7 @@ function compareSemver(a, b) {
|
|
|
14035
15146
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
14036
15147
|
async function readCache(cachePath) {
|
|
14037
15148
|
try {
|
|
14038
|
-
const raw = await
|
|
15149
|
+
const raw = await readFile11(cachePath, "utf-8");
|
|
14039
15150
|
const parsed = JSON.parse(raw);
|
|
14040
15151
|
if (parsed !== null && typeof parsed === "object" && "checkedAt" in parsed && "latest" in parsed && typeof parsed.checkedAt === "string" && typeof parsed.latest === "string") {
|
|
14041
15152
|
return parsed;
|