legal-doc-editor 0.5.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 +4 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +155 -116
- package/dist/io.d.ts +16 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
- **조·항·호 자동 번호**: 조를 넣거나 옮기면 제1조, ①, 1. 번호가 알아서 다시 매겨져요
|
|
8
8
|
- **`{{변수}}` 채우기**: 본문에 `{{갑}}`을 입력하면 입력칸이 생기고, 값을 넣으면 문서에 반영돼요
|
|
9
|
-
- **한글(.hwp·.hwpx)·Word(.docx) 열기·저장**: 일반 문단으로 쓴 계약서도 "제N조", "①", "1." 패턴을 인식해서 조·항·호 구조로
|
|
9
|
+
- **한글(.hwp·.hwpx)·Word(.docx·.doc) 열기·저장**: 일반 문단으로 쓴 계약서도 "제N조", "①", "1." 패턴을 인식해서 조·항·호 구조로 바꿔요. "제1조(목적) 이 계약은…"처럼 한 줄에 붙은 조는 제목과 본문으로 나눠요
|
|
10
10
|
- 저장은 `.docx`, `.hwpx`, `.hwp`(HWP 5.0)예요. `.hwpx`는 한글 2014 이상에서 열려요
|
|
11
|
+
- 옛 Word `.doc`(97–2003)는 열기만 돼요. 글자와 표만 옮기고 굵게 같은 글자 서식과 그림은 옮기지 않아요
|
|
11
12
|
- 파일 변환 라이브러리는 필요할 때만 불러와서 에디터 본체 번들은 가벼워요
|
|
12
13
|
- **판례·법령 인용 링크**: 본문의 `민법 제750조`, `대법원 2016. 4. 28. 선고 2015다12345 판결`, `2024가단157033` 같은 인용을 알아보고 [국가법령정보센터](https://www.law.go.kr) 링크를 붙여요 (API 키가 필요 없어요)
|
|
13
14
|
- **조항 라이브러리**: 비밀유지·계약해지·관할법원 같은 자주 쓰는 조항을 골라 커서 위치에 넣어요. 조 번호는 알아서 다시 매겨져요
|
|
@@ -154,7 +155,7 @@ import { LegalEditor, clauses } from 'legal-doc-editor'
|
|
|
154
155
|
| `toHwpx(editor.getJSON(), values)` | `.hwpx` Blob 생성 |
|
|
155
156
|
| `toHwp(editor.getJSON(), values, { wasm })` | `.hwp`(HWP 5.0) Blob 생성. `wasm`은 WASM 주소나 바이트(선택) |
|
|
156
157
|
| `toPlainHtml(editor.getJSON(), values)` | 번호가 텍스트로 들어간 독립 HTML |
|
|
157
|
-
| `fromFile(file)` | `.docx` / `.hwp` / `.hwpx`를 에디터용 HTML로 변환 (`fromDocx`, `fromHwp`도 있음) |
|
|
158
|
+
| `fromFile(file)` | `.docx` / `.doc` / `.hwp` / `.hwpx`를 에디터용 HTML로 변환 (`fromDocx`, `fromDoc`, `fromHwp`도 있음) |
|
|
158
159
|
| `normalizeLegalHtml(html)` | "제N조 / ① / 1." 문단을 조·항·호 구조로 변환 |
|
|
159
160
|
| `Variable` | 다른 Tiptap 에디터에 넣어 쓸 수 있는 변수 확장 |
|
|
160
161
|
| `Numbering` | 번호 문단 확장 (`setNumbering(1~4 \| null)`, Tab·Shift+Tab 단계 변경, 맨 앞 Backspace 해제) |
|
|
@@ -165,7 +166,7 @@ import { LegalEditor, clauses } from 'legal-doc-editor'
|
|
|
165
166
|
|
|
166
167
|
- [x] HWP / HWPX 열기, HWPX 저장
|
|
167
168
|
- [x] `.hwp` 바이너리로 바로 저장 ([rhwp](https://github.com/edwardkim/rhwp))
|
|
168
|
-
- [
|
|
169
|
+
- [x] 옛 Word `.doc` 열기 ([@file-viewer/doc](https://www.npmjs.com/package/@file-viewer/doc))
|
|
169
170
|
- [ ] 표
|
|
170
171
|
- [ ] 조항 라이브러리(자주 쓰는 조항 끼워 넣기)
|
|
171
172
|
|
package/dist/index.d.ts
CHANGED
|
@@ -12,4 +12,4 @@ export { clauses } from './clauses';
|
|
|
12
12
|
export type { Clause } from './clauses';
|
|
13
13
|
export type { Template } from './templates';
|
|
14
14
|
export type { Values } from './variable';
|
|
15
|
-
export { toDocx, toHwpx, toHwp, toPlainHtml, fromDocx, fromHwp, fromFile, normalizeLegalHtml } from './io';
|
|
15
|
+
export { toDocx, toHwpx, toHwp, toPlainHtml, fromDocx, fromDoc, fromHwp, fromFile, normalizeLegalHtml } from './io';
|
package/dist/index.js
CHANGED
|
@@ -71,7 +71,7 @@ function ee(e, t) {
|
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|
|
73
73
|
//#region src/numbering.ts
|
|
74
|
-
var v = 4,
|
|
74
|
+
var v = 4, y = o.create({
|
|
75
75
|
name: "numbering",
|
|
76
76
|
priority: 1e3,
|
|
77
77
|
addGlobalAttributes() {
|
|
@@ -105,31 +105,31 @@ var v = 4, te = o.create({
|
|
|
105
105
|
Enter: () => !!e() && this.editor.state.selection.$from.parent.content.size === 0 && this.editor.commands.setNumbering(null)
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
|
-
}),
|
|
109
|
-
function
|
|
108
|
+
}), b = String.raw`(?<!\d)\d{4}(?![만억천년월일원])[가-힣]{1,3}\d{1,6}(?!\d)`, x = String.raw`(?:[가-힣]{0,6}법원(?:\s*[가-힣]{2,6}지원)?|헌법재판소)`, S = String.raw`\d{4}\.\s*\d{1,2}\.\s*\d{1,2}\.`, C = String.raw`${x}\s*${S}\s*(?:선고|자)\s*(?<caseNo>${b})(?:\s*[,·]\s*${b})*\s*(?:전원합의체\s*)?(?:판결|결정|명령)`, te = String.raw`(?:[가-힣]+(?<![은는이가을를와과로며고여하한된])\s+){0,4}[가-힣]+에\s*관한\s*[가-힣]*법률?|[가-힣]+법률?`, w = String.raw`(?<lawName>${te})\s*제\s*(?<jo>\d+)\s*조(?:\s*의\s*(?<ui>\d+))?(?:\s*제\s*\d+\s*(?:항|호))*`, T = new RegExp(String.raw`${C}|${w}|(?<bare>${b})`, "g"), E = /^[방불합위적탈편문어용수요기비사입공]법$/, D = (...e) => `https://www.law.go.kr/${e.map(encodeURIComponent).join("/")}`;
|
|
109
|
+
function O(e) {
|
|
110
110
|
let t = [];
|
|
111
|
-
for (let n of e.matchAll(
|
|
111
|
+
for (let n of e.matchAll(T)) {
|
|
112
112
|
let e = n.groups, r = e.caseNo ?? e.bare;
|
|
113
|
-
if (!r &&
|
|
113
|
+
if (!r && E.test(e.lawName)) continue;
|
|
114
114
|
let i = n.index ?? 0;
|
|
115
115
|
t.push({
|
|
116
116
|
type: r ? "case" : "law",
|
|
117
117
|
text: n[0],
|
|
118
118
|
from: i,
|
|
119
119
|
to: i + n[0].length,
|
|
120
|
-
url: r ? `https://www.law.go.kr/LSW/precSc.do?query=${encodeURIComponent(r)}` :
|
|
120
|
+
url: r ? `https://www.law.go.kr/LSW/precSc.do?query=${encodeURIComponent(r)}` : D("법령", e.lawName.replace(/\s+/g, " "), `제${e.jo}조${e.ui ? `의${e.ui}` : ""}`)
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
return t;
|
|
124
124
|
}
|
|
125
|
-
function
|
|
125
|
+
function k({ court: e, date: t, caseNo: n, kind: r = "" }) {
|
|
126
126
|
let i = t.match(/(\d{4})\D*(\d{1,2})\D*(\d{1,2})/), a = i ? `${i[1]}. ${+i[2]}. ${+i[3]}.` : t, o = /결정/.test(r) ? "결정" : /명령/.test(r) ? "명령" : "판결";
|
|
127
127
|
return o === "판결" ? `${e} ${a} 선고 ${n} 판결` : `${e} ${a}자 ${n} ${o}`;
|
|
128
128
|
}
|
|
129
|
-
var
|
|
129
|
+
var A = new d("citation"), j = (e) => {
|
|
130
130
|
let t = [];
|
|
131
131
|
return e.descendants((e, n) => {
|
|
132
|
-
if (e.isText) for (let r of
|
|
132
|
+
if (e.isText) for (let r of O(e.text)) t.push(f.inline(n + r.from, n + r.to, {
|
|
133
133
|
nodeName: "a",
|
|
134
134
|
class: "le-cite",
|
|
135
135
|
href: r.url,
|
|
@@ -138,17 +138,17 @@ var k = new d("citation"), A = (e) => {
|
|
|
138
138
|
rel: "noopener noreferrer"
|
|
139
139
|
}));
|
|
140
140
|
}), p.create(e, t);
|
|
141
|
-
},
|
|
141
|
+
}, M = o.create({
|
|
142
142
|
name: "citationLink",
|
|
143
143
|
addProseMirrorPlugins() {
|
|
144
144
|
return [new u({
|
|
145
|
-
key:
|
|
145
|
+
key: A,
|
|
146
146
|
state: {
|
|
147
|
-
init: (e, { doc: t }) =>
|
|
148
|
-
apply: (e, t) => e.docChanged ?
|
|
147
|
+
init: (e, { doc: t }) => j(t),
|
|
148
|
+
apply: (e, t) => e.docChanged ? j(e.doc) : t
|
|
149
149
|
},
|
|
150
150
|
props: {
|
|
151
|
-
decorations: (e) =>
|
|
151
|
+
decorations: (e) => A.getState(e),
|
|
152
152
|
handleClick: (e, t, n) => {
|
|
153
153
|
let r = n.target.closest?.("a.le-cite");
|
|
154
154
|
return !r || e.editable && !n.ctrlKey && !n.metaKey ? !1 : (window.open(r.getAttribute("href"), "_blank", "noopener,noreferrer"), !0);
|
|
@@ -159,21 +159,21 @@ var k = new d("citation"), A = (e) => {
|
|
|
159
159
|
});
|
|
160
160
|
//#endregion
|
|
161
161
|
//#region src/fees.ts
|
|
162
|
-
function
|
|
162
|
+
function N(e, t = {}) {
|
|
163
163
|
if (!(e > 0)) return 0;
|
|
164
|
-
let n =
|
|
164
|
+
let n = P(e);
|
|
165
165
|
return t.electronic ? n < 1e7 ? 900 : Math.floor(n * 9 / 1e7) * 100 : n < 1e7 ? 1e3 : Math.floor(n / 1e6) * 100;
|
|
166
166
|
}
|
|
167
|
-
function
|
|
167
|
+
function P(e) {
|
|
168
168
|
let [t, n] = e < 1e7 ? [50, 0] : e < 1e8 ? [45, 5e3] : e < 1e9 ? [40, 55e3] : [35, 555e3];
|
|
169
169
|
return e * t + n * 1e4;
|
|
170
170
|
}
|
|
171
|
-
function
|
|
171
|
+
function F(e) {
|
|
172
172
|
if (!(e > 0)) return 0;
|
|
173
|
-
let t =
|
|
173
|
+
let t = P(e);
|
|
174
174
|
return t < 1e8 ? 1e3 : Math.floor(t / 1e7) * 100;
|
|
175
175
|
}
|
|
176
|
-
var
|
|
176
|
+
var I = {
|
|
177
177
|
소액: 10,
|
|
178
178
|
단독: 15,
|
|
179
179
|
합의: 15,
|
|
@@ -181,23 +181,23 @@ var F = {
|
|
|
181
181
|
상고: 8,
|
|
182
182
|
조정: 5,
|
|
183
183
|
독촉: 6
|
|
184
|
-
},
|
|
185
|
-
function
|
|
186
|
-
return e *
|
|
184
|
+
}, L = 5500;
|
|
185
|
+
function R({ parties: e, procedure: t, unitFee: n = L }) {
|
|
186
|
+
return e * I[t] * n;
|
|
187
187
|
}
|
|
188
|
-
var
|
|
189
|
-
function
|
|
190
|
-
let r = (n) =>
|
|
191
|
-
for (let a of e) n.test(a) && !t[a]?.trim() && (i[a] =
|
|
188
|
+
var z = 3e7, ne = (e) => `${e.toLocaleString("ko-KR")}원`, B = (e = "") => Number(e.replace(/[^\d]/g, "")) || 0;
|
|
189
|
+
function re(e, t, n = {}) {
|
|
190
|
+
let r = (n) => B(t[e.find((e) => n.test(e)) ?? ""]), i = { ...t }, a = (n, r) => {
|
|
191
|
+
for (let a of e) n.test(a) && !t[a]?.trim() && (i[a] = ne(r));
|
|
192
192
|
}, o = n.parties ?? 2, s = r(/^(소가|소송목적의\s*값)/);
|
|
193
|
-
s && (a(/^인지(액|대)/,
|
|
193
|
+
s && (a(/^인지(액|대)/, N(s, n)), a(/^송달료/, R({
|
|
194
194
|
parties: o,
|
|
195
195
|
procedure: s <= 3e7 ? "소액" : "단독",
|
|
196
196
|
unitFee: n.unitFee
|
|
197
197
|
})));
|
|
198
198
|
let c = r(/^청구금액/);
|
|
199
199
|
if (c && e.some((e) => /^독촉절차/.test(e))) {
|
|
200
|
-
let e = /^독촉절차\s*인지(액|대)/, t = /^독촉절차\s*송달료/, i =
|
|
200
|
+
let e = /^독촉절차\s*인지(액|대)/, t = /^독촉절차\s*송달료/, i = F(c), s = R({
|
|
201
201
|
parties: o,
|
|
202
202
|
procedure: "독촉",
|
|
203
203
|
unitFee: n.unitFee
|
|
@@ -208,36 +208,36 @@ function ie(e, t, n = {}) {
|
|
|
208
208
|
}
|
|
209
209
|
//#endregion
|
|
210
210
|
//#region src/amount.ts
|
|
211
|
-
var
|
|
211
|
+
var ie = "영일이삼사오육칠팔구", ae = [
|
|
212
212
|
"천",
|
|
213
213
|
"백",
|
|
214
214
|
"십",
|
|
215
215
|
""
|
|
216
|
-
],
|
|
216
|
+
], oe = [
|
|
217
217
|
"",
|
|
218
218
|
"만",
|
|
219
219
|
"억",
|
|
220
220
|
"조",
|
|
221
221
|
"경"
|
|
222
222
|
];
|
|
223
|
-
function
|
|
223
|
+
function V(e) {
|
|
224
224
|
if (!Number.isSafeInteger(e)) throw RangeError(`금액은 정수여야 해요: ${e}`);
|
|
225
225
|
if (e === 0) return "영";
|
|
226
|
-
if (e < 0) return `마이너스 ${
|
|
226
|
+
if (e < 0) return `마이너스 ${V(-e)}`;
|
|
227
227
|
let t = "", n = String(e).padStart(Math.ceil(String(e).length / 4) * 4, "0");
|
|
228
228
|
for (let e = 0; e < n.length / 4; e++) {
|
|
229
229
|
let r = n.slice(e * 4, e * 4 + 4);
|
|
230
230
|
if (r !== "0000") {
|
|
231
|
-
for (let e = 0; e < 4; e++) r[e] !== "0" && (t +=
|
|
232
|
-
t +=
|
|
231
|
+
for (let e = 0; e < 4; e++) r[e] !== "0" && (t += ie[+r[e]] + ae[e]);
|
|
232
|
+
t += oe[n.length / 4 - 1 - e];
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
return t;
|
|
236
236
|
}
|
|
237
|
-
var
|
|
237
|
+
var se = (e) => Number(e.replace(/[^\d]/g, "")) || 0;
|
|
238
238
|
function H(e, t = "소장") {
|
|
239
239
|
let n = e.toLocaleString("ko-KR");
|
|
240
|
-
return t === "소장" ? `금 ${n}원` : `금 ${
|
|
240
|
+
return t === "소장" ? `금 ${n}원` : `금 ${V(e)} 원정(₩${n})`;
|
|
241
241
|
}
|
|
242
242
|
//#endregion
|
|
243
243
|
//#region src/io.ts
|
|
@@ -327,18 +327,18 @@ function G(e, t) {
|
|
|
327
327
|
};
|
|
328
328
|
return c(e.content), n;
|
|
329
329
|
}
|
|
330
|
-
var
|
|
330
|
+
var K = (e) => e.replace(/[&<>"]/g, (e) => `&#${e.charCodeAt(0)};`), ue = (e) => e.map((e) => {
|
|
331
331
|
if (e.br) return "<br>";
|
|
332
|
-
let t =
|
|
332
|
+
let t = K(e.text);
|
|
333
333
|
return e.bold && (t = `<b>${t}</b>`), e.italics && (t = `<i>${t}</i>`), e.underline && (t = `<u>${t}</u>`), e.strike && (t = `<s>${t}</s>`), t;
|
|
334
334
|
}).join("");
|
|
335
335
|
function de(e, t) {
|
|
336
336
|
return G(e, t).map((e) => {
|
|
337
337
|
if (e.kind === "table") {
|
|
338
|
-
let t = (e) => `<td${e.colspan > 1 ? ` colspan="${e.colspan}"` : ""}${e.rowspan > 1 ? ` rowspan="${e.rowspan}"` : ""}>${
|
|
338
|
+
let t = (e) => `<td${e.colspan > 1 ? ` colspan="${e.colspan}"` : ""}${e.rowspan > 1 ? ` rowspan="${e.rowspan}"` : ""}>${ue(e.runs)}</td>`;
|
|
339
339
|
return `<table>${e.rows.map((e) => `<tr>${e.map(t).join("")}</tr>`).join("")}</table>`;
|
|
340
340
|
}
|
|
341
|
-
let t = (e.label ? e.kind === "article" ? `<b>${e.label}</b> ` : `${e.label} ` : "") +
|
|
341
|
+
let t = (e.label ? e.kind === "article" ? `<b>${e.label}</b> ` : `${e.label} ` : "") + ue(e.runs);
|
|
342
342
|
if (e.kind === "title") return `<h1>${t}</h1>`;
|
|
343
343
|
if (e.kind === "section") return `<h3>${t}</h3>`;
|
|
344
344
|
let n = e.label ? e.depth - 1 : e.depth;
|
|
@@ -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,37 +525,51 @@ 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
|
-
function
|
|
514
|
-
|
|
538
|
+
async function De(e) {
|
|
539
|
+
let { parseMsDoc: t } = await import("@file-viewer/doc");
|
|
540
|
+
return Q(ke(t(new Uint8Array(await e.arrayBuffer())).blocks));
|
|
541
|
+
}
|
|
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("");
|
|
545
|
+
return e.map((e) => {
|
|
546
|
+
if (e.type === "paragraph") return t(e.text);
|
|
547
|
+
if (e.type !== "table") return "";
|
|
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>`;
|
|
549
|
+
return `<table>${e.rows.map((e) => `<tr>${e.cells.filter((e) => !e.hidden).map(n).join("")}</tr>`).join("")}</table>`;
|
|
550
|
+
}).join("");
|
|
551
|
+
}
|
|
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 파일만 열 수 있어요"));
|
|
515
554
|
}
|
|
516
555
|
//#endregion
|
|
517
556
|
//#region src/LegalEditor.tsx
|
|
518
|
-
function
|
|
557
|
+
function $(e) {
|
|
519
558
|
let t = /* @__PURE__ */ new Set();
|
|
520
559
|
return e.state.doc.descendants((e) => {
|
|
521
560
|
e.type.name === "variable" && t.add(e.attrs.name);
|
|
522
561
|
}), [...t];
|
|
523
562
|
}
|
|
524
|
-
function
|
|
563
|
+
function je() {
|
|
525
564
|
let e = document.documentElement;
|
|
526
565
|
e.classList.add("le-printing"), window.addEventListener("afterprint", () => e.classList.remove("le-printing"), { once: !0 }), window.print();
|
|
527
566
|
}
|
|
528
|
-
function
|
|
567
|
+
function Me(e, t) {
|
|
529
568
|
let n = document.createElement("a");
|
|
530
569
|
n.href = URL.createObjectURL(e), n.download = t, n.click(), setTimeout(() => URL.revokeObjectURL(n.href), 1e3);
|
|
531
570
|
}
|
|
532
|
-
function
|
|
533
|
-
let [v,
|
|
571
|
+
function Ne({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, editable: u = !0, autoFees: d, searchCases: f, clauses: p, hwpWasmUrl: ee }) {
|
|
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({
|
|
534
573
|
extensions: [
|
|
535
574
|
i.configure({ heading: { levels: [
|
|
536
575
|
1,
|
|
@@ -539,60 +578,60 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
539
578
|
] } }),
|
|
540
579
|
a,
|
|
541
580
|
g,
|
|
542
|
-
|
|
543
|
-
|
|
581
|
+
y,
|
|
582
|
+
M
|
|
544
583
|
],
|
|
545
584
|
content: _(o),
|
|
546
585
|
editable: u,
|
|
547
586
|
immediatelyRender: !1,
|
|
548
587
|
editorProps: { attributes: { class: "legal-doc" } },
|
|
549
|
-
onCreate: ({ editor: e }) =>
|
|
588
|
+
onCreate: ({ editor: e }) => S($(e)),
|
|
550
589
|
onUpdate: ({ editor: e }) => {
|
|
551
|
-
|
|
590
|
+
S($(e)), c?.(e.getHTML());
|
|
552
591
|
}
|
|
553
592
|
});
|
|
554
593
|
e(() => {
|
|
555
|
-
|
|
556
|
-
}, [
|
|
557
|
-
let
|
|
594
|
+
N && (N.storage.variable.values = j, N.storage.variable.views.forEach((e) => e()));
|
|
595
|
+
}, [N, j]);
|
|
596
|
+
let P = (e, t) => {
|
|
558
597
|
let n = {
|
|
559
598
|
...v,
|
|
560
599
|
[e]: t
|
|
561
600
|
};
|
|
562
|
-
|
|
563
|
-
},
|
|
601
|
+
b(n), l?.(n);
|
|
602
|
+
}, F = () => {
|
|
564
603
|
let e = window.prompt("변수 이름 (예: 갑, 계약일)")?.replace(/[{}<>"]/g, "").trim();
|
|
565
|
-
e &&
|
|
604
|
+
e && N?.chain().focus().insertContent({
|
|
566
605
|
type: "variable",
|
|
567
606
|
attrs: { name: e }
|
|
568
607
|
}).run();
|
|
569
|
-
},
|
|
608
|
+
}, I = async (e) => {
|
|
570
609
|
let t = e.target.files?.[0];
|
|
571
|
-
if (e.target.value = "", t &&
|
|
572
|
-
|
|
610
|
+
if (e.target.value = "", t && N) try {
|
|
611
|
+
N.commands.setContent(_(await Ae(t)), { emitUpdate: !0 });
|
|
573
612
|
} catch (e) {
|
|
574
613
|
window.alert(`파일을 열 수 없어요: ${e.message}`);
|
|
575
614
|
}
|
|
576
|
-
},
|
|
577
|
-
let n =
|
|
615
|
+
}, L = async (e, t) => {
|
|
616
|
+
let n = N.state.doc.firstChild?.textContent.trim() || "문서";
|
|
578
617
|
try {
|
|
579
|
-
|
|
618
|
+
Me(await e(N.getJSON(), j), `${n}.${t}`);
|
|
580
619
|
} catch (e) {
|
|
581
620
|
window.alert(`저장하지 못했어요: ${e.message}`);
|
|
582
621
|
}
|
|
583
|
-
},
|
|
584
|
-
let e =
|
|
622
|
+
}, R = async () => {
|
|
623
|
+
let e = C.trim();
|
|
585
624
|
if (f && e) {
|
|
586
|
-
|
|
625
|
+
D("검색 중…");
|
|
587
626
|
try {
|
|
588
|
-
|
|
627
|
+
T(await f(e)), D("");
|
|
589
628
|
} catch (e) {
|
|
590
|
-
|
|
629
|
+
T(null), D(`검색하지 못했어요: ${e.message}`);
|
|
591
630
|
}
|
|
592
631
|
}
|
|
593
|
-
},
|
|
594
|
-
let { from: t, to: n } =
|
|
595
|
-
r &&
|
|
632
|
+
}, z = () => N.chain().focus(), ne = (e) => z().setNumbering(N.getAttributes("paragraph").num === e ? null : e).run(), B = (e) => {
|
|
633
|
+
let { from: t, to: n } = N.state.selection, r = se(N.state.doc.textBetween(t, n));
|
|
634
|
+
r && z().insertContentAt({
|
|
596
635
|
from: t,
|
|
597
636
|
to: n
|
|
598
637
|
}, H(r, e)).run();
|
|
@@ -601,22 +640,22 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
601
640
|
className: "le-root",
|
|
602
641
|
children: [/* @__PURE__ */ h("div", {
|
|
603
642
|
className: "le-main",
|
|
604
|
-
children: [u &&
|
|
643
|
+
children: [u && N && /* @__PURE__ */ h("div", {
|
|
605
644
|
className: "le-toolbar",
|
|
606
645
|
children: [
|
|
607
646
|
/* @__PURE__ */ m("button", {
|
|
608
647
|
type: "button",
|
|
609
|
-
onClick: () =>
|
|
648
|
+
onClick: () => z().toggleHeading({ level: 1 }).run(),
|
|
610
649
|
children: "제목"
|
|
611
650
|
}),
|
|
612
651
|
/* @__PURE__ */ m("button", {
|
|
613
652
|
type: "button",
|
|
614
|
-
onClick: () =>
|
|
653
|
+
onClick: () => z().toggleHeading({ level: 3 }).run(),
|
|
615
654
|
children: "소제목"
|
|
616
655
|
}),
|
|
617
656
|
/* @__PURE__ */ m("button", {
|
|
618
657
|
type: "button",
|
|
619
|
-
onClick: () =>
|
|
658
|
+
onClick: () => z().toggleHeading({ level: 2 }).run(),
|
|
620
659
|
children: "조"
|
|
621
660
|
}),
|
|
622
661
|
[
|
|
@@ -627,37 +666,37 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
627
666
|
].map((e, t) => /* @__PURE__ */ m("button", {
|
|
628
667
|
type: "button",
|
|
629
668
|
title: "번호 문단 — Tab·Shift+Tab 단계 변경, 맨 앞 Backspace 해제",
|
|
630
|
-
onClick: () =>
|
|
669
|
+
onClick: () => ne(t + 1),
|
|
631
670
|
children: e
|
|
632
671
|
}, e)),
|
|
633
672
|
/* @__PURE__ */ m("button", {
|
|
634
673
|
type: "button",
|
|
635
|
-
onClick: () =>
|
|
674
|
+
onClick: () => z().toggleOrderedList().run(),
|
|
636
675
|
children: "항"
|
|
637
676
|
}),
|
|
638
677
|
/* @__PURE__ */ m("button", {
|
|
639
678
|
type: "button",
|
|
640
|
-
onClick: () =>
|
|
679
|
+
onClick: () => z().sinkListItem("listItem").run(),
|
|
641
680
|
children: "호 →"
|
|
642
681
|
}),
|
|
643
682
|
/* @__PURE__ */ m("button", {
|
|
644
683
|
type: "button",
|
|
645
|
-
onClick: () =>
|
|
684
|
+
onClick: () => z().liftListItem("listItem").run(),
|
|
646
685
|
children: "← 내어쓰기"
|
|
647
686
|
}),
|
|
648
687
|
/* @__PURE__ */ m("button", {
|
|
649
688
|
type: "button",
|
|
650
|
-
onClick: () =>
|
|
689
|
+
onClick: () => z().toggleBold().run(),
|
|
651
690
|
children: /* @__PURE__ */ m("b", { children: "B" })
|
|
652
691
|
}),
|
|
653
692
|
/* @__PURE__ */ h("button", {
|
|
654
693
|
type: "button",
|
|
655
|
-
onClick:
|
|
694
|
+
onClick: F,
|
|
656
695
|
children: ["{{ }}", " 변수"]
|
|
657
696
|
}),
|
|
658
697
|
/* @__PURE__ */ m("button", {
|
|
659
698
|
type: "button",
|
|
660
|
-
onClick: () =>
|
|
699
|
+
onClick: () => z().insertTable({
|
|
661
700
|
rows: 3,
|
|
662
701
|
cols: 3,
|
|
663
702
|
withHeaderRow: !1
|
|
@@ -667,13 +706,13 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
667
706
|
/* @__PURE__ */ m("button", {
|
|
668
707
|
type: "button",
|
|
669
708
|
title: "선택한 금액 → 금 37,200,000원",
|
|
670
|
-
onClick: () =>
|
|
709
|
+
onClick: () => B("소장"),
|
|
671
710
|
children: "금액"
|
|
672
711
|
}),
|
|
673
712
|
/* @__PURE__ */ m("button", {
|
|
674
713
|
type: "button",
|
|
675
714
|
title: "선택한 금액 → 금 삼천칠백이십만 원정(₩37,200,000)",
|
|
676
|
-
onClick: () =>
|
|
715
|
+
onClick: () => B("계약서"),
|
|
677
716
|
children: "금액(한글)"
|
|
678
717
|
}),
|
|
679
718
|
/* @__PURE__ */ m("span", { className: "le-spacer" }),
|
|
@@ -681,39 +720,39 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
681
720
|
className: "le-btn",
|
|
682
721
|
children: ["열기", /* @__PURE__ */ m("input", {
|
|
683
722
|
type: "file",
|
|
684
|
-
accept: ".docx,.hwp,.hwpx",
|
|
723
|
+
accept: ".docx,.doc,.hwp,.hwpx",
|
|
685
724
|
hidden: !0,
|
|
686
|
-
onChange:
|
|
725
|
+
onChange: I
|
|
687
726
|
})]
|
|
688
727
|
}),
|
|
689
728
|
/* @__PURE__ */ m("button", {
|
|
690
729
|
type: "button",
|
|
691
|
-
onClick: () =>
|
|
730
|
+
onClick: () => L(pe, "docx"),
|
|
692
731
|
children: "Word 저장"
|
|
693
732
|
}),
|
|
694
733
|
/* @__PURE__ */ m("button", {
|
|
695
734
|
type: "button",
|
|
696
|
-
onClick: () =>
|
|
735
|
+
onClick: () => L(q, "hwpx"),
|
|
697
736
|
children: "한글(.hwpx) 저장"
|
|
698
737
|
}),
|
|
699
738
|
/* @__PURE__ */ m("button", {
|
|
700
739
|
type: "button",
|
|
701
740
|
title: "HWP 5.0 — 처음 저장할 때 변환기(약 10MB)를 불러와요",
|
|
702
|
-
onClick: () =>
|
|
741
|
+
onClick: () => L((e, t) => me(e, t, { wasm: ee }), "hwp"),
|
|
703
742
|
children: "한글(.hwp) 저장"
|
|
704
743
|
}),
|
|
705
744
|
/* @__PURE__ */ m("button", {
|
|
706
745
|
type: "button",
|
|
707
|
-
onClick:
|
|
746
|
+
onClick: je,
|
|
708
747
|
children: "인쇄 · PDF"
|
|
709
748
|
})
|
|
710
749
|
]
|
|
711
|
-
}), /* @__PURE__ */ m(n, { editor:
|
|
750
|
+
}), /* @__PURE__ */ m(n, { editor: N })]
|
|
712
751
|
}), /* @__PURE__ */ h("aside", {
|
|
713
752
|
className: "le-panel",
|
|
714
753
|
children: [
|
|
715
754
|
/* @__PURE__ */ m("h3", { children: "입력값" }),
|
|
716
|
-
|
|
755
|
+
x.length === 0 && /* @__PURE__ */ h("p", {
|
|
717
756
|
className: "le-hint",
|
|
718
757
|
children: [
|
|
719
758
|
"본문에 ",
|
|
@@ -721,27 +760,27 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
721
760
|
"처럼 입력하면 변수가 생겨요."
|
|
722
761
|
]
|
|
723
762
|
}),
|
|
724
|
-
|
|
763
|
+
x.map((e) => /* @__PURE__ */ h("label", { children: [e, /* @__PURE__ */ m("input", {
|
|
725
764
|
value: v[e] ?? "",
|
|
726
|
-
placeholder: v[e] ? void 0 :
|
|
727
|
-
onChange: (t) =>
|
|
765
|
+
placeholder: v[e] ? void 0 : j[e],
|
|
766
|
+
onChange: (t) => P(e, t.target.value)
|
|
728
767
|
})] }, e)),
|
|
729
768
|
p && /* @__PURE__ */ h("section", {
|
|
730
769
|
className: "le-clauses",
|
|
731
770
|
children: [
|
|
732
771
|
/* @__PURE__ */ m("h3", { children: "조항 라이브러리" }),
|
|
733
772
|
/* @__PURE__ */ m("input", {
|
|
734
|
-
value:
|
|
773
|
+
value: O,
|
|
735
774
|
placeholder: "조항 찾기 (예: 해지)",
|
|
736
|
-
onChange: (e) =>
|
|
775
|
+
onChange: (e) => A(e.target.value)
|
|
737
776
|
}),
|
|
738
777
|
/* @__PURE__ */ m("ul", {
|
|
739
778
|
className: "le-clause-list",
|
|
740
|
-
children: p.filter((e) => `${e.category} ${e.title}`.includes(
|
|
779
|
+
children: p.filter((e) => `${e.category} ${e.title}`.includes(O.trim())).map((e) => /* @__PURE__ */ m("li", { children: /* @__PURE__ */ h("button", {
|
|
741
780
|
type: "button",
|
|
742
781
|
disabled: !u,
|
|
743
782
|
title: "커서 위치에 조항 넣기",
|
|
744
|
-
onClick: () =>
|
|
783
|
+
onClick: () => N?.chain().focus().insertContent(_(e.html), { parseOptions: { preserveWhitespace: !1 } }).run(),
|
|
745
784
|
children: [/* @__PURE__ */ m("small", { children: e.category }), e.title]
|
|
746
785
|
}) }, e.id))
|
|
747
786
|
})
|
|
@@ -754,36 +793,36 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
754
793
|
/* @__PURE__ */ h("div", {
|
|
755
794
|
className: "le-cases-bar",
|
|
756
795
|
children: [/* @__PURE__ */ m("input", {
|
|
757
|
-
value:
|
|
796
|
+
value: C,
|
|
758
797
|
placeholder: "사건명·사건번호·키워드",
|
|
759
|
-
onChange: (e) =>
|
|
798
|
+
onChange: (e) => te(e.target.value),
|
|
760
799
|
onKeyDown: (e) => {
|
|
761
|
-
e.key === "Enter" && !e.nativeEvent.isComposing && (e.preventDefault(),
|
|
800
|
+
e.key === "Enter" && !e.nativeEvent.isComposing && (e.preventDefault(), R());
|
|
762
801
|
}
|
|
763
802
|
}), /* @__PURE__ */ m("button", {
|
|
764
803
|
type: "button",
|
|
765
|
-
onClick:
|
|
804
|
+
onClick: R,
|
|
766
805
|
children: "검색"
|
|
767
806
|
})]
|
|
768
807
|
}),
|
|
769
|
-
|
|
808
|
+
E && /* @__PURE__ */ m("p", {
|
|
770
809
|
className: "le-hint",
|
|
771
|
-
children:
|
|
810
|
+
children: E
|
|
772
811
|
}),
|
|
773
|
-
!
|
|
812
|
+
!E && w?.length === 0 && /* @__PURE__ */ m("p", {
|
|
774
813
|
className: "le-hint",
|
|
775
814
|
children: "결과가 없어요."
|
|
776
815
|
}),
|
|
777
816
|
/* @__PURE__ */ m("ul", {
|
|
778
817
|
className: "le-case-list",
|
|
779
|
-
children:
|
|
818
|
+
children: w?.map((e) => /* @__PURE__ */ h("li", { children: [/* @__PURE__ */ h("button", {
|
|
780
819
|
type: "button",
|
|
781
820
|
disabled: !u,
|
|
782
821
|
title: "커서 위치에 인용 넣기",
|
|
783
|
-
onClick: () =>
|
|
822
|
+
onClick: () => N?.chain().focus().insertContent(k(e)).run(),
|
|
784
823
|
children: [
|
|
785
824
|
/* @__PURE__ */ m("strong", { children: e.title }),
|
|
786
|
-
/* @__PURE__ */ m("span", { children:
|
|
825
|
+
/* @__PURE__ */ m("span", { children: k(e) }),
|
|
787
826
|
e.summary && /* @__PURE__ */ m("small", { children: e.summary })
|
|
788
827
|
]
|
|
789
828
|
}), /* @__PURE__ */ m("a", {
|
|
@@ -801,7 +840,7 @@ function Ee({ content: o = "", values: s = {}, onChange: c, onValuesChange: l, e
|
|
|
801
840
|
}
|
|
802
841
|
//#endregion
|
|
803
842
|
//#region src/templates.ts
|
|
804
|
-
var
|
|
843
|
+
var Pe = [
|
|
805
844
|
{
|
|
806
845
|
id: "complaint-loan",
|
|
807
846
|
title: "소장 (대여금)",
|
|
@@ -826,7 +865,7 @@ var De = [
|
|
|
826
865
|
description: "재판 없이 법원의 지급명령을 받는 독촉절차 신청서. 인지대는 소장의 10분의 1, 송달료는 당사자 수 × 6회분",
|
|
827
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"
|
|
828
867
|
}
|
|
829
|
-
],
|
|
868
|
+
], Fe = [
|
|
830
869
|
{
|
|
831
870
|
id: "purpose",
|
|
832
871
|
title: "목적",
|
|
@@ -895,4 +934,4 @@ var De = [
|
|
|
895
934
|
}
|
|
896
935
|
];
|
|
897
936
|
//#endregion
|
|
898
|
-
export {
|
|
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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JSONContent } from '@tiptap/core';
|
|
2
|
+
import type { MsDocBlock } from '@file-viewer/doc';
|
|
2
3
|
import type { Values } from './variable';
|
|
3
4
|
/** 번호가 텍스트로 박힌 독립 HTML — CSS 없이도 조·항·호가 보여서 메일·다른 변환기에 넘기기 좋음 */
|
|
4
5
|
export declare function toPlainHtml(doc: JSONContent, values: Values): string;
|
|
@@ -14,6 +15,13 @@ export declare function toHwpx(doc: JSONContent, values: Values): Promise<Blob>;
|
|
|
14
15
|
export declare function toHwp(doc: JSONContent, values: Values, opts?: {
|
|
15
16
|
wasm?: string | URL | BufferSource;
|
|
16
17
|
}): Promise<Blob>;
|
|
18
|
+
/**
|
|
19
|
+
* "제N조"를 뗀 나머지에서 조 제목이 차지하는 글자 수.
|
|
20
|
+
* - 괄호 제목 "(목적) 본문" → 괄호까지 (괄호 안 괄호도 짝을 셈)
|
|
21
|
+
* - 괄호 없는 짧은 한 줄 "목적" → 전체
|
|
22
|
+
* - 괄호 없이 문장 "갑은 …한다." → 0 (제목 없음, 전부 본문)
|
|
23
|
+
*/
|
|
24
|
+
export declare function articleTitleLength(text: string): number;
|
|
17
25
|
/**
|
|
18
26
|
* 문단으로만 된 법률문서 HTML(한글·워드에서 온 것)을 에디터 구조로 정리.
|
|
19
27
|
* - 빈 줄 문단 제거, 짧은 첫 줄 → 제목(h1), "청 구 취 지" 같은 소제목 → h3, [빈칸] → {{변수}}
|
|
@@ -27,5 +35,12 @@ export declare function normalizeLegalHtml(html: string): string;
|
|
|
27
35
|
export declare function fromDocx(file: Blob): Promise<string>;
|
|
28
36
|
/** .hwp / .hwpx → 에디터용 HTML. .hwp는 hwpx로 바꾼 뒤 읽음 (암호화·배포용·HWP 3.0은 에러) */
|
|
29
37
|
export declare function fromHwp(file: Blob): Promise<string>;
|
|
30
|
-
/**
|
|
38
|
+
/** .doc(Word 97–2003) → 에디터용 HTML. @file-viewer/doc로 읽고 글자·표만 옮김 */
|
|
39
|
+
export declare function fromDoc(file: Blob): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* .doc 파싱 결과 → 단순 HTML. 한 문단 블록 안에 단락 기호(\r)로 여러 문단이 붙어 오는 경우가 있어 \r로 나누고, \v(줄 바꿈)는 <br>로
|
|
42
|
+
* ponytail: 글자 서식(굵게 등)·그림은 옮기지 않음 — 필요하면 inlines의 글자 속성을 읽어 확장
|
|
43
|
+
*/
|
|
44
|
+
export declare function docBlocksToHtml(blocks: MsDocBlock[]): string;
|
|
45
|
+
/** 확장자로 골라 여는 헬퍼: .docx / .doc / .hwp / .hwpx */
|
|
31
46
|
export declare function fromFile(file: File): Promise<string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "legal-doc-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "법률문서(계약서·소장·고소장) 생성용 React 에디터 — 조항·번호 자동 매김, {{변수}} 채우기, 한글(hwp·hwpx)·Word(docx) 열기·저장",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"react-dom": ">=18"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
+
"@file-viewer/doc": "^3.1.1",
|
|
53
54
|
"@rhwp/core": "^0.8.6",
|
|
54
55
|
"@tiptap/core": "^3.31.3",
|
|
55
56
|
"@tiptap/extension-table": "^3.31.3",
|