@hwp-editor/react 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +257 -188
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +3 -6
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +13 -6
- package/dist/index.d.ts +13 -6
- package/dist/index.js +136 -67
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -26,10 +26,10 @@ function useHwpEditorContext() {
|
|
|
26
26
|
|
|
27
27
|
// src/PageCanvas.tsx
|
|
28
28
|
import { useMemo } from "react";
|
|
29
|
-
import { base64, segmentAtRef, segmentText, selectionAction } from "@hwp-editor/core";
|
|
29
|
+
import { base64, segmentAtRef, segmentText as segmentText2, selectionAction } from "@hwp-editor/core";
|
|
30
30
|
|
|
31
31
|
// src/geometry.ts
|
|
32
|
-
import { segmentAtOffset, markdownLength } from "@hwp-editor/core";
|
|
32
|
+
import { segmentAtOffset, markdownLength, segmentIdPath } from "@hwp-editor/core";
|
|
33
33
|
function clamp(value, min, max) {
|
|
34
34
|
return Math.min(max, Math.max(min, value));
|
|
35
35
|
}
|
|
@@ -81,21 +81,46 @@ function idIndex(envelope) {
|
|
|
81
81
|
idIndexes.set(envelope, index);
|
|
82
82
|
return index;
|
|
83
83
|
}
|
|
84
|
+
var pathIndexes = /* @__PURE__ */ new WeakMap();
|
|
85
|
+
function pathIndex(envelope) {
|
|
86
|
+
const cached = pathIndexes.get(envelope);
|
|
87
|
+
if (cached !== void 0) return cached;
|
|
88
|
+
const index = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const segment of envelope.segments) {
|
|
90
|
+
if (segment.id !== void 0 || segment.kind !== "para") continue;
|
|
91
|
+
const key = [segment.section, ...segment.indices ?? [segment.para]].join(".");
|
|
92
|
+
if (!index.has(key)) index.set(key, segment);
|
|
93
|
+
}
|
|
94
|
+
pathIndexes.set(envelope, index);
|
|
95
|
+
return index;
|
|
96
|
+
}
|
|
97
|
+
function rowSegment(envelope, rowId) {
|
|
98
|
+
const byId = idIndex(envelope).get(rowId);
|
|
99
|
+
if (byId !== void 0) return byId;
|
|
100
|
+
const path = segmentIdPath(rowId);
|
|
101
|
+
return path.length === 0 ? void 0 : pathIndex(envelope).get(path.join("."));
|
|
102
|
+
}
|
|
103
|
+
function hasIds(envelope) {
|
|
104
|
+
return envelope.segments.some((segment) => segment.id !== void 0);
|
|
105
|
+
}
|
|
106
|
+
function rowDraws(envelope, rowId, segment) {
|
|
107
|
+
if (segment.id !== void 0) return rowId === segment.id;
|
|
108
|
+
return rowSegment(envelope, rowId) === segment;
|
|
109
|
+
}
|
|
84
110
|
function segmentOnPage(envelope, page, xFraction, yFraction, pageIndex, pageCount) {
|
|
85
|
-
if (page.layout === void 0 || !envelope.
|
|
111
|
+
if (page.layout === void 0 || !hasIds(envelope) && page.layout.rows.every((row) => rowSegment(envelope, row.id) === void 0)) {
|
|
86
112
|
return nearestSegment(envelope, pageIndex, yFraction, pageCount);
|
|
87
113
|
}
|
|
88
114
|
const { width_pt, height_pt, rows } = page.layout;
|
|
89
115
|
const x = clamp(xFraction, 0, 1) * width_pt;
|
|
90
116
|
const y = clamp(yFraction, 0, 1) * height_pt;
|
|
91
|
-
const byId = idIndex(envelope);
|
|
92
117
|
let best;
|
|
93
118
|
let bestVertical = Infinity;
|
|
94
119
|
let bestHorizontal = Infinity;
|
|
95
120
|
let bestArea = Infinity;
|
|
96
121
|
for (const row of rows) {
|
|
97
122
|
const box = row.box;
|
|
98
|
-
const segment =
|
|
123
|
+
const segment = rowSegment(envelope, row.id);
|
|
99
124
|
if (box === null || segment === void 0) continue;
|
|
100
125
|
const dx = Math.max(box.x0 - x, 0, x - box.x1);
|
|
101
126
|
const dy = Math.max(box.y0 - y, 0, y - box.y1);
|
|
@@ -112,13 +137,13 @@ function segmentOnPage(envelope, page, xFraction, yFraction, pageIndex, pageCoun
|
|
|
112
137
|
return best;
|
|
113
138
|
}
|
|
114
139
|
function bandsOnPage(envelope, segment, page, pageIndex, pageCount) {
|
|
115
|
-
if (page.layout === void 0
|
|
140
|
+
if (page.layout === void 0) {
|
|
116
141
|
const band = segmentBand(envelope, segment, pageCount);
|
|
117
|
-
return band?.pageIndex === pageIndex ? [{ left: 0, top: band.top, width: 100, height: band.height }] : [];
|
|
142
|
+
return band?.pageIndex === pageIndex ? [{ left: 0, top: band.top, width: 100, height: band.height, approximate: true }] : [];
|
|
118
143
|
}
|
|
119
144
|
const target = ["run", "image", "field"].includes(segment.kind) && segment.indices !== void 0 ? envelope.segments.find((candidate) => candidate.kind === "para" && candidate.section === segment.section && candidate.indices !== void 0 && candidate.indices.length === segment.indices.length - 1 && candidate.indices.every((value, index) => value === segment.indices[index])) ?? segment : segment;
|
|
120
145
|
const { width_pt, height_pt, rows } = page.layout;
|
|
121
|
-
return rows.filter((row) => row.
|
|
146
|
+
return rows.filter((row) => row.box !== null && rowDraws(envelope, row.id, target)).map((row) => {
|
|
122
147
|
const box = row.box;
|
|
123
148
|
return {
|
|
124
149
|
left: box.x0 / width_pt * 100,
|
|
@@ -129,32 +154,8 @@ function bandsOnPage(envelope, segment, page, pageIndex, pageCount) {
|
|
|
129
154
|
});
|
|
130
155
|
}
|
|
131
156
|
|
|
132
|
-
// src/sanitize.ts
|
|
133
|
-
function sanitizeSvg(svgText) {
|
|
134
|
-
const doc = new DOMParser().parseFromString(svgText, "image/svg+xml");
|
|
135
|
-
const root = doc.documentElement;
|
|
136
|
-
if (root === null || root.nodeName === "parsererror" || doc.getElementsByTagName("parsererror").length > 0) {
|
|
137
|
-
return "";
|
|
138
|
-
}
|
|
139
|
-
const dangerous = root.querySelectorAll("script, foreignObject");
|
|
140
|
-
for (const node of Array.from(dangerous)) node.remove();
|
|
141
|
-
scrubElement(root);
|
|
142
|
-
return root.outerHTML;
|
|
143
|
-
}
|
|
144
|
-
function scrubElement(el) {
|
|
145
|
-
for (const attr of Array.from(el.attributes)) {
|
|
146
|
-
const name = attr.name.toLowerCase();
|
|
147
|
-
const value = attr.value.trim().toLowerCase();
|
|
148
|
-
if (name.startsWith("on")) {
|
|
149
|
-
el.removeAttribute(attr.name);
|
|
150
|
-
} else if ((name === "href" || name === "xlink:href") && value.startsWith("javascript:")) {
|
|
151
|
-
el.removeAttribute(attr.name);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
for (const child of Array.from(el.children)) scrubElement(child);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
157
|
// src/text.ts
|
|
158
|
+
import { segmentIdPath as segmentIdPath2, segmentText } from "@hwp-editor/core";
|
|
158
159
|
function plainSegmentText(markdownSlice) {
|
|
159
160
|
return markdownSlice.split("\n").map(stripLine).join("\n").trim();
|
|
160
161
|
}
|
|
@@ -168,6 +169,41 @@ function stripLine(line) {
|
|
|
168
169
|
out = out.replace(/`([^`]*)`/g, "$1");
|
|
169
170
|
return out;
|
|
170
171
|
}
|
|
172
|
+
var LIST_MARKER = /^[ \t]*(?:\d{1,9}[.)]|[-*+])[ \t]+/;
|
|
173
|
+
function paragraphSourceText(envelope, para, pages = []) {
|
|
174
|
+
const plain = plainSegmentText(segmentText(envelope, para));
|
|
175
|
+
const marker = LIST_MARKER.exec(plain);
|
|
176
|
+
if (marker === null) return plain;
|
|
177
|
+
const stripped = plain.slice(marker[0].length);
|
|
178
|
+
if (para.indices !== void 0) {
|
|
179
|
+
const first = envelope.segments.find((candidate) => candidate.kind === "run" && candidate.section === para.section && candidate.indices !== void 0 && candidate.indices.length === para.indices.length + 1 && para.indices.every((value, index) => candidate.indices[index] === value));
|
|
180
|
+
if (first !== void 0) {
|
|
181
|
+
const lead = [...segmentText(envelope, para)].slice(0, Math.max(0, first.start - para.start)).join("");
|
|
182
|
+
return LIST_MARKER.test(lead) && lead.trim() === marker[0].trim() ? stripped : plain;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const length = sourceLength(para, pages);
|
|
186
|
+
if (length === void 0) return plain;
|
|
187
|
+
return stripped.length === length && plain.length !== length ? stripped : plain;
|
|
188
|
+
}
|
|
189
|
+
function sourceLength(para, pages) {
|
|
190
|
+
let start = Infinity;
|
|
191
|
+
let end = -Infinity;
|
|
192
|
+
for (const page of pages) {
|
|
193
|
+
for (const row of page.layout?.rows ?? []) {
|
|
194
|
+
if (row.kind !== "para" || row.source_chars === null || !rowMatches(row.id, para)) continue;
|
|
195
|
+
start = Math.min(start, row.source_chars.start);
|
|
196
|
+
end = Math.max(end, row.source_chars.end);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return end >= start ? end - start : void 0;
|
|
200
|
+
}
|
|
201
|
+
function rowMatches(rowId, segment) {
|
|
202
|
+
if (segment.id !== void 0) return rowId === segment.id;
|
|
203
|
+
const path = segmentIdPath2(rowId);
|
|
204
|
+
const own = [segment.section, ...segment.indices ?? [segment.para]];
|
|
205
|
+
return path.length === own.length && path.every((value, index) => value === own[index]);
|
|
206
|
+
}
|
|
171
207
|
|
|
172
208
|
// src/PageCanvas.tsx
|
|
173
209
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -186,7 +222,7 @@ function PageCanvas() {
|
|
|
186
222
|
if (segment !== void 0) selectedSegments.add(segment);
|
|
187
223
|
}
|
|
188
224
|
}
|
|
189
|
-
if (pages.length === 0) {
|
|
225
|
+
if (pages.length === 0 || envelope === null) {
|
|
190
226
|
return /* @__PURE__ */ jsx("div", { className: "hwped-canvas", role: "main", "aria-label": t("canvas.aria"), children: /* @__PURE__ */ jsx("div", { className: "hwped-empty", children: t("canvas.emptyPages") }) });
|
|
191
227
|
}
|
|
192
228
|
const selectAt = (pageIndex, xFraction, yFraction, modifiers) => {
|
|
@@ -240,7 +276,7 @@ function bandsForPage(envelope, page, pageIndex, pageCount, selected, pendingKey
|
|
|
240
276
|
bands.push({
|
|
241
277
|
...band,
|
|
242
278
|
key: `${isSelected ? "sel" : "pend"}-${key}-${bandIdentity(segment)}-${index}`,
|
|
243
|
-
className: isSelected ? "hwped-band hwped-band-selected" : "hwped-band hwped-band-pending"
|
|
279
|
+
className: (isSelected ? "hwped-band hwped-band-selected" : "hwped-band hwped-band-pending") + (band.approximate ? " hwped-band-approximate" : "")
|
|
244
280
|
});
|
|
245
281
|
}
|
|
246
282
|
}
|
|
@@ -250,13 +286,8 @@ function PageView(props) {
|
|
|
250
286
|
const { page, onClick, onActivate, bands } = props;
|
|
251
287
|
const { t } = useHwpEditorContext();
|
|
252
288
|
const label = t("page.label", { page: page.page });
|
|
253
|
-
const svgHtml = useMemo(
|
|
254
|
-
() => page.format === "svg" ? sanitizeSvg(new TextDecoder().decode(page.data)) : null,
|
|
255
|
-
[page]
|
|
256
|
-
);
|
|
257
289
|
const imgSrc = useMemo(() => {
|
|
258
|
-
|
|
259
|
-
const mime = page.format === "jpeg" ? "image/jpeg" : `image/${page.format}`;
|
|
290
|
+
const mime = page.format === "jpeg" ? "image/jpeg" : page.format === "svg" ? "image/svg+xml" : `image/${page.format}`;
|
|
260
291
|
return `data:${mime};base64,${base64.encode(page.data)}`;
|
|
261
292
|
}, [page]);
|
|
262
293
|
return /* @__PURE__ */ jsxs(
|
|
@@ -275,17 +306,11 @@ function PageView(props) {
|
|
|
275
306
|
}
|
|
276
307
|
},
|
|
277
308
|
children: [
|
|
278
|
-
|
|
279
|
-
"div",
|
|
280
|
-
{
|
|
281
|
-
className: "hwped-page-svg",
|
|
282
|
-
dangerouslySetInnerHTML: { __html: svgHtml }
|
|
283
|
-
}
|
|
284
|
-
) : /* @__PURE__ */ jsx(
|
|
309
|
+
/* @__PURE__ */ jsx(
|
|
285
310
|
"img",
|
|
286
311
|
{
|
|
287
312
|
className: "hwped-page-img",
|
|
288
|
-
src: imgSrc
|
|
313
|
+
src: imgSrc,
|
|
289
314
|
alt: label,
|
|
290
315
|
width: page.width,
|
|
291
316
|
height: page.height
|
|
@@ -295,6 +320,7 @@ function PageView(props) {
|
|
|
295
320
|
"div",
|
|
296
321
|
{
|
|
297
322
|
className: band.className,
|
|
323
|
+
"data-approximate": band.className.includes("hwped-band-approximate") ? "true" : void 0,
|
|
298
324
|
style: { top: `${band.top}%`, height: `${band.height}%`, left: `${band.left}%`, width: `${band.width}%` }
|
|
299
325
|
},
|
|
300
326
|
band.key
|
|
@@ -327,7 +353,7 @@ function targetsSegment(op, envelope, segment) {
|
|
|
327
353
|
return "run" in at ? segment.kind === "run" && segment.indices?.length === 2 && segment.indices[1] === at.run : segment.kind === "para" && (segment.indices === void 0 || segment.indices.length === 1);
|
|
328
354
|
}
|
|
329
355
|
const target = opSearchText(op);
|
|
330
|
-
return target !== null && target !== "" && plainSegmentText(
|
|
356
|
+
return target !== null && target !== "" && plainSegmentText(segmentText2(envelope, segment)).includes(target);
|
|
331
357
|
}
|
|
332
358
|
|
|
333
359
|
// src/unit-input.ts
|
|
@@ -338,7 +364,7 @@ function isPlainDecimal(value) {
|
|
|
338
364
|
|
|
339
365
|
// src/SegmentInspector.tsx
|
|
340
366
|
import { useEffect, useMemo as useMemo2, useRef, useState } from "react";
|
|
341
|
-
import { compareSegments, documentFormat, formatTargets as collectFormatTargets, hasDirectFormatting, hasOwnRef, isSectionDefinitionParagraph, paragraphAddress, paragraphRuns, resolveSelection, segmentAtRef as segmentAtRef2, segmentIdPath, segmentPath, segmentRef, segmentText as
|
|
367
|
+
import { compareSegments, documentFormat, formatTargets as collectFormatTargets, hasDirectFormatting, hasOwnRef, isSectionDefinitionParagraph, paragraphAddress, paragraphRuns, resolveSelection, segmentAtRef as segmentAtRef2, segmentIdPath as segmentIdPath3, segmentPath, segmentRef, segmentText as segmentText3, selectSimilar, selectedParagraphs, selectionAction as selectionAction2, stylesVersionSupported } from "@hwp-editor/core";
|
|
342
368
|
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
343
369
|
var ALIGNMENTS = [
|
|
344
370
|
{ value: "left", key: "segment.alignLeft" },
|
|
@@ -354,6 +380,14 @@ var SHAPE_KEYS = [
|
|
|
354
380
|
{ value: "top", key: "segment.shape.top" },
|
|
355
381
|
{ value: "bottom", key: "segment.shape.bottom" }
|
|
356
382
|
];
|
|
383
|
+
var CLICK_REFUSAL_MESSAGE = {
|
|
384
|
+
cell: "segment.clickRefusedCell",
|
|
385
|
+
table: "segment.clickRefusedTable",
|
|
386
|
+
container: "segment.clickRefusedContainer",
|
|
387
|
+
unaddressable: "segment.clickRefusedUnaddressable",
|
|
388
|
+
unaddressableRuns: "segment.clickRefusedUnaddressableRuns",
|
|
389
|
+
noRuns: "segment.clickRefusedNoRuns"
|
|
390
|
+
};
|
|
357
391
|
function SegmentInspector() {
|
|
358
392
|
const { state, store, envelope, editable, capabilities, styles, stylesStatus, t, insertedHidden } = useHwpEditorContext();
|
|
359
393
|
const { selection } = state;
|
|
@@ -378,11 +412,11 @@ function SegmentInspector() {
|
|
|
378
412
|
})();
|
|
379
413
|
const directStyleWhy = directStyleRow !== void 0 ? null : stylesStatus === "loading" ? "segment.directStyleLoading" : stylesStatus === "failed" ? "segment.directStyleFailed" : stylesStatus === "unsupported" ? "styles.unavailableHost" : capabilities === null ? "styles.checkingHost" : capabilities.styles !== "v1" ? stylesVersionSupported(capabilities.version) ? "styles.unavailableHost" : "segment.directStyleUnavailable" : "segment.directStyleUnknown";
|
|
380
414
|
const refusalText = (reason) => reason === null || reason === void 0 ? null : reason === "cell" ? t("segment.cellSelectionRefused") : reason === "table" ? t("segment.tableSelectionRefused") : reason === "container" ? t("segment.containerSelectionRefused") : t("segment.noParagraphSelection");
|
|
381
|
-
const clickRefusedText = (reason) => reason === null || reason === void 0 ? null :
|
|
415
|
+
const clickRefusedText = (reason) => reason === null || reason === void 0 ? null : t(CLICK_REFUSAL_MESSAGE[reason]);
|
|
382
416
|
const refusalReason2 = refusalText(targets?.refusal);
|
|
383
417
|
const targetLabel = segment === void 0 ? "" : segment.kind === "cell" ? t("segment.selectedCell", { path: segmentPath(segment), count: paragraphTargets.length }) : singleParagraph === void 0 ? "" : t("segment.selectedPara", { path: segmentPath(singleParagraph) });
|
|
384
|
-
const currentText = envelope !== null && paragraphTargets.length > 0 ?
|
|
385
|
-
const markdownText = envelope !== null && singleParagraph !== void 0 ?
|
|
418
|
+
const currentText = envelope !== null && paragraphTargets.length > 0 ? paragraphTargets.map((candidate) => paragraphSourceText(envelope, candidate, state.pages)).join("\n").trim() : "";
|
|
419
|
+
const markdownText = envelope !== null && singleParagraph !== void 0 ? segmentText3(envelope, singleParagraph) : "";
|
|
386
420
|
const supplementaryStarts = useMemo2(() => {
|
|
387
421
|
const positions = [];
|
|
388
422
|
let offset = 0;
|
|
@@ -441,7 +475,7 @@ function SegmentInspector() {
|
|
|
441
475
|
if (insertionRun === null || !insertionCurrent || runParent === void 0) return [];
|
|
442
476
|
const section = insertionRun[0].section;
|
|
443
477
|
return insertedHidden.ids.flatMap((id) => {
|
|
444
|
-
const [idSection, ...indices] =
|
|
478
|
+
const [idSection, ...indices] = segmentIdPath3(id);
|
|
445
479
|
if (idSection !== section || indices.length !== runParent.length + 1) return [];
|
|
446
480
|
if (!runParent.every((index, at) => indices[at] === index)) return [];
|
|
447
481
|
return [{ id, index: indices[indices.length - 1] }];
|
|
@@ -557,7 +591,10 @@ function SegmentInspector() {
|
|
|
557
591
|
type: "selectMany",
|
|
558
592
|
selections: refs,
|
|
559
593
|
primary,
|
|
560
|
-
|
|
594
|
+
// A matching-character-format selection is a new run selection, so its
|
|
595
|
+
// anchor is its own primary (#71 rule 7), never a run left over from an
|
|
596
|
+
// earlier one. The paragraph level keeps an existing anchor as before.
|
|
597
|
+
...level === "char" || state.selectionAnchor === null || state.selectionAnchor === void 0 ? { anchor: primary } : { anchor: state.selectionAnchor }
|
|
561
598
|
});
|
|
562
599
|
};
|
|
563
600
|
const moveParagraph = (direction) => {
|
|
@@ -645,7 +682,7 @@ function SegmentInspector() {
|
|
|
645
682
|
style: { maxHeight: "12rem", overflowY: "auto" },
|
|
646
683
|
children: paragraphs.map((candidate, index) => {
|
|
647
684
|
const isSelected = selected.some((item) => item === candidate || item.id !== void 0 && item.id === candidate.id || item.id === void 0 && candidate.id === void 0 && item.section === candidate.section && item.para === candidate.para);
|
|
648
|
-
const snippet = plainSegmentText(
|
|
685
|
+
const snippet = plainSegmentText(segmentText3(envelope, candidate)).trim().replace(/\s+/g, " ");
|
|
649
686
|
const text = snippet.length > 96 ? `${snippet.slice(0, 93)}...` : snippet || t("segment.emptyPara");
|
|
650
687
|
const label = t("segment.choiceLabel", { index: index + 1, text });
|
|
651
688
|
return /* @__PURE__ */ jsx2(
|
|
@@ -671,9 +708,9 @@ function SegmentInspector() {
|
|
|
671
708
|
// state holds and the targets the format op will address.
|
|
672
709
|
children: hasRunSelection ? t("segment.selectedRunCount", { count: explicitSegments.length }) : t("segment.selectedCount", { count: selected.length })
|
|
673
710
|
}),
|
|
674
|
-
// A run-level selection formats runs, and a modifier click
|
|
675
|
-
//
|
|
676
|
-
hasRunSelection && /* @__PURE__ */ jsx2("p", { className: "hwped-hint", role: "status", children: t("segment.
|
|
711
|
+
// A run-level selection formats runs, and a modifier click keeps it at
|
|
712
|
+
// run level: Ctrl toggles a unit's runs, Shift extends by run (#71).
|
|
713
|
+
hasRunSelection && /* @__PURE__ */ jsx2("p", { className: "hwped-hint", role: "status", children: t("segment.runSelectionGestures") }),
|
|
677
714
|
// A modifier click that had no paragraph to add left the selection
|
|
678
715
|
// alone, and reports the click here whatever the selection's own
|
|
679
716
|
// reason is: the reader's action is what was refused, and a click
|
|
@@ -1542,7 +1579,7 @@ import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
|
1542
1579
|
import { tableCountMismatch } from "@hwp-editor/core";
|
|
1543
1580
|
|
|
1544
1581
|
// src/tables.ts
|
|
1545
|
-
import { segmentText as
|
|
1582
|
+
import { segmentText as segmentText4 } from "@hwp-editor/core";
|
|
1546
1583
|
var GFM_DELIMITER_CELL = /^:?-{2,}:?$/;
|
|
1547
1584
|
function endsWithCellDelimiter(row) {
|
|
1548
1585
|
if (!row.endsWith("|")) return false;
|
|
@@ -1609,7 +1646,7 @@ function findTables(envelope) {
|
|
|
1609
1646
|
tables.push({
|
|
1610
1647
|
tableIndex: tables.length,
|
|
1611
1648
|
segment,
|
|
1612
|
-
rows: parseMarkdownTable(
|
|
1649
|
+
rows: parseMarkdownTable(segmentText4(envelope, segment))
|
|
1613
1650
|
});
|
|
1614
1651
|
}
|
|
1615
1652
|
return tables;
|
|
@@ -1914,7 +1951,7 @@ function TableGrid() {
|
|
|
1914
1951
|
|
|
1915
1952
|
// src/FieldsPanel.tsx
|
|
1916
1953
|
import { useMemo as useMemo3, useState as useState4 } from "react";
|
|
1917
|
-
import { segmentAtRef as segmentAtRef4, segmentText as
|
|
1954
|
+
import { segmentAtRef as segmentAtRef4, segmentText as segmentText5 } from "@hwp-editor/core";
|
|
1918
1955
|
|
|
1919
1956
|
// src/fields.ts
|
|
1920
1957
|
import { resolveSelection as resolveSelection3, segmentRef as segmentRef3 } from "@hwp-editor/core";
|
|
@@ -1983,7 +2020,7 @@ function FieldsPanel() {
|
|
|
1983
2020
|
legacy && /* @__PURE__ */ jsx5("p", { className: "hwped-hint", children: t("fields.legacySeparatorHint") }),
|
|
1984
2021
|
slots.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "hwped-hint", children: t("fields.hint") }) : /* @__PURE__ */ jsx5("ul", { className: "hwped-fields", children: slots.map((slot) => {
|
|
1985
2022
|
const segment = envelope === null ? void 0 : segmentAtRef4(envelope, slot.segment);
|
|
1986
|
-
const snippet = envelope === null || segment === void 0 ? "" :
|
|
2023
|
+
const snippet = envelope === null || segment === void 0 ? "" : segmentText5(envelope, segment).trim().slice(0, 40);
|
|
1987
2024
|
const draft = drafts[slot.name] ?? "";
|
|
1988
2025
|
return /* @__PURE__ */ jsxs5("li", { className: "hwped-field-item", children: [
|
|
1989
2026
|
/* @__PURE__ */ jsx5("div", { className: "hwped-row", children: /* @__PURE__ */ jsx5(
|
|
@@ -3834,7 +3871,7 @@ var en = {
|
|
|
3834
3871
|
"segment.rangeTextEditHint": "A character range formats text but cannot rewrite it. Use whole paragraph to edit its text.",
|
|
3835
3872
|
"segment.runTextEditHint": "A run selection formats runs only, so the text controls are off. Select a paragraph to edit its text.",
|
|
3836
3873
|
"segment.runSelectionParagraphs": (p) => p.count === 1 ? "Alignment and spacing apply to the 1 paragraph containing a selected run." : `Alignment and spacing apply to all ${p.count} paragraphs containing a selected run.`,
|
|
3837
|
-
"segment.
|
|
3874
|
+
"segment.runSelectionGestures": "Ctrl adds or removes a paragraph's runs; Shift extends by run.",
|
|
3838
3875
|
"segment.noParagraphSelection": "This selection carries no paragraph the engine can address, so paragraph edits are off. Pick a paragraph from the list above.",
|
|
3839
3876
|
"segment.cellSelectionRefused": "This selection is a table cell. Alignment and spacing apply to every paragraph in the cell, but replace, insert and delete address exactly one paragraph, and the engine refuses to delete a cell's last one. Pick a paragraph from the list above.",
|
|
3840
3877
|
"segment.tableSelectionRefused": "This selection is a table, not a paragraph. The paragraph that holds the table also holds its own text, so editing or deleting it would take the table with it. Pick a paragraph from the list above.",
|
|
@@ -3850,6 +3887,8 @@ var en = {
|
|
|
3850
3887
|
"segment.clickRefusedTable": "The click added nothing: a table has no paragraph of its own, so there is none to add. Click a paragraph or a cell.",
|
|
3851
3888
|
"segment.clickRefusedContainer": "The click added nothing: an image, a field or a bookmark has no text of its own, so there is no paragraph to add. Click a paragraph or a cell.",
|
|
3852
3889
|
"segment.clickRefusedUnaddressable": "That unit does not name a paragraph the engine can address, so the click selected nothing. Click a paragraph or a cell.",
|
|
3890
|
+
"segment.clickRefusedUnaddressableRuns": "The clicked paragraph or cell has runs, but the engine published no address for them, so the click selected nothing. Click without Ctrl or Shift to select the whole paragraph, or click a paragraph or cell whose runs have addresses.",
|
|
3891
|
+
"segment.clickRefusedNoRuns": "The clicked paragraph or cell has no runs to add. Click a paragraph or cell that has text.",
|
|
3853
3892
|
"segment.selectSimilar": "Select content with matching direct formatting",
|
|
3854
3893
|
"segment.similarChar": "Matching character format",
|
|
3855
3894
|
"segment.similarPara": "Matching paragraph format",
|
|
@@ -3976,6 +4015,7 @@ var en = {
|
|
|
3976
4015
|
"error.staleCompose": "The open document changed while the new document was being composed; compose again.",
|
|
3977
4016
|
"error.stalePreview": "The document changed while this preview was being made; preview again on the open document.",
|
|
3978
4017
|
"error.unmatchedReplace": "The engine found no text to replace in that paragraph, so nothing was applied. Select the text again and retry.",
|
|
4018
|
+
"error.unresolvableAddress": "The engine could not address the edit's paragraph or run. Runs inside some nested paragraphs cannot be addressed. Format text in a top-level paragraph or a table cell, or edit this target in a word processor.",
|
|
3979
4019
|
"error.runRangeConflict": "A run format change was refused because an earlier pending change in that paragraph (text, a link, a field, a bookmark, an image or a cell) may change its text, so the run might no longer name the same characters. Nothing was applied. Remove the format change from the pending list, apply the rest, then format the run again; or queue the format change before the others.",
|
|
3980
4020
|
"error.rangeTagRefused": "This HWP document holds a range mark the engine cannot move, so a pending edit was refused and nothing was applied. Edits that move text around the mark are refused: changing a paragraph's text length, changing a field's value to one of a different length, inserting or deleting a link, field, bookmark or image, or deleting a table. A table cell that holds a mark refuses any change to its content. The engine does not say which edit. Remove those pending edits from the pending list (a same-length text replacement in a paragraph, or a same-length field value, is accepted) and apply the rest. To make those edits, convert the document to HWPX, which drops the range marks, or remove the mark in Hangul, then reopen it; reopening drops the pending list.",
|
|
3981
4021
|
"error.stoppedWithoutDiagnostic": STOPPED_WITHOUT_DIAGNOSTIC,
|
|
@@ -4305,7 +4345,7 @@ var ko = {
|
|
|
4305
4345
|
"segment.rangeTextEditHint": "\uAE00\uC790 \uBC94\uC704 \uC120\uD0DD\uC740 \uC11C\uC2DD\uB9CC \uBC14\uAFC0 \uC218 \uC788\uACE0 \uD14D\uC2A4\uD2B8\uB294 \uBC14\uAFC0 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uB97C \uD3B8\uC9D1\uD558\uB824\uBA74 \uBB38\uB2E8 \uC804\uCCB4\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
|
|
4306
4346
|
"segment.runTextEditHint": "run \uC120\uD0DD\uC740 \uC11C\uC2DD\uB9CC \uBC14\uAFC0 \uC218 \uC788\uC5B4 \uD14D\uC2A4\uD2B8 \uC785\uB825\uB780\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uB97C \uD3B8\uC9D1\uD558\uB824\uBA74 \uBB38\uB2E8\uC744 \uC120\uD0DD\uD558\uC138\uC694.",
|
|
4307
4347
|
"segment.runSelectionParagraphs": (p) => `\uBB38\uB2E8 \uC815\uB82C\uACFC \uAC04\uACA9\uC740 \uC120\uD0DD\uD55C run\uC774 \uC788\uB294 \uBB38\uB2E8 ${p.count}\uAC1C \uC804\uCCB4\uC5D0 \uC801\uC6A9\uB429\uB2C8\uB2E4.`,
|
|
4308
|
-
"segment.
|
|
4348
|
+
"segment.runSelectionGestures": "Ctrl\uB85C \uBB38\uB2E8\uC758 run\uC744 \uB354\uD558\uAC70\uB098 \uBE7C\uACE0, Shift\uB85C run \uB2E8\uC704\uB85C \uB113\uD799\uB2C8\uB2E4.",
|
|
4309
4349
|
"segment.noParagraphSelection": "\uC774 \uC120\uD0DD\uC5D0\uB294 \uC5D4\uC9C4\uC774 \uC8FC\uC18C\uB85C \uC9C0\uC815\uD560 \uC218 \uC788\uB294 \uBB38\uB2E8\uC774 \uC5C6\uC5B4 \uBB38\uB2E8 \uD3B8\uC9D1\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC704 \uBAA9\uB85D\uC5D0\uC11C \uBB38\uB2E8\uC744 \uC120\uD0DD\uD558\uC138\uC694.",
|
|
4310
4350
|
"segment.cellSelectionRefused": "\uC774 \uC120\uD0DD\uC740 \uD45C\uC758 \uC140\uC785\uB2C8\uB2E4. \uC815\uB82C\uACFC \uBB38\uB2E8 \uBAA8\uC591\uC740 \uC140 \uC548\uC758 \uBAA8\uB4E0 \uBB38\uB2E8\uC5D0 \uC801\uC6A9\uB418\uC9C0\uB9CC, \uAD50\uCCB4, \uC0BD\uC785, \uC0AD\uC81C\uB294 \uBB38\uB2E8 \uD558\uB098\uB9CC \uB300\uC0C1\uC73C\uB85C \uD558\uBA70, \uC5D4\uC9C4\uC740 \uC140\uC758 \uB9C8\uC9C0\uB9C9 \uBB38\uB2E8 \uC0AD\uC81C\uB3C4 \uAC70\uBD80\uD569\uB2C8\uB2E4. \uC704 \uBAA9\uB85D\uC5D0\uC11C \uBB38\uB2E8\uC744 \uC120\uD0DD\uD558\uC138\uC694.",
|
|
4311
4351
|
"segment.tableSelectionRefused": "\uC774 \uC120\uD0DD\uC740 \uBB38\uB2E8\uC774 \uC544\uB2C8\uB77C \uD45C\uC785\uB2C8\uB2E4. \uD45C\uB97C \uB2F4\uACE0 \uC788\uB294 \uBB38\uB2E8\uC740 \uC790\uAE30 \uD14D\uC2A4\uD2B8\uB3C4 \uD568\uAED8 \uB2F4\uACE0 \uC788\uC5B4, \uADF8 \uBB38\uB2E8\uC744 \uD3B8\uC9D1\uD558\uAC70\uB098 \uC0AD\uC81C\uD558\uBA74 \uD45C\uAC00 \uC0AC\uB77C\uC9D1\uB2C8\uB2E4. \uC704 \uBAA9\uB85D\uC5D0\uC11C \uBB38\uB2E8\uC744 \uC120\uD0DD\uD558\uC138\uC694.",
|
|
@@ -4314,6 +4354,8 @@ var ko = {
|
|
|
4314
4354
|
"segment.clickRefusedTable": "\uD074\uB9AD\uC73C\uB85C \uCD94\uAC00\uB41C \uBB38\uB2E8\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD45C\uB294 \uC790\uCCB4 \uBB38\uB2E8\uC774 \uC5C6\uC5B4 \uCD94\uAC00\uD560 \uB300\uC0C1\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uB2E8\uC774\uB098 \uC140\uC744 \uD074\uB9AD\uD558\uC138\uC694.",
|
|
4315
4355
|
"segment.clickRefusedContainer": "\uD074\uB9AD\uC73C\uB85C \uCD94\uAC00\uB41C \uBB38\uB2E8\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uADF8\uB9BC, \uD544\uB4DC \uB610\uB294 \uCC45\uAC08\uD53C\uB294 \uC790\uCCB4 \uD14D\uC2A4\uD2B8\uAC00 \uC5C6\uC5B4 \uCD94\uAC00\uD560 \uBB38\uB2E8\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uB2E8\uC774\uB098 \uC140\uC744 \uD074\uB9AD\uD558\uC138\uC694.",
|
|
4316
4356
|
"segment.clickRefusedUnaddressable": "\uADF8 \uC694\uC18C\uB294 \uC5D4\uC9C4\uC774 \uC8FC\uC18C\uB85C \uC9C0\uC815\uD560 \uC218 \uC788\uB294 \uBB38\uB2E8\uC744 \uAC00\uB9AC\uD0A4\uC9C0 \uC54A\uC73C\uBBC0\uB85C \uD074\uB9AD\uC73C\uB85C \uC120\uD0DD\uB41C \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBB38\uB2E8\uC774\uB098 \uC140\uC744 \uD074\uB9AD\uD558\uC138\uC694.",
|
|
4357
|
+
"segment.clickRefusedUnaddressableRuns": "\uD074\uB9AD\uD55C \uBB38\uB2E8\uC774\uB098 \uC140\uC5D0 run\uC740 \uC788\uC9C0\uB9CC \uC5D4\uC9C4\uC774 \uADF8 run\uC758 \uC8FC\uC18C\uB97C \uC81C\uACF5\uD558\uC9C0 \uC54A\uC544 \uC120\uD0DD\uB41C \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. Ctrl\uC774\uB098 Shift \uC5C6\uC774 \uD074\uB9AD\uD574 \uBB38\uB2E8 \uC804\uCCB4\uB97C \uC120\uD0DD\uD558\uAC70\uB098, \uC8FC\uC18C\uAC00 \uC788\uB294 run\uC744 \uAC00\uC9C4 \uBB38\uB2E8\uC774\uB098 \uC140\uC744 \uD074\uB9AD\uD558\uC138\uC694.",
|
|
4358
|
+
"segment.clickRefusedNoRuns": "\uD074\uB9AD\uD55C \uBB38\uB2E8\uC774\uB098 \uC140\uC5D0 \uCD94\uAC00\uD560 run\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uAC00 \uC788\uB294 \uBB38\uB2E8\uC774\uB098 \uC140\uC744 \uD074\uB9AD\uD558\uC138\uC694.",
|
|
4317
4359
|
"segment.selectSimilar": "\uC9C1\uC811 \uC11C\uC2DD\uC774 \uAC19\uC740 \uB0B4\uC6A9 \uC120\uD0DD",
|
|
4318
4360
|
"segment.similarChar": "\uAC19\uC740 \uAE00\uC790 \uC11C\uC2DD",
|
|
4319
4361
|
"segment.similarPara": "\uAC19\uC740 \uBB38\uB2E8 \uC11C\uC2DD",
|
|
@@ -4431,6 +4473,7 @@ var ko = {
|
|
|
4431
4473
|
"error.staleCompose": "\uC0C8 \uBB38\uC11C\uB97C \uC791\uC131\uD558\uB294 \uB3D9\uC548 \uC5F4\uB9B0 \uBB38\uC11C\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC791\uC131\uD558\uC138\uC694.",
|
|
4432
4474
|
"error.stalePreview": "\uBBF8\uB9AC\uBCF4\uAE30\uB97C \uB9CC\uB4DC\uB294 \uB3D9\uC548 \uBB38\uC11C\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uC5F4\uB9B0 \uBB38\uC11C\uC5D0\uC11C \uB2E4\uC2DC \uBBF8\uB9AC\uBCF4\uAE30\uD558\uC138\uC694.",
|
|
4433
4475
|
"error.unmatchedReplace": "\uD574\uB2F9 \uBB38\uB2E8\uC5D0\uC11C \uBC14\uAFC0 \uD14D\uC2A4\uD2B8\uB97C \uC5D4\uC9C4\uC774 \uCC3E\uC9C0 \uBABB\uD574 \uC544\uBB34\uAC83\uB3C4 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uB97C \uB2E4\uC2DC \uC120\uD0DD\uD55C \uB4A4 \uC7AC\uC2DC\uB3C4\uD558\uC138\uC694.",
|
|
4476
|
+
"error.unresolvableAddress": "\uC5D4\uC9C4\uC774 \uD3B8\uC9D1 \uB300\uC0C1 \uBB38\uB2E8\uC774\uB098 run\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC77C\uBD80 \uC911\uCCA9 \uBB38\uB2E8 \uC548\uC758 run\uC740 \uC5D4\uC9C4\uC774 \uC8FC\uC18C\uB85C \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uCD5C\uC0C1\uC704 \uBB38\uB2E8\uC774\uB098 \uD45C \uC140\uC758 \uD14D\uC2A4\uD2B8\uC5D0 \uC11C\uC2DD\uC744 \uC9C0\uC815\uD558\uAC70\uB098, \uC774 \uB300\uC0C1\uC740 \uC6CC\uB4DC\uD504\uB85C\uC138\uC11C\uC5D0\uC11C \uD3B8\uC9D1\uD558\uC138\uC694.",
|
|
4434
4477
|
"error.runRangeConflict": "\uAC19\uC740 \uBB38\uB2E8\uC5D0 \uB300\uAE30 \uC911\uC778 \uC55E\uC120 \uBCC0\uACBD(\uD14D\uC2A4\uD2B8, \uB9C1\uD06C, \uD544\uB4DC, \uCC45\uAC08\uD53C, \uC774\uBBF8\uC9C0, \uC140)\uC774 \uBB38\uB2E8 \uD14D\uC2A4\uD2B8\uB97C \uBC14\uAFC0 \uC218 \uC788\uC5B4 run\uC774 \uB354 \uC774\uC0C1 \uAC19\uC740 \uAE00\uC790\uB97C \uAC00\uB9AC\uD0A4\uC9C0 \uC54A\uC744 \uC218 \uC788\uC73C\uBBC0\uB85C run \uC11C\uC2DD \uBCC0\uACBD\uC774 \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uB300\uAE30 \uBAA9\uB85D\uC5D0\uC11C \uC11C\uC2DD \uBCC0\uACBD\uC744 \uC81C\uAC70\uD558\uACE0 \uB098\uBA38\uC9C0\uB97C \uC801\uC6A9\uD55C \uB4A4 run \uC11C\uC2DD\uC744 \uB2E4\uC2DC \uC9C0\uC815\uD558\uAC70\uB098, \uC11C\uC2DD \uBCC0\uACBD\uC744 \uB2E4\uB978 \uBCC0\uACBD\uBCF4\uB2E4 \uBA3C\uC800 \uB300\uAE30\uC5F4\uC5D0 \uB123\uC73C\uC138\uC694.",
|
|
4435
4478
|
"error.rangeTagRefused": "\uC774 HWP \uBB38\uC11C\uC5D0\uB294 \uC5D4\uC9C4\uC774 \uC62E\uAE38 \uC218 \uC5C6\uB294 \uAD6C\uAC04 \uD45C\uC2DC\uAC00 \uC788\uC5B4 \uB300\uAE30 \uC911\uC778 \uD3B8\uC9D1\uC774 \uAC70\uBD80\uB418\uC5C8\uACE0, \uC544\uBB34\uAC83\uB3C4 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uAD6C\uAC04 \uD45C\uC2DC \uC8FC\uBCC0\uC758 \uAE00\uC790\uB97C \uC6C0\uC9C1\uC774\uB294 \uD3B8\uC9D1\uC740 \uAC70\uBD80\uB429\uB2C8\uB2E4: \uBB38\uB2E8 \uD14D\uC2A4\uD2B8 \uAE38\uC774 \uBCC0\uACBD, \uAE38\uC774\uAC00 \uB2E4\uB978 \uD544\uB4DC \uAC12\uC73C\uB85C\uC758 \uBCC0\uACBD, \uB9C1\uD06C\xB7\uD544\uB4DC\xB7\uCC45\uAC08\uD53C\xB7\uC774\uBBF8\uC9C0\uC758 \uC0BD\uC785\uC774\uB098 \uC0AD\uC81C, \uD45C \uC0AD\uC81C. \uAD6C\uAC04 \uD45C\uC2DC\uAC00 \uC788\uB294 \uD45C \uC140\uC740 \uB0B4\uC6A9\uC744 \uC5B4\uB5BB\uAC8C \uBC14\uAFB8\uB4E0 \uAC70\uBD80\uD569\uB2C8\uB2E4. \uC5D4\uC9C4\uC740 \uC5B4\uB290 \uD3B8\uC9D1\uC778\uC9C0 \uC54C\uB824 \uC8FC\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB300\uAE30 \uBAA9\uB85D\uC5D0\uC11C \uD574\uB2F9 \uD3B8\uC9D1\uC744 \uC81C\uAC70\uD558\uACE0(\uBB38\uB2E8 \uC548\uC758 \uAC19\uC740 \uAE38\uC774 \uD14D\uC2A4\uD2B8 \uCE58\uD658\uACFC \uAC19\uC740 \uAE38\uC774\uC758 \uD544\uB4DC \uAC12\uC740 \uD5C8\uC6A9\uB429\uB2C8\uB2E4) \uB098\uBA38\uC9C0\uB97C \uC801\uC6A9\uD558\uC138\uC694. \uADF8\uB7F0 \uD3B8\uC9D1\uC774 \uD544\uC694\uD558\uBA74 \uBB38\uC11C\uB97C HWPX\uB85C \uBCC0\uD658\uD558\uAC70\uB098(\uBCC0\uD658\uD558\uBA74 \uAD6C\uAC04 \uD45C\uC2DC\uAC00 \uC0AC\uB77C\uC9D1\uB2C8\uB2E4) \uD55C\uAE00\uC5D0\uC11C \uAD6C\uAC04 \uD45C\uC2DC\uB97C \uC81C\uAC70\uD55C \uB4A4 \uB2E4\uC2DC \uC5EC\uC138\uC694. \uB2E4\uC2DC \uC5F4\uBA74 \uB300\uAE30 \uBAA9\uB85D\uC740 \uC0AC\uB77C\uC9D1\uB2C8\uB2E4.",
|
|
4436
4479
|
"error.noEngineCause": "\uC5D4\uC9C4\uC774 \uC6D0\uC778\uC744 \uBC1D\uD788\uC9C0 \uC54A\uACE0 \uC2E4\uD328\uD588\uC73C\uBBC0\uB85C(\uB300\uAC1C \uBE44\uC815\uC0C1 \uC885\uB8CC\uB41C \uACBD\uC6B0\uC785\uB2C8\uB2E4) \uC544\uBB34\uAC83\uB3C4 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC801\uC6A9\uD558\uACE0, \uAC19\uC740 \uBC29\uC2DD\uC73C\uB85C \uC2E4\uD328\uD558\uBA74 \uD638\uC2A4\uD2B8 \uB85C\uADF8\uC640 \uD568\uAED8 \uC54C\uB824 \uC8FC\uC138\uC694.",
|
|
@@ -4608,7 +4651,8 @@ var RENDER_SVG = { format: "svg" };
|
|
|
4608
4651
|
var REFUSAL_MESSAGE = {
|
|
4609
4652
|
unmatched_replace: "error.unmatchedReplace",
|
|
4610
4653
|
run_range_conflict: "error.runRangeConflict",
|
|
4611
|
-
range_tag: "error.rangeTagRefused"
|
|
4654
|
+
range_tag: "error.rangeTagRefused",
|
|
4655
|
+
unresolvable_address: "error.unresolvableAddress"
|
|
4612
4656
|
};
|
|
4613
4657
|
var HwpEditor = forwardRef(
|
|
4614
4658
|
function HwpEditor2(props, ref) {
|
|
@@ -5477,6 +5521,31 @@ var HwpEditor = forwardRef(
|
|
|
5477
5521
|
) });
|
|
5478
5522
|
}
|
|
5479
5523
|
);
|
|
5524
|
+
|
|
5525
|
+
// src/sanitize.ts
|
|
5526
|
+
function sanitizeSvg(svgText) {
|
|
5527
|
+
const doc = new DOMParser().parseFromString(svgText, "image/svg+xml");
|
|
5528
|
+
const root = doc.documentElement;
|
|
5529
|
+
if (root === null || root.nodeName === "parsererror" || doc.getElementsByTagName("parsererror").length > 0) {
|
|
5530
|
+
return "";
|
|
5531
|
+
}
|
|
5532
|
+
const dangerous = root.querySelectorAll("script, foreignObject");
|
|
5533
|
+
for (const node of Array.from(dangerous)) node.remove();
|
|
5534
|
+
scrubElement(root);
|
|
5535
|
+
return root.outerHTML;
|
|
5536
|
+
}
|
|
5537
|
+
function scrubElement(el) {
|
|
5538
|
+
for (const attr of Array.from(el.attributes)) {
|
|
5539
|
+
const name = attr.name.toLowerCase();
|
|
5540
|
+
const value = attr.value.trim().toLowerCase();
|
|
5541
|
+
if (name.startsWith("on")) {
|
|
5542
|
+
el.removeAttribute(attr.name);
|
|
5543
|
+
} else if ((name === "href" || name === "xlink:href") && value.startsWith("javascript:")) {
|
|
5544
|
+
el.removeAttribute(attr.name);
|
|
5545
|
+
}
|
|
5546
|
+
}
|
|
5547
|
+
for (const child of Array.from(el.children)) scrubElement(child);
|
|
5548
|
+
}
|
|
5480
5549
|
export {
|
|
5481
5550
|
COMPOSE_PRESETS,
|
|
5482
5551
|
ComponentsPanel,
|