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