legal-doc-editor 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +48 -23
- package/dist/io.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- **조·항·호 자동 번호**: 조를 넣거나 옮기면 제1조, ①, 1. 번호가 알아서 다시 매겨져요
|
|
8
8
|
- **`{{변수}}` 채우기**: 본문에 `{{갑}}`을 입력하면 입력칸이 생기고, 값을 넣으면 문서에 반영돼요
|
|
9
|
-
- **한글(.hwp·.hwpx)·Word(.docx·.doc) 열기·저장**: 일반 문단으로 쓴 계약서도 "제N조", "①", "1." 패턴을 인식해서 조·항·호 구조로
|
|
9
|
+
- **한글(.hwp·.hwpx)·Word(.docx·.doc) 열기·저장**: 일반 문단으로 쓴 계약서도 "제N조", "①", "1." 패턴을 인식해서 조·항·호 구조로 바꿔요. "제1조(목적) 이 계약은…"처럼 한 줄에 붙은 조는 제목과 본문으로 나눠요
|
|
10
10
|
- 저장은 `.docx`, `.hwpx`, `.hwp`(HWP 5.0)예요. `.hwpx`는 한글 2014 이상에서 열려요
|
|
11
11
|
- 옛 Word `.doc`(97–2003)는 열기만 돼요. 글자와 표만 옮기고 굵게 같은 글자 서식과 그림은 옮기지 않아요
|
|
12
12
|
- 파일 변환 라이브러리는 필요할 때만 불러와서 에디터 본체 번들은 가벼워요
|
package/dist/index.js
CHANGED
|
@@ -440,6 +440,30 @@ function be(e) {
|
|
|
440
440
|
}
|
|
441
441
|
return null;
|
|
442
442
|
}
|
|
443
|
+
var xe = "(([【〔", Se = "))]】〕";
|
|
444
|
+
function Ce(e) {
|
|
445
|
+
let t = e.length - e.trimStart().length, n = e.trimStart();
|
|
446
|
+
if (xe.includes(n[0] ?? "_")) {
|
|
447
|
+
let e = 0;
|
|
448
|
+
for (let r = 0; r < Math.min(n.length, 60); r++) if (xe.includes(n[r])) e++;
|
|
449
|
+
else if (Se.includes(n[r]) && --e === 0) return t + r + 1;
|
|
450
|
+
return 0;
|
|
451
|
+
}
|
|
452
|
+
return n.trim().length <= 20 && !/[.다]$/.test(n.trim()) ? e.length : 0;
|
|
453
|
+
}
|
|
454
|
+
function we(e, t) {
|
|
455
|
+
let n = e.ownerDocument.createRange();
|
|
456
|
+
n.setStart(e, 0), n.setEnd(e, e.childNodes.length);
|
|
457
|
+
let r = e.ownerDocument.createTreeWalker(e, NodeFilter.SHOW_TEXT), i = t;
|
|
458
|
+
for (let e = r.nextNode(); e; e = r.nextNode()) {
|
|
459
|
+
if (i <= e.data.length) {
|
|
460
|
+
n.setEnd(e, i);
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
i -= e.data.length;
|
|
464
|
+
}
|
|
465
|
+
return n.extractContents();
|
|
466
|
+
}
|
|
443
467
|
function Z(e, t) {
|
|
444
468
|
let n = typeof t == "number" ? t : e.textContent.match(t)[0].length, r = e.ownerDocument.createTreeWalker(e, NodeFilter.SHOW_TEXT);
|
|
445
469
|
for (let e = r.nextNode(); e && n > 0; e = r.nextNode()) {
|
|
@@ -479,8 +503,9 @@ function Q(e) {
|
|
|
479
503
|
let n = t.createElement("h3");
|
|
480
504
|
n.textContent = r, e.replaceWith(n), a = null, o.fill(0), s.fill(0);
|
|
481
505
|
} else if (e.tagName === "P" && Y.test(n)) {
|
|
506
|
+
Z(e, Y);
|
|
482
507
|
let n = t.createElement("h2");
|
|
483
|
-
n.
|
|
508
|
+
n.append(we(e, Ce(e.textContent ?? ""))), Z(e, /^\s*/), e.replaceWith(n), e.textContent.trim() && n.after(e), a = null, o.fill(0), s.fill(0);
|
|
484
509
|
} else if (e.tagName === "P" && he.test(n)) {
|
|
485
510
|
let r = n.trim().charCodeAt(0) - 9311;
|
|
486
511
|
(!a || a.children.length + Number(a.getAttribute("start") ?? 1) !== r) && (e.before(a = t.createElement("ol")), r > 1 && a.setAttribute("start", String(r))), Z(e, he);
|
|
@@ -500,50 +525,50 @@ function Q(e) {
|
|
|
500
525
|
}
|
|
501
526
|
return t.body.innerHTML;
|
|
502
527
|
}
|
|
503
|
-
async function
|
|
528
|
+
async function Te(e) {
|
|
504
529
|
let { default: t } = await import("mammoth"), { value: n } = await t.convertToHtml({ arrayBuffer: await e.arrayBuffer() });
|
|
505
530
|
return Q(n);
|
|
506
531
|
}
|
|
507
|
-
async function
|
|
532
|
+
async function Ee(e) {
|
|
508
533
|
let { detectFormat: t, hwpToHwpx: n, HwpxReader: r } = await import("hwp-convert"), i = new Uint8Array(await e.arrayBuffer());
|
|
509
534
|
t(i) === "hwp" && (i = new Uint8Array(await n(i)));
|
|
510
535
|
let a = new r();
|
|
511
536
|
return await a.loadFromArrayBuffer(i.buffer), Q(await a.extractHtml());
|
|
512
537
|
}
|
|
513
|
-
async function
|
|
538
|
+
async function De(e) {
|
|
514
539
|
let { parseMsDoc: t } = await import("@file-viewer/doc");
|
|
515
|
-
return Q(
|
|
540
|
+
return Q(ke(t(new Uint8Array(await e.arrayBuffer())).blocks));
|
|
516
541
|
}
|
|
517
|
-
var
|
|
518
|
-
function
|
|
519
|
-
let t = (e) =>
|
|
542
|
+
var Oe = (e) => e.replace(/[\x00-\x08\x0c\x0e-\x1f]/g, "");
|
|
543
|
+
function ke(e) {
|
|
544
|
+
let t = (e) => Oe(e).split("\r").filter((e) => e.trim()).map((e) => `<p>${K(e).replace(/\v/g, "<br>")}</p>`).join("");
|
|
520
545
|
return e.map((e) => {
|
|
521
546
|
if (e.type === "paragraph") return t(e.text);
|
|
522
547
|
if (e.type !== "table") return "";
|
|
523
|
-
let n = (e) => `<td${(e.colspan ?? 1) > 1 ? ` colspan="${e.colspan}"` : ""}${(e.rowspan ?? 1) > 1 ? ` rowspan="${e.rowspan}"` : ""}>${e.paragraphs.map((e) => K(
|
|
548
|
+
let n = (e) => `<td${(e.colspan ?? 1) > 1 ? ` colspan="${e.colspan}"` : ""}${(e.rowspan ?? 1) > 1 ? ` rowspan="${e.rowspan}"` : ""}>${e.paragraphs.map((e) => K(Oe(e.text).replace(/[\r\v]/g, ""))).filter(Boolean).join("<br>")}</td>`;
|
|
524
549
|
return `<table>${e.rows.map((e) => `<tr>${e.cells.filter((e) => !e.hidden).map(n).join("")}</tr>`).join("")}</table>`;
|
|
525
550
|
}).join("");
|
|
526
551
|
}
|
|
527
|
-
function
|
|
528
|
-
return /\.docx$/i.test(e.name) ?
|
|
552
|
+
function Ae(e) {
|
|
553
|
+
return /\.docx$/i.test(e.name) ? Te(e) : /\.doc$/i.test(e.name) ? De(e) : /\.hwpx?$/i.test(e.name) ? Ee(e) : Promise.reject(/* @__PURE__ */ Error(".docx, .doc, .hwp, .hwpx 파일만 열 수 있어요"));
|
|
529
554
|
}
|
|
530
555
|
//#endregion
|
|
531
556
|
//#region src/LegalEditor.tsx
|
|
532
|
-
function
|
|
557
|
+
function $(e) {
|
|
533
558
|
let t = /* @__PURE__ */ new Set();
|
|
534
559
|
return e.state.doc.descendants((e) => {
|
|
535
560
|
e.type.name === "variable" && t.add(e.attrs.name);
|
|
536
561
|
}), [...t];
|
|
537
562
|
}
|
|
538
|
-
function
|
|
563
|
+
function je() {
|
|
539
564
|
let e = document.documentElement;
|
|
540
565
|
e.classList.add("le-printing"), window.addEventListener("afterprint", () => e.classList.remove("le-printing"), { once: !0 }), window.print();
|
|
541
566
|
}
|
|
542
|
-
function
|
|
567
|
+
function Me(e, t) {
|
|
543
568
|
let n = document.createElement("a");
|
|
544
569
|
n.href = URL.createObjectURL(e), n.download = t, n.click(), setTimeout(() => URL.revokeObjectURL(n.href), 1e3);
|
|
545
570
|
}
|
|
546
|
-
function
|
|
571
|
+
function Ne({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, editable: u = !0, autoFees: d, searchCases: f, clauses: p, hwpWasmUrl: ee }) {
|
|
547
572
|
let [v, b] = t(s), [x, S] = t([]), [C, te] = t(""), [w, T] = t(null), [E, D] = t(""), [O, A] = t(""), j = d ? re(x, v, d === !0 ? {} : d) : v, N = r({
|
|
548
573
|
extensions: [
|
|
549
574
|
i.configure({ heading: { levels: [
|
|
@@ -560,9 +585,9 @@ function ke({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
560
585
|
editable: u,
|
|
561
586
|
immediatelyRender: !1,
|
|
562
587
|
editorProps: { attributes: { class: "legal-doc" } },
|
|
563
|
-
onCreate: ({ editor: e }) => S(
|
|
588
|
+
onCreate: ({ editor: e }) => S($(e)),
|
|
564
589
|
onUpdate: ({ editor: e }) => {
|
|
565
|
-
S(
|
|
590
|
+
S($(e)), c?.(e.getHTML());
|
|
566
591
|
}
|
|
567
592
|
});
|
|
568
593
|
e(() => {
|
|
@@ -583,14 +608,14 @@ function ke({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
583
608
|
}, I = async (e) => {
|
|
584
609
|
let t = e.target.files?.[0];
|
|
585
610
|
if (e.target.value = "", t && N) try {
|
|
586
|
-
N.commands.setContent(_(await
|
|
611
|
+
N.commands.setContent(_(await Ae(t)), { emitUpdate: !0 });
|
|
587
612
|
} catch (e) {
|
|
588
613
|
window.alert(`파일을 열 수 없어요: ${e.message}`);
|
|
589
614
|
}
|
|
590
615
|
}, L = async (e, t) => {
|
|
591
616
|
let n = N.state.doc.firstChild?.textContent.trim() || "문서";
|
|
592
617
|
try {
|
|
593
|
-
|
|
618
|
+
Me(await e(N.getJSON(), j), `${n}.${t}`);
|
|
594
619
|
} catch (e) {
|
|
595
620
|
window.alert(`저장하지 못했어요: ${e.message}`);
|
|
596
621
|
}
|
|
@@ -718,7 +743,7 @@ function ke({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
718
743
|
}),
|
|
719
744
|
/* @__PURE__ */ m("button", {
|
|
720
745
|
type: "button",
|
|
721
|
-
onClick:
|
|
746
|
+
onClick: je,
|
|
722
747
|
children: "인쇄 · PDF"
|
|
723
748
|
})
|
|
724
749
|
]
|
|
@@ -815,7 +840,7 @@ function ke({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
815
840
|
}
|
|
816
841
|
//#endregion
|
|
817
842
|
//#region src/templates.ts
|
|
818
|
-
var
|
|
843
|
+
var Pe = [
|
|
819
844
|
{
|
|
820
845
|
id: "complaint-loan",
|
|
821
846
|
title: "소장 (대여금)",
|
|
@@ -840,7 +865,7 @@ var Ae = [
|
|
|
840
865
|
description: "재판 없이 법원의 지급명령을 받는 독촉절차 신청서. 인지대는 소장의 10분의 1, 송달료는 당사자 수 × 6회분",
|
|
841
866
|
html: "\n<h1>지 급 명 령 신 청 서</h1>\n<p>채 권 자 {{채권자 이름}}</p>\n<p>주소 {{채권자 주소}}</p>\n<p>연락처 {{채권자 연락처}}</p>\n<p>채 무 자 {{채무자 이름}}</p>\n<p>주소 {{채무자 주소}}</p>\n<p>대여금 청구 독촉사건</p>\n<p>청구금액 {{청구금액}}</p>\n<h3>신 청 취 지</h3>\n<p>채무자는 채권자에게 아래 청구금액 및 독촉절차비용을 지급하라는 명령을 구합니다.</p>\n<p data-num=\"1\">{{청구금액}}</p>\n<p data-num=\"1\">위 금액에 대하여 {{변제기 다음 날}}부터 이 사건 지급명령정본을 송달받은 날까지는 연 5%의, 그 다음 날부터 다 갚는 날까지는 연 12%의 각 비율로 계산한 돈</p>\n<p data-num=\"1\">독촉절차비용 {{독촉절차비용}} (인지대 {{독촉절차 인지대}}, 송달료 {{독촉절차 송달료}})</p>\n<h3>신 청 이 유</h3>\n<p data-num=\"1\">채권자는 {{대여일}} 채무자에게 {{청구금액}}을 변제기 {{변제기}}로 정하여 빌려주었습니다.</p>\n<p data-num=\"1\">그러나 채무자는 변제기가 지났는데도 위 돈을 갚지 않고 있습니다.</p>\n<p data-num=\"1\">따라서 채권자는 채무자에게 위 청구금액과 지연손해금 및 독촉절차비용의 지급을 구하기 위하여 이 신청에 이르렀습니다.</p>\n<h3>첨 부 서 류</h3>\n<p data-num=\"1\">차용증 사본 1통</p>\n<p data-num=\"1\">송달료 납부서 1통</p>\n<p>{{작성일}}</p>\n<p>채권자 {{채권자 이름}} (서명 또는 날인)</p>\n<p>{{관할 법원}} 귀중</p>\n"
|
|
842
867
|
}
|
|
843
|
-
],
|
|
868
|
+
], Fe = [
|
|
844
869
|
{
|
|
845
870
|
id: "purpose",
|
|
846
871
|
title: "목적",
|
|
@@ -909,4 +934,4 @@ var Ae = [
|
|
|
909
934
|
}
|
|
910
935
|
];
|
|
911
936
|
//#endregion
|
|
912
|
-
export { M as CitationLink,
|
|
937
|
+
export { M as CitationLink, Ne as LegalEditor, y as Numbering, I as SERVICE_ROUNDS, L as SERVICE_UNIT_FEE, z as SMALL_CLAIM_LIMIT, g as Variable, F as calcPaymentOrderStampFee, R as calcServiceFee, N as calcStampFee, Fe as clauses, ee as fillTemplate, O as findCitations, H as formatAmount, k as formatCaseCitation, De as fromDoc, Te as fromDocx, Ae as fromFile, Ee as fromHwp, Q as normalizeLegalHtml, se as parseAmount, Pe as templates, _ as toChips, pe as toDocx, me as toHwp, q as toHwpx, V as toKoreanAmount, de as toPlainHtml, re as withCourtFees };
|
package/dist/io.d.ts
CHANGED
|
@@ -15,6 +15,13 @@ export declare function toHwpx(doc: JSONContent, values: Values): Promise<Blob>;
|
|
|
15
15
|
export declare function toHwp(doc: JSONContent, values: Values, opts?: {
|
|
16
16
|
wasm?: string | URL | BufferSource;
|
|
17
17
|
}): Promise<Blob>;
|
|
18
|
+
/**
|
|
19
|
+
* "제N조"를 뗀 나머지에서 조 제목이 차지하는 글자 수.
|
|
20
|
+
* - 괄호 제목 "(목적) 본문" → 괄호까지 (괄호 안 괄호도 짝을 셈)
|
|
21
|
+
* - 괄호 없는 짧은 한 줄 "목적" → 전체
|
|
22
|
+
* - 괄호 없이 문장 "갑은 …한다." → 0 (제목 없음, 전부 본문)
|
|
23
|
+
*/
|
|
24
|
+
export declare function articleTitleLength(text: string): number;
|
|
18
25
|
/**
|
|
19
26
|
* 문단으로만 된 법률문서 HTML(한글·워드에서 온 것)을 에디터 구조로 정리.
|
|
20
27
|
* - 빈 줄 문단 제거, 짧은 첫 줄 → 제목(h1), "청 구 취 지" 같은 소제목 → h3, [빈칸] → {{변수}}
|