@hwp-editor/react 1.4.0 → 1.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.cjs CHANGED
@@ -58,7 +58,7 @@ module.exports = __toCommonJS(index_exports);
58
58
 
59
59
  // src/HwpEditor.tsx
60
60
  var import_react13 = require("react");
61
- var import_core19 = require("@hwp-editor/core");
61
+ var import_core20 = require("@hwp-editor/core");
62
62
 
63
63
  // src/context.ts
64
64
  var import_react = require("react");
@@ -75,7 +75,7 @@ function useHwpEditorContext() {
75
75
 
76
76
  // src/PageCanvas.tsx
77
77
  var import_react2 = require("react");
78
- var import_core2 = require("@hwp-editor/core");
78
+ var import_core3 = require("@hwp-editor/core");
79
79
 
80
80
  // src/geometry.ts
81
81
  var import_core = require("@hwp-editor/core");
@@ -130,21 +130,46 @@ function idIndex(envelope) {
130
130
  idIndexes.set(envelope, index);
131
131
  return index;
132
132
  }
133
+ var pathIndexes = /* @__PURE__ */ new WeakMap();
134
+ function pathIndex(envelope) {
135
+ const cached = pathIndexes.get(envelope);
136
+ if (cached !== void 0) return cached;
137
+ const index = /* @__PURE__ */ new Map();
138
+ for (const segment of envelope.segments) {
139
+ if (segment.id !== void 0 || segment.kind !== "para") continue;
140
+ const key = [segment.section, ...segment.indices ?? [segment.para]].join(".");
141
+ if (!index.has(key)) index.set(key, segment);
142
+ }
143
+ pathIndexes.set(envelope, index);
144
+ return index;
145
+ }
146
+ function rowSegment(envelope, rowId) {
147
+ const byId = idIndex(envelope).get(rowId);
148
+ if (byId !== void 0) return byId;
149
+ const path = (0, import_core.segmentIdPath)(rowId);
150
+ return path.length === 0 ? void 0 : pathIndex(envelope).get(path.join("."));
151
+ }
152
+ function hasIds(envelope) {
153
+ return envelope.segments.some((segment) => segment.id !== void 0);
154
+ }
155
+ function rowDraws(envelope, rowId, segment) {
156
+ if (segment.id !== void 0) return rowId === segment.id;
157
+ return rowSegment(envelope, rowId) === segment;
158
+ }
133
159
  function segmentOnPage(envelope, page, xFraction, yFraction, pageIndex, pageCount) {
134
- if (page.layout === void 0 || !envelope.segments.some((segment) => segment.id !== void 0)) {
160
+ if (page.layout === void 0 || !hasIds(envelope) && page.layout.rows.every((row) => rowSegment(envelope, row.id) === void 0)) {
135
161
  return nearestSegment(envelope, pageIndex, yFraction, pageCount);
136
162
  }
137
163
  const { width_pt, height_pt, rows } = page.layout;
138
164
  const x = clamp(xFraction, 0, 1) * width_pt;
139
165
  const y = clamp(yFraction, 0, 1) * height_pt;
140
- const byId = idIndex(envelope);
141
166
  let best;
142
167
  let bestVertical = Infinity;
143
168
  let bestHorizontal = Infinity;
144
169
  let bestArea = Infinity;
145
170
  for (const row of rows) {
146
171
  const box = row.box;
147
- const segment = byId.get(row.id);
172
+ const segment = rowSegment(envelope, row.id);
148
173
  if (box === null || segment === void 0) continue;
149
174
  const dx = Math.max(box.x0 - x, 0, x - box.x1);
150
175
  const dy = Math.max(box.y0 - y, 0, y - box.y1);
@@ -161,13 +186,13 @@ function segmentOnPage(envelope, page, xFraction, yFraction, pageIndex, pageCoun
161
186
  return best;
162
187
  }
163
188
  function bandsOnPage(envelope, segment, page, pageIndex, pageCount) {
164
- if (page.layout === void 0 || segment.id === void 0) {
189
+ if (page.layout === void 0) {
165
190
  const band = segmentBand(envelope, segment, pageCount);
166
- return band?.pageIndex === pageIndex ? [{ left: 0, top: band.top, width: 100, height: band.height }] : [];
191
+ return band?.pageIndex === pageIndex ? [{ left: 0, top: band.top, width: 100, height: band.height, approximate: true }] : [];
167
192
  }
168
193
  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;
169
194
  const { width_pt, height_pt, rows } = page.layout;
170
- return rows.filter((row) => row.id === target.id && row.box !== null).map((row) => {
195
+ return rows.filter((row) => row.box !== null && rowDraws(envelope, row.id, target)).map((row) => {
171
196
  const box = row.box;
172
197
  return {
173
198
  left: box.x0 / width_pt * 100,
@@ -179,6 +204,7 @@ function bandsOnPage(envelope, segment, page, pageIndex, pageCount) {
179
204
  }
180
205
 
181
206
  // src/text.ts
207
+ var import_core2 = require("@hwp-editor/core");
182
208
  function plainSegmentText(markdownSlice) {
183
209
  return markdownSlice.split("\n").map(stripLine).join("\n").trim();
184
210
  }
@@ -192,6 +218,41 @@ function stripLine(line) {
192
218
  out = out.replace(/`([^`]*)`/g, "$1");
193
219
  return out;
194
220
  }
221
+ var LIST_MARKER = /^[ \t]*(?:\d{1,9}[.)]|[-*+])[ \t]+/;
222
+ function paragraphSourceText(envelope, para, pages = []) {
223
+ const plain = plainSegmentText((0, import_core2.segmentText)(envelope, para));
224
+ const marker = LIST_MARKER.exec(plain);
225
+ if (marker === null) return plain;
226
+ const stripped = plain.slice(marker[0].length);
227
+ if (para.indices !== void 0) {
228
+ 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));
229
+ if (first !== void 0) {
230
+ const lead = [...(0, import_core2.segmentText)(envelope, para)].slice(0, Math.max(0, first.start - para.start)).join("");
231
+ return LIST_MARKER.test(lead) && lead.trim() === marker[0].trim() ? stripped : plain;
232
+ }
233
+ }
234
+ const length = sourceLength(para, pages);
235
+ if (length === void 0) return plain;
236
+ return stripped.length === length && plain.length !== length ? stripped : plain;
237
+ }
238
+ function sourceLength(para, pages) {
239
+ let start = Infinity;
240
+ let end = -Infinity;
241
+ for (const page of pages) {
242
+ for (const row of page.layout?.rows ?? []) {
243
+ if (row.kind !== "para" || row.source_chars === null || !rowMatches(row.id, para)) continue;
244
+ start = Math.min(start, row.source_chars.start);
245
+ end = Math.max(end, row.source_chars.end);
246
+ }
247
+ }
248
+ return end >= start ? end - start : void 0;
249
+ }
250
+ function rowMatches(rowId, segment) {
251
+ if (segment.id !== void 0) return rowId === segment.id;
252
+ const path = (0, import_core2.segmentIdPath)(rowId);
253
+ const own = [segment.section, ...segment.indices ?? [segment.para]];
254
+ return path.length === own.length && path.every((value, index) => value === own[index]);
255
+ }
195
256
 
196
257
  // src/PageCanvas.tsx
197
258
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -206,11 +267,11 @@ function PageCanvas() {
206
267
  if (envelope !== null) {
207
268
  const refs = state.selections?.length ? state.selections : selection === null ? [] : [selection];
208
269
  for (const ref of refs) {
209
- const segment = (0, import_core2.segmentAtRef)(envelope, ref);
270
+ const segment = (0, import_core3.segmentAtRef)(envelope, ref);
210
271
  if (segment !== void 0) selectedSegments.add(segment);
211
272
  }
212
273
  }
213
- if (pages.length === 0) {
274
+ if (pages.length === 0 || envelope === null) {
214
275
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "hwped-canvas", role: "main", "aria-label": t("canvas.aria"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "hwped-empty", children: t("canvas.emptyPages") }) });
215
276
  }
216
277
  const selectAt = (pageIndex, xFraction, yFraction, modifiers) => {
@@ -218,7 +279,7 @@ function PageCanvas() {
218
279
  const segment = segmentOnPage(envelope, pages[pageIndex], xFraction, yFraction, pageIndex, pages.length);
219
280
  if (segment !== void 0) {
220
281
  const mode = modifiers?.shiftKey ? "extend" : modifiers?.metaKey || modifiers?.ctrlKey ? "toggle" : "single";
221
- store.dispatch((0, import_core2.selectionAction)(envelope, store.getState(), segment, mode));
282
+ store.dispatch((0, import_core3.selectionAction)(envelope, store.getState(), segment, mode));
222
283
  }
223
284
  };
224
285
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "hwped-canvas", role: "main", "aria-label": t("canvas.aria"), children: pages.map((page, pageIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -264,7 +325,7 @@ function bandsForPage(envelope, page, pageIndex, pageCount, selected, pendingKey
264
325
  bands.push({
265
326
  ...band,
266
327
  key: `${isSelected ? "sel" : "pend"}-${key}-${bandIdentity(segment)}-${index}`,
267
- className: isSelected ? "hwped-band hwped-band-selected" : "hwped-band hwped-band-pending"
328
+ className: (isSelected ? "hwped-band hwped-band-selected" : "hwped-band hwped-band-pending") + (band.approximate ? " hwped-band-approximate" : "")
268
329
  });
269
330
  }
270
331
  }
@@ -276,7 +337,7 @@ function PageView(props) {
276
337
  const label = t("page.label", { page: page.page });
277
338
  const imgSrc = (0, import_react2.useMemo)(() => {
278
339
  const mime = page.format === "jpeg" ? "image/jpeg" : page.format === "svg" ? "image/svg+xml" : `image/${page.format}`;
279
- return `data:${mime};base64,${import_core2.base64.encode(page.data)}`;
340
+ return `data:${mime};base64,${import_core3.base64.encode(page.data)}`;
280
341
  }, [page]);
281
342
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
282
343
  "div",
@@ -308,6 +369,7 @@ function PageView(props) {
308
369
  "div",
309
370
  {
310
371
  className: band.className,
372
+ "data-approximate": band.className.includes("hwped-band-approximate") ? "true" : void 0,
311
373
  style: { top: `${band.top}%`, height: `${band.height}%`, left: `${band.left}%`, width: `${band.width}%` }
312
374
  },
313
375
  band.key
@@ -340,7 +402,7 @@ function targetsSegment(op, envelope, segment) {
340
402
  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);
341
403
  }
342
404
  const target = opSearchText(op);
343
- return target !== null && target !== "" && plainSegmentText((0, import_core2.segmentText)(envelope, segment)).includes(target);
405
+ return target !== null && target !== "" && plainSegmentText((0, import_core3.segmentText)(envelope, segment)).includes(target);
344
406
  }
345
407
 
346
408
  // src/unit-input.ts
@@ -351,7 +413,7 @@ function isPlainDecimal(value) {
351
413
 
352
414
  // src/SegmentInspector.tsx
353
415
  var import_react3 = require("react");
354
- var import_core3 = require("@hwp-editor/core");
416
+ var import_core4 = require("@hwp-editor/core");
355
417
  var import_jsx_runtime2 = require("react/jsx-runtime");
356
418
  var ALIGNMENTS = [
357
419
  { value: "left", key: "segment.alignLeft" },
@@ -381,15 +443,15 @@ function SegmentInspector() {
381
443
  const paragraphKeys = /* @__PURE__ */ new Set();
382
444
  const paragraphs = (envelope?.segments.filter((candidate) => {
383
445
  if (candidate.kind !== "para") return false;
384
- if (!(0, import_core3.hasOwnRef)(candidate)) return false;
446
+ if (!(0, import_core4.hasOwnRef)(candidate)) return false;
385
447
  const key = candidate.id ?? `${candidate.section}:${candidate.para}`;
386
448
  if (paragraphKeys.has(key)) return false;
387
449
  paragraphKeys.add(key);
388
450
  return true;
389
- }) ?? []).sort(import_core3.compareSegments);
390
- const selected = envelope === null ? [] : (0, import_core3.selectedParagraphs)(envelope, state);
391
- const segment = envelope !== null && selection !== null ? (0, import_core3.segmentAtRef)(envelope, selection) : void 0;
392
- const targets = envelope !== null && segment !== void 0 ? (0, import_core3.resolveSelection)(envelope, segment) : void 0;
451
+ }) ?? []).sort(import_core4.compareSegments);
452
+ const selected = envelope === null ? [] : (0, import_core4.selectedParagraphs)(envelope, state);
453
+ const segment = envelope !== null && selection !== null ? (0, import_core4.segmentAtRef)(envelope, selection) : void 0;
454
+ const targets = envelope !== null && segment !== void 0 ? (0, import_core4.resolveSelection)(envelope, segment) : void 0;
393
455
  const paragraphTargets = targets?.paragraphs ?? [];
394
456
  const singleParagraph = targets?.paragraph;
395
457
  const directStyleRow = (() => {
@@ -397,13 +459,13 @@ function SegmentInspector() {
397
459
  if (id === void 0 || styles === null) return void 0;
398
460
  return styles.styles.find((item) => item.kind === "paragraph" && item.used_by.includes(id));
399
461
  })();
400
- 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" ? (0, import_core3.stylesVersionSupported)(capabilities.version) ? "styles.unavailableHost" : "segment.directStyleUnavailable" : "segment.directStyleUnknown";
462
+ 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" ? (0, import_core4.stylesVersionSupported)(capabilities.version) ? "styles.unavailableHost" : "segment.directStyleUnavailable" : "segment.directStyleUnknown";
401
463
  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");
402
464
  const clickRefusedText = (reason) => reason === null || reason === void 0 ? null : t(CLICK_REFUSAL_MESSAGE[reason]);
403
465
  const refusalReason2 = refusalText(targets?.refusal);
404
- const targetLabel = segment === void 0 ? "" : segment.kind === "cell" ? t("segment.selectedCell", { path: (0, import_core3.segmentPath)(segment), count: paragraphTargets.length }) : singleParagraph === void 0 ? "" : t("segment.selectedPara", { path: (0, import_core3.segmentPath)(singleParagraph) });
405
- const currentText = envelope !== null && paragraphTargets.length > 0 ? plainSegmentText(paragraphTargets.map((candidate) => (0, import_core3.segmentText)(envelope, candidate)).join("\n")) : "";
406
- const markdownText = envelope !== null && singleParagraph !== void 0 ? (0, import_core3.segmentText)(envelope, singleParagraph) : "";
466
+ const targetLabel = segment === void 0 ? "" : segment.kind === "cell" ? t("segment.selectedCell", { path: (0, import_core4.segmentPath)(segment), count: paragraphTargets.length }) : singleParagraph === void 0 ? "" : t("segment.selectedPara", { path: (0, import_core4.segmentPath)(singleParagraph) });
467
+ const currentText = envelope !== null && paragraphTargets.length > 0 ? paragraphTargets.map((candidate) => paragraphSourceText(envelope, candidate, state.pages)).join("\n").trim() : "";
468
+ const markdownText = envelope !== null && singleParagraph !== void 0 ? (0, import_core4.segmentText)(envelope, singleParagraph) : "";
407
469
  const supplementaryStarts = (0, import_react3.useMemo)(() => {
408
470
  const positions = [];
409
471
  let offset = 0;
@@ -439,19 +501,19 @@ function SegmentInspector() {
439
501
  setPlaceholderName("");
440
502
  }, [selection]);
441
503
  const find = currentText;
442
- const address = singleParagraph === void 0 ? void 0 : (0, import_core3.paragraphAddress)(singleParagraph);
504
+ const address = singleParagraph === void 0 ? void 0 : (0, import_core4.paragraphAddress)(singleParagraph);
443
505
  const siblings = singleParagraph?.indices === void 0 ? [] : paragraphs.filter((candidate) => candidate.section === singleParagraph.section && candidate.id !== void 0 && candidate.indices !== void 0 && candidate.indices.length === singleParagraph.indices.length && singleParagraph.indices.slice(0, -1).every((index, at) => candidate.indices[at] === index));
444
506
  const siblingAt = siblings.findIndex((candidate) => candidate.id === singleParagraph?.id);
445
507
  const addressedUnavailable = capabilities?.editOps !== "v1";
446
508
  const disabled = !editable || addressedUnavailable || state.status === "applying";
447
509
  const selectedRefs = state.selections?.length ? state.selections : selection === null ? [] : [selection];
448
510
  const explicitSegments = envelope === null ? [] : selectedRefs.flatMap((ref) => {
449
- const item = (0, import_core3.segmentAtRef)(envelope, ref);
511
+ const item = (0, import_core4.segmentAtRef)(envelope, ref);
450
512
  return item === void 0 ? [] : [item];
451
513
  });
452
514
  const hasRunSelection = explicitSegments.some((item) => item.kind === "run");
453
515
  const multiSelection = explicitSegments.length > 1;
454
- const paragraphRun = multiSelection && explicitSegments.every((item) => item.kind === "para") ? [...explicitSegments].sort(import_core3.compareSegments) : null;
516
+ const paragraphRun = multiSelection && explicitSegments.every((item) => item.kind === "para") ? [...explicitSegments].sort(import_core4.compareSegments) : null;
455
517
  const runStart = paragraphRun === null ? -1 : siblings.findIndex((candidate) => candidate.id === paragraphRun[0]?.id);
456
518
  const runContiguous = paragraphRun !== null && runStart >= 0 && paragraphRun.every((item, at) => siblings[runStart + at]?.id === item.id);
457
519
  const runEnd = runContiguous ? runStart + paragraphRun.length - 1 : -1;
@@ -462,7 +524,7 @@ function SegmentInspector() {
462
524
  if (insertionRun === null || !insertionCurrent || runParent === void 0) return [];
463
525
  const section = insertionRun[0].section;
464
526
  return insertedHidden.ids.flatMap((id) => {
465
- const [idSection, ...indices] = (0, import_core3.segmentIdPath)(id);
527
+ const [idSection, ...indices] = (0, import_core4.segmentIdPath)(id);
466
528
  if (idSection !== section || indices.length !== runParent.length + 1) return [];
467
529
  if (!runParent.every((index, at) => indices[at] === index)) return [];
468
530
  return [{ id, index: indices[indices.length - 1] }];
@@ -473,16 +535,16 @@ function SegmentInspector() {
473
535
  if (published === void 0) return void 0;
474
536
  const publishedIndex = published.indices?.[published.indices.length - 1];
475
537
  const beyond = hiddenInRun.filter((hidden) => publishedIndex === void 0 || (edge === "first" ? hidden.index < publishedIndex : hidden.index > publishedIndex));
476
- if (beyond.length === 0) return (0, import_core3.paragraphAddress)(published);
538
+ if (beyond.length === 0) return (0, import_core4.paragraphAddress)(published);
477
539
  const pick = beyond.reduce((best, hidden) => (edge === "first" ? hidden.index < best.index : hidden.index > best.index) ? hidden : best);
478
540
  return { id: pick.id };
479
541
  };
480
542
  const textSelectionRestricted = multiSelection || hasRunSelection || state.range != null;
481
543
  const singleTargetDisabled = disabled || textSelectionRestricted || address === void 0;
482
544
  const reorderRefused = multiSelection && !runContiguous;
483
- const definitionLocked = paragraphRun === null ? singleParagraph !== void 0 && (0, import_core3.isSectionDefinitionParagraph)(singleParagraph) : paragraphRun.some(import_core3.isSectionDefinitionParagraph);
545
+ const definitionLocked = paragraphRun === null ? singleParagraph !== void 0 && (0, import_core4.isSectionDefinitionParagraph)(singleParagraph) : paragraphRun.some(import_core4.isSectionDefinitionParagraph);
484
546
  const moveUpNeighbour = siblings[(paragraphRun === null ? siblingAt : runStart) - 1];
485
- const definitionAbove = moveUpNeighbour !== void 0 && (0, import_core3.isSectionDefinitionParagraph)(moveUpNeighbour);
547
+ const definitionAbove = moveUpNeighbour !== void 0 && (0, import_core4.isSectionDefinitionParagraph)(moveUpNeighbour);
486
548
  const deleteDisabled = disabled || definitionLocked || (paragraphRun === null ? textSelectionRestricted || address === void 0 : !runContiguous);
487
549
  const moveUpDisabled = deleteDisabled || definitionAbove || (paragraphRun === null ? siblingAt <= 0 : runStart <= 0);
488
550
  const moveDownDisabled = deleteDisabled || (paragraphRun === null ? siblingAt < 0 || siblingAt >= siblings.length - 1 : runEnd >= siblings.length - 1);
@@ -490,12 +552,12 @@ function SegmentInspector() {
490
552
  const legacyLinkSeparator = capabilities?.editOps !== "v1" && [currentText, linkText, linkUrl].some((value) => value.includes("=>"));
491
553
  const validShape = isPlainDecimal(shapeValue) && (shapeKey !== "line-spacing" || Number(shapeValue) > 0);
492
554
  const validFontSize = fontSize.trim() === "" || isPlainDecimal(fontSize) && Number(fontSize) > 0;
493
- const formatInputs = envelope === null ? [] : explicitSegments.flatMap((item) => item.kind === "run" ? [item] : (0, import_core3.resolveSelection)(envelope, item).paragraphs).sort(import_core3.compareSegments);
494
- const formatTargets = envelope === null ? [] : (0, import_core3.formatTargets)(envelope, formatInputs, state.range, (0, import_core3.documentFormat)(state.document?.name, state.document?.data));
495
- const runs = envelope === null || singleParagraph === void 0 ? [] : (0, import_core3.paragraphRuns)(envelope, singleParagraph);
555
+ const formatInputs = envelope === null ? [] : explicitSegments.flatMap((item) => item.kind === "run" ? [item] : (0, import_core4.resolveSelection)(envelope, item).paragraphs).sort(import_core4.compareSegments);
556
+ const formatTargets = envelope === null ? [] : (0, import_core4.formatTargets)(envelope, formatInputs, state.range, (0, import_core4.documentFormat)(state.document?.name, state.document?.data));
557
+ const runs = envelope === null || singleParagraph === void 0 ? [] : (0, import_core4.paragraphRuns)(envelope, singleParagraph);
496
558
  const formatNoRuns = state.range == null && singleParagraph !== void 0 && singleParagraph.start !== singleParagraph.end && singleParagraph.indices === void 0 && runs.length === 0;
497
559
  const rangeAvailable = !multiSelection && runs.some((run) => run.id !== void 0 || singleParagraph?.indices?.length === 1 && run.indices?.length === 2);
498
- const charPrimary = segment?.kind === "run" ? segment : envelope !== null && state.range != null && formatTargets[0]?.address.id !== void 0 ? (0, import_core3.segmentAtRef)(envelope, { id: formatTargets[0].address.id, section: 0, para: 0 }) ?? singleParagraph : singleParagraph;
560
+ const charPrimary = segment?.kind === "run" ? segment : envelope !== null && state.range != null && formatTargets[0]?.address.id !== void 0 ? (0, import_core4.segmentAtRef)(envelope, { id: formatTargets[0].address.id, section: 0, para: 0 }) ?? singleParagraph : singleParagraph;
499
561
  const plainParagraph = (() => {
500
562
  if (envelope === null || singleParagraph === void 0) return void 0;
501
563
  const depth = singleParagraph.indices?.length;
@@ -506,7 +568,7 @@ function SegmentInspector() {
506
568
  const contentEnd = singleParagraph.start + points.length;
507
569
  const holdsControl = envelope.segments.some((item) => (item.kind === "field" || item.kind === "image" || item.kind === "bookmark") && item.start < contentEnd && item.end > singleParagraph.start);
508
570
  if (holdsControl) return void 0;
509
- const ordered = (0, import_core3.paragraphRuns)(envelope, singleParagraph);
571
+ const ordered = (0, import_core4.paragraphRuns)(envelope, singleParagraph);
510
572
  let cursor = singleParagraph.start;
511
573
  for (const run of ordered) {
512
574
  if (run.start !== cursor || run.end < run.start || run.end > contentEnd) return void 0;
@@ -522,7 +584,7 @@ function SegmentInspector() {
522
584
  if (/^(?:[-+]|\d{1,3}[.)])\s/u.test(text)) return void 0;
523
585
  return { runs: ordered, texts, text };
524
586
  })();
525
- const rangeRuns = envelope !== null && singleParagraph !== void 0 && state.range != null ? (0, import_core3.paragraphRuns)(envelope, singleParagraph, state.range) : [];
587
+ const rangeRuns = envelope !== null && singleParagraph !== void 0 && state.range != null ? (0, import_core4.paragraphRuns)(envelope, singleParagraph, state.range) : [];
526
588
  const rangeFrom = rangeRuns.length === 0 ? -1 : Math.min(...rangeRuns.map((item) => item.start));
527
589
  const rangeTo = rangeRuns.length === 0 ? -1 : Math.max(...rangeRuns.map((item) => item.end));
528
590
  const wholeRuns = state.range != null && rangeRuns.length > 0 && rangeFrom === state.range.start && rangeTo === state.range.end;
@@ -555,7 +617,7 @@ function SegmentInspector() {
555
617
  };
556
618
  const activateSelection = (target, mode) => {
557
619
  if (envelope === null) return;
558
- store.dispatch((0, import_core3.selectionAction)(envelope, store.getState(), target, mode));
620
+ store.dispatch((0, import_core4.selectionAction)(envelope, store.getState(), target, mode));
559
621
  };
560
622
  const handleOptionKeyDown = (event, target) => {
561
623
  if (event.key !== "Enter" && event.key !== " ") return;
@@ -566,11 +628,11 @@ function SegmentInspector() {
566
628
  if (envelope === null || singleParagraph === void 0) return;
567
629
  const match = level === "char" ? charPrimary : singleParagraph;
568
630
  if (match === void 0) return;
569
- const refs = (0, import_core3.selectSimilar)(envelope, match, level);
631
+ const refs = (0, import_core4.selectSimilar)(envelope, match, level);
570
632
  if (refs.length === 0) return;
571
633
  const activeMatch = refs.find((ref) => {
572
- const item = (0, import_core3.segmentAtRef)(envelope, ref);
573
- const parent = item === void 0 ? void 0 : (0, import_core3.resolveSelection)(envelope, item).paragraph;
634
+ const item = (0, import_core4.segmentAtRef)(envelope, ref);
635
+ const parent = item === void 0 ? void 0 : (0, import_core4.resolveSelection)(envelope, item).paragraph;
574
636
  return parent === singleParagraph || parent?.id !== void 0 && parent.id === singleParagraph.id || parent !== void 0 && parent.id === void 0 && singleParagraph.id === void 0 && parent.section === singleParagraph.section && parent.para === singleParagraph.para;
575
637
  });
576
638
  const primary = activeMatch ?? refs[0];
@@ -593,7 +655,7 @@ function SegmentInspector() {
593
655
  type: "queueOp",
594
656
  op: {
595
657
  kind: "move-para",
596
- address: (0, import_core3.paragraphAddress)(neighbour2),
658
+ address: (0, import_core4.paragraphAddress)(neighbour2),
597
659
  to: { address: anchor2, position: direction === -1 ? "after" : "before" }
598
660
  }
599
661
  });
@@ -607,7 +669,7 @@ function SegmentInspector() {
607
669
  op: {
608
670
  kind: "move-para",
609
671
  address,
610
- to: { address: (0, import_core3.paragraphAddress)(neighbour2), position: direction === -1 ? "before" : "after" }
672
+ to: { address: (0, import_core4.paragraphAddress)(neighbour2), position: direction === -1 ? "before" : "after" }
611
673
  }
612
674
  });
613
675
  return;
@@ -620,7 +682,7 @@ function SegmentInspector() {
620
682
  type: "queueOp",
621
683
  op: {
622
684
  kind: "move-para",
623
- address: (0, import_core3.paragraphAddress)(neighbour),
685
+ address: (0, import_core4.paragraphAddress)(neighbour),
624
686
  to: { address: anchor, position: direction === -1 ? "after" : "before" }
625
687
  }
626
688
  });
@@ -645,7 +707,7 @@ function SegmentInspector() {
645
707
  store.dispatch({
646
708
  type: "queueOps",
647
709
  ops: [
648
- ...paragraphRun.map((item) => ({ kind: "delete-para", address: (0, import_core3.paragraphAddress)(item) })),
710
+ ...paragraphRun.map((item) => ({ kind: "delete-para", address: (0, import_core4.paragraphAddress)(item) })),
649
711
  // The insertion's unpublished paragraphs go with it (#521).
650
712
  ...hiddenInRun.map((hidden) => ({ kind: "delete-para", address: { id: hidden.id } }))
651
713
  ]
@@ -669,7 +731,7 @@ function SegmentInspector() {
669
731
  style: { maxHeight: "12rem", overflowY: "auto" },
670
732
  children: paragraphs.map((candidate, index) => {
671
733
  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);
672
- const snippet = plainSegmentText((0, import_core3.segmentText)(envelope, candidate)).trim().replace(/\s+/g, " ");
734
+ const snippet = plainSegmentText((0, import_core4.segmentText)(envelope, candidate)).trim().replace(/\s+/g, " ");
673
735
  const text = snippet.length > 96 ? `${snippet.slice(0, 93)}...` : snippet || t("segment.emptyPara");
674
736
  const label = t("segment.choiceLabel", { index: index + 1, text });
675
737
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -845,7 +907,7 @@ function SegmentInspector() {
845
907
  disabled: disabled || selected.length === 0,
846
908
  onClick: () => store.dispatch({
847
909
  type: "queueOps",
848
- ops: selected.map((target) => ({ kind: "set-align", address: (0, import_core3.paragraphAddress)(target), alignment: align.value }))
910
+ ops: selected.map((target) => ({ kind: "set-align", address: (0, import_core4.paragraphAddress)(target), alignment: align.value }))
849
911
  }),
850
912
  children: t(align.key)
851
913
  },
@@ -891,7 +953,7 @@ function SegmentInspector() {
891
953
  type: "button",
892
954
  className: "hwped-btn",
893
955
  disabled: disabled || selected.length === 0 || !validShape,
894
- onClick: () => store.dispatch({ type: "queueOps", ops: selected.map((target) => ({ kind: "set-para", address: (0, import_core3.paragraphAddress)(target), key: shapeKey, value: `${shapeValue.trim()}${shapeKey === "line-spacing" ? lineSpacingUnit : "mm"}` })) }),
956
+ onClick: () => store.dispatch({ type: "queueOps", ops: selected.map((target) => ({ kind: "set-para", address: (0, import_core4.paragraphAddress)(target), key: shapeKey, value: `${shapeValue.trim()}${shapeKey === "line-spacing" ? lineSpacingUnit : "mm"}` })) }),
895
957
  children: t("segment.setPara")
896
958
  }
897
959
  )
@@ -907,7 +969,7 @@ function SegmentInspector() {
907
969
  charPrimary?.directChar === void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "hwped-hint", children: t("segment.similarCharUnavailable") }),
908
970
  singleParagraph.directPara === void 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "hwped-hint", children: t("segment.similarParaUnavailable") })
909
971
  ] }),
910
- singleParagraph !== void 0 && (0, import_core3.hasDirectFormatting)(singleParagraph) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hwped-field", children: [
972
+ singleParagraph !== void 0 && (0, import_core4.hasDirectFormatting)(singleParagraph) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hwped-field", children: [
911
973
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hwped-label", children: t("segment.directFormatting") }),
912
974
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "hwped-hint", children: t("segment.directFormattingHint") }),
913
975
  directStyleRow === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "hwped-hint", children: t(directStyleWhy ?? "segment.directStyleUnknown") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -916,7 +978,7 @@ function SegmentInspector() {
916
978
  type: "button",
917
979
  className: "hwped-btn",
918
980
  disabled,
919
- onClick: () => store.dispatch({ type: "queueOp", op: { kind: "apply-style", address: (0, import_core3.paragraphAddress)(singleParagraph), style: directStyleRow.id, clearDirect: true } }),
981
+ onClick: () => store.dispatch({ type: "queueOp", op: { kind: "apply-style", address: (0, import_core4.paragraphAddress)(singleParagraph), style: directStyleRow.id, clearDirect: true } }),
920
982
  children: t("segment.clearDirect")
921
983
  }
922
984
  )
@@ -990,7 +1052,7 @@ function SegmentInspector() {
990
1052
  }
991
1053
  setRangeSplit(false);
992
1054
  store.dispatch({ type: "setRange", range: start === end ? null : {
993
- paragraph: (0, import_core3.segmentRef)(singleParagraph),
1055
+ paragraph: (0, import_core4.segmentRef)(singleParagraph),
994
1056
  start: singleParagraph.start + start,
995
1057
  end: singleParagraph.start + end
996
1058
  } });
@@ -1008,7 +1070,7 @@ function SegmentInspector() {
1008
1070
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "hwped-btn", onClick: () => {
1009
1071
  setRangeSplit(false);
1010
1072
  rangeInput.current?.setSelectionRange(0, 0);
1011
- if (hasRunSelection) store.dispatch({ type: "select", selection: (0, import_core3.segmentRef)(singleParagraph) });
1073
+ if (hasRunSelection) store.dispatch({ type: "select", selection: (0, import_core4.segmentRef)(singleParagraph) });
1012
1074
  else store.dispatch({ type: "setRange", range: null });
1013
1075
  }, children: t("segment.rangeClear") })
1014
1076
  ] })
@@ -1168,7 +1230,7 @@ function wholeRunText(run, markdown) {
1168
1230
 
1169
1231
  // src/StylesPanel.tsx
1170
1232
  var import_react4 = require("react");
1171
- var import_core4 = require("@hwp-editor/core");
1233
+ var import_core5 = require("@hwp-editor/core");
1172
1234
  var import_jsx_runtime3 = require("react/jsx-runtime");
1173
1235
  var ALIGNMENTS2 = ["justify", "left", "right", "center", "distribute"];
1174
1236
  var ALIGN_LABEL = {
@@ -1241,7 +1303,7 @@ function DefinitionForm(props) {
1241
1303
  type: "queueOps",
1242
1304
  ops: paragraphs.map((paragraph) => ({
1243
1305
  kind: "apply-style",
1244
- address: (0, import_core4.paragraphAddress)(paragraph),
1306
+ address: (0, import_core5.paragraphAddress)(paragraph),
1245
1307
  style: row.id
1246
1308
  }))
1247
1309
  });
@@ -1365,12 +1427,12 @@ function StylesPanel() {
1365
1427
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hwped-panel", role: "region", "aria-label": t("tabs.styles"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "hwped-hint", role: "status", children: t("styles.checkingHost") }) });
1366
1428
  }
1367
1429
  if (capabilities.styles !== "v1") {
1368
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hwped-panel", role: "region", "aria-label": t("tabs.styles"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "hwped-hint", role: "status", children: (0, import_core4.stylesVersionSupported)(capabilities.version) ? t("styles.unavailableHost") : t("styles.unavailable", { version: capabilities.version }) }) });
1430
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hwped-panel", role: "region", "aria-label": t("tabs.styles"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "hwped-hint", role: "status", children: (0, import_core5.stylesVersionSupported)(capabilities.version) ? t("styles.unavailableHost") : t("styles.unavailable", { version: capabilities.version }) }) });
1369
1431
  }
1370
1432
  if (engine.styles === void 0) {
1371
1433
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "hwped-panel", role: "region", "aria-label": t("tabs.styles"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "hwped-hint", role: "status", children: t("styles.unavailableHost") }) });
1372
1434
  }
1373
- const paragraphs = envelope === null ? [] : (0, import_core4.selectedParagraphs)(envelope, state);
1435
+ const paragraphs = envelope === null ? [] : (0, import_core5.selectedParagraphs)(envelope, state);
1374
1436
  const rows = styles?.styles ?? [];
1375
1437
  const row = rows.find((item) => item.id === selectedId) ?? null;
1376
1438
  const paragraphRow = row !== null && row.kind === "paragraph" ? row : null;
@@ -1380,8 +1442,8 @@ function StylesPanel() {
1380
1442
  setSelectedId(item.id);
1381
1443
  if (envelope === null || item.used_by.length === 0) return;
1382
1444
  const refs = item.used_by.flatMap((id) => {
1383
- const segment = (0, import_core4.segmentAtRef)(envelope, { id, section: 0, para: 0 });
1384
- return segment === void 0 ? [] : [(0, import_core4.segmentRef)(segment)];
1445
+ const segment = (0, import_core5.segmentAtRef)(envelope, { id, section: 0, para: 0 });
1446
+ return segment === void 0 ? [] : [(0, import_core5.segmentRef)(segment)];
1385
1447
  });
1386
1448
  if (refs.length === 0) return;
1387
1449
  store.dispatch({ type: "selectMany", selections: refs, primary: refs[0], anchor: refs[0] });
@@ -1392,12 +1454,12 @@ function StylesPanel() {
1392
1454
  const shape = base === void 0 ? void 0 : base.direct ?? base.style;
1393
1455
  if (name === "" || base === void 0 || shape === void 0 || envelope === null) return;
1394
1456
  const face = firstFace(shape);
1395
- const paragraph = (0, import_core4.resolveSelection)(envelope, base).paragraph ?? base;
1457
+ const paragraph = (0, import_core5.resolveSelection)(envelope, base).paragraph ?? base;
1396
1458
  const id = paragraph.id;
1397
1459
  const own = id === void 0 ? void 0 : rows.find((item) => item.kind === "paragraph" && item.used_by.includes(id));
1398
- const targets = (0, import_core4.selectSimilar)(envelope, paragraph, "para").flatMap((ref) => {
1399
- const segment = (0, import_core4.segmentAtRef)(envelope, ref);
1400
- return segment === void 0 ? [] : [(0, import_core4.paragraphAddress)(segment)];
1460
+ const targets = (0, import_core5.selectSimilar)(envelope, paragraph, "para").flatMap((ref) => {
1461
+ const segment = (0, import_core5.segmentAtRef)(envelope, ref);
1462
+ return segment === void 0 ? [] : [(0, import_core5.paragraphAddress)(segment)];
1401
1463
  });
1402
1464
  const align = shape.para === void 0 ? void 0 : writableAlignment(shape.para.alignment);
1403
1465
  store.dispatch({
@@ -1433,7 +1495,7 @@ function StylesPanel() {
1433
1495
  const switchPreset = () => {
1434
1496
  if (styles === null || envelope === null) return;
1435
1497
  const epoch = loadEpoch();
1436
- const styleOps = (0, import_core4.presetStyleOps)(styles, preset, envelope);
1498
+ const styleOps = (0, import_core5.presetStyleOps)(styles, preset, envelope);
1437
1499
  const carriedStyleOp = styleOps.length > 0;
1438
1500
  const batch = [
1439
1501
  ...styleOps,
@@ -1443,10 +1505,10 @@ function StylesPanel() {
1443
1505
  let unstyled = false;
1444
1506
  let changed = false;
1445
1507
  void replaceDocument(async (current) => {
1446
- const styled = await engine.edit(current, batch, (0, import_core4.presetBatchOptions)(batch, { verify: true }));
1447
- const reading = (0, import_core4.readPresetBatch)(styled.report);
1508
+ const styled = await engine.edit(current, batch, (0, import_core5.presetBatchOptions)(batch, { verify: true }));
1509
+ const reading = (0, import_core5.readPresetBatch)(styled.report);
1448
1510
  if (reading.refusal !== null) {
1449
- throw new import_core4.HwpEngineError(reading.refusal.code, reading.refusal.message);
1511
+ throw new import_core5.HwpEngineError(reading.refusal.code, reading.refusal.message);
1450
1512
  }
1451
1513
  unstyled = reading.unstyledTable;
1452
1514
  changed = !reading.unchanged;
@@ -1529,7 +1591,7 @@ function StylesPanel() {
1529
1591
  disabled,
1530
1592
  "aria-label": t("styles.presetLabel"),
1531
1593
  onChange: (e) => setPreset(e.target.value),
1532
- children: import_core4.STYLE_TABLE_PRESETS.map((value) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value, children: t(PRESET_LABEL[value]) }, value))
1594
+ children: import_core5.STYLE_TABLE_PRESETS.map((value) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value, children: t(PRESET_LABEL[value]) }, value))
1533
1595
  }
1534
1596
  ),
1535
1597
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -1550,10 +1612,10 @@ function StylesPanel() {
1550
1612
 
1551
1613
  // src/TableGrid.tsx
1552
1614
  var import_react5 = require("react");
1553
- var import_core6 = require("@hwp-editor/core");
1615
+ var import_core7 = require("@hwp-editor/core");
1554
1616
 
1555
1617
  // src/tables.ts
1556
- var import_core5 = require("@hwp-editor/core");
1618
+ var import_core6 = require("@hwp-editor/core");
1557
1619
  var GFM_DELIMITER_CELL = /^:?-{2,}:?$/;
1558
1620
  function endsWithCellDelimiter(row) {
1559
1621
  if (!row.endsWith("|")) return false;
@@ -1620,7 +1682,7 @@ function findTables(envelope) {
1620
1682
  tables.push({
1621
1683
  tableIndex: tables.length,
1622
1684
  segment,
1623
- rows: parseMarkdownTable((0, import_core5.segmentText)(envelope, segment))
1685
+ rows: parseMarkdownTable((0, import_core6.segmentText)(envelope, segment))
1624
1686
  });
1625
1687
  }
1626
1688
  return tables;
@@ -1690,7 +1752,7 @@ function TableGrid() {
1690
1752
  }
1691
1753
  )
1692
1754
  ] });
1693
- const mismatch = envelope === null ? null : (0, import_core6.tableCountMismatch)(envelope);
1755
+ const mismatch = envelope === null ? null : (0, import_core7.tableCountMismatch)(envelope);
1694
1756
  if (mismatch !== null) {
1695
1757
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "hwped-panel", role: "region", "aria-label": t("table.panelAria"), children: [
1696
1758
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "hwped-hint", children: t("table.indexMismatch", mismatch) }),
@@ -1925,10 +1987,10 @@ function TableGrid() {
1925
1987
 
1926
1988
  // src/FieldsPanel.tsx
1927
1989
  var import_react6 = require("react");
1928
- var import_core8 = require("@hwp-editor/core");
1990
+ var import_core9 = require("@hwp-editor/core");
1929
1991
 
1930
1992
  // src/fields.ts
1931
- var import_core7 = require("@hwp-editor/core");
1993
+ var import_core8 = require("@hwp-editor/core");
1932
1994
  var SLOT_PATTERN = /\{\{\s*([^{}]+?)\s*\}\}/g;
1933
1995
  var CONTROL_KINDS = /* @__PURE__ */ new Set(["field", "image", "bookmark"]);
1934
1996
  function innermostAt(envelope, offset) {
@@ -1956,10 +2018,10 @@ function extractFieldSlots(envelope) {
1956
2018
  const offset = [...envelope.markdown.slice(0, match.index ?? 0)].length;
1957
2019
  const hit = innermostAt(envelope, offset);
1958
2020
  if (hit === void 0 || seen.has(name)) continue;
1959
- const paragraph = (0, import_core7.resolveSelection)(envelope, hit).paragraph;
2021
+ const paragraph = (0, import_core8.resolveSelection)(envelope, hit).paragraph;
1960
2022
  if (paragraph === void 0) continue;
1961
2023
  seen.add(name);
1962
- slots.push({ name, segment: (0, import_core7.segmentRef)(paragraph) });
2024
+ slots.push({ name, segment: (0, import_core8.segmentRef)(paragraph) });
1963
2025
  }
1964
2026
  return slots;
1965
2027
  }
@@ -1993,8 +2055,8 @@ function FieldsPanel() {
1993
2055
  children: [
1994
2056
  legacy && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "hwped-hint", children: t("fields.legacySeparatorHint") }),
1995
2057
  slots.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "hwped-hint", children: t("fields.hint") }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "hwped-fields", children: slots.map((slot) => {
1996
- const segment = envelope === null ? void 0 : (0, import_core8.segmentAtRef)(envelope, slot.segment);
1997
- const snippet = envelope === null || segment === void 0 ? "" : (0, import_core8.segmentText)(envelope, segment).trim().slice(0, 40);
2058
+ const segment = envelope === null ? void 0 : (0, import_core9.segmentAtRef)(envelope, slot.segment);
2059
+ const snippet = envelope === null || segment === void 0 ? "" : (0, import_core9.segmentText)(envelope, segment).trim().slice(0, 40);
1998
2060
  const draft = drafts[slot.name] ?? "";
1999
2061
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { className: "hwped-field-item", children: [
2000
2062
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "hwped-row", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -2063,19 +2125,19 @@ function FieldsPanel() {
2063
2125
 
2064
2126
  // src/TemplatePanel.tsx
2065
2127
  var import_react7 = require("react");
2066
- var import_core12 = require("@hwp-editor/core");
2128
+ var import_core13 = require("@hwp-editor/core");
2067
2129
 
2068
2130
  // src/errors.ts
2069
- var import_core9 = require("@hwp-editor/core");
2131
+ var import_core10 = require("@hwp-editor/core");
2070
2132
  function toEditorError(e) {
2071
2133
  const refusal = engineRefusalKind(e);
2072
- const base = (0, import_core9.isHwpEngineError)(e) && (0, import_core9.isHwpErrorCode)(e.code) ? { code: e.code, message: e.message } : { message: e instanceof Error ? e.message : String(e) };
2134
+ const base = (0, import_core10.isHwpEngineError)(e) && (0, import_core10.isHwpErrorCode)(e.code) ? { code: e.code, message: e.message } : { message: e instanceof Error ? e.message : String(e) };
2073
2135
  return refusal === void 0 ? base : { ...base, refusal };
2074
2136
  }
2075
2137
  function engineRefusalKind(e) {
2076
2138
  if (typeof e !== "object" || e === null) return void 0;
2077
2139
  const refusal = e.refusal;
2078
- return (0, import_core9.isEditRefusalKind)(refusal) ? refusal : void 0;
2140
+ return (0, import_core10.isEditRefusalKind)(refusal) ? refusal : void 0;
2079
2141
  }
2080
2142
  function engineStoppedWithoutDiagnostic(e) {
2081
2143
  return typeof e === "object" && e !== null && e.stopped === true;
@@ -2145,8 +2207,8 @@ function ErrorLine(props) {
2145
2207
  }
2146
2208
 
2147
2209
  // src/template-support.ts
2148
- var import_core10 = require("@hwp-editor/core");
2149
2210
  var import_core11 = require("@hwp-editor/core");
2211
+ var import_core12 = require("@hwp-editor/core");
2150
2212
  var METHOD_LABEL_KEYS = {
2151
2213
  slots: "templates.methodSlots",
2152
2214
  fill: "templates.methodFill",
@@ -2156,7 +2218,7 @@ var METHOD_LABEL_KEYS = {
2156
2218
  create: "templates.methodCreate"
2157
2219
  };
2158
2220
  function templateMethodStatus(engine, capabilities, method) {
2159
- if (!(0, import_core10.templateVersionSupported)(capabilities?.version ?? "")) {
2221
+ if (!(0, import_core11.templateVersionSupported)(capabilities?.version ?? "")) {
2160
2222
  return { supported: false, reason: "version" };
2161
2223
  }
2162
2224
  if (typeof engine[method] !== "function") {
@@ -2168,7 +2230,7 @@ function templateMethodStatus(engine, capabilities, method) {
2168
2230
  return { supported: true, reason: null };
2169
2231
  }
2170
2232
  function templateStatusReason(t, status, method) {
2171
- if (status.reason === "version") return t("templates.versionRequired", { version: import_core11.TEMPLATE_MIN_VERSION });
2233
+ if (status.reason === "version") return t("templates.versionRequired", { version: import_core12.TEMPLATE_MIN_VERSION });
2172
2234
  const label = t(METHOD_LABEL_KEYS[method]);
2173
2235
  if (status.reason === "missing") return t("templates.methodMissing", { method: label });
2174
2236
  if (status.reason === "unadvertised") return t("templates.methodUnadvertised", { method: label });
@@ -2190,7 +2252,7 @@ function initialListRow(fields, supplied = {}) {
2190
2252
  }
2191
2253
  function pageData(page) {
2192
2254
  const mime = page.format === "svg" ? "image/svg+xml" : `image/${page.format}`;
2193
- return `data:${mime};base64,${import_core12.base64.encode(page.data)}`;
2255
+ return `data:${mime};base64,${import_core13.base64.encode(page.data)}`;
2194
2256
  }
2195
2257
  function kindLabel(input, t) {
2196
2258
  if (input.kind === "slot") return t("templates.kindSlot");
@@ -2681,7 +2743,7 @@ function DocumentPanel() {
2681
2743
 
2682
2744
  // src/ImagePanel.tsx
2683
2745
  var import_react10 = require("react");
2684
- var import_core13 = require("@hwp-editor/core");
2746
+ var import_core14 = require("@hwp-editor/core");
2685
2747
  var import_jsx_runtime10 = require("react/jsx-runtime");
2686
2748
  function ImagePanel() {
2687
2749
  const { state, store, envelope, capabilities, editable, queueImage, onError, t } = useHwpEditorContext();
@@ -2719,14 +2781,14 @@ function ImagePanel() {
2719
2781
  if (file === void 0) return;
2720
2782
  setReading(true);
2721
2783
  try {
2722
- if (file.size === 0 || file.size > import_core13.MAX_IMAGE_ASSET_BYTES) throw new Error(t("image.invalidSize"));
2784
+ if (file.size === 0 || file.size > import_core14.MAX_IMAGE_ASSET_BYTES) throw new Error(t("image.invalidSize"));
2723
2785
  let bytes;
2724
2786
  try {
2725
2787
  bytes = await file.arrayBuffer();
2726
2788
  } catch {
2727
2789
  throw new Error(t("image.readError"));
2728
2790
  }
2729
- const data = new Uint8Array(bytes), mediaType = (0, import_core13.imageAssetMediaType)(data);
2791
+ const data = new Uint8Array(bytes), mediaType = (0, import_core14.imageAssetMediaType)(data);
2730
2792
  if (mediaType === null) throw new Error(t("image.invalidType"));
2731
2793
  if (generation.current === version && store.getState().document === source) {
2732
2794
  setChosen({ name: file.name, data, extension: mediaType === "image/png" ? "png" : "jpg" });
@@ -2784,12 +2846,12 @@ function ImagePanel() {
2784
2846
 
2785
2847
  // src/ComposePanel.tsx
2786
2848
  var import_react11 = require("react");
2787
- var import_core16 = require("@hwp-editor/core");
2849
+ var import_core17 = require("@hwp-editor/core");
2788
2850
 
2789
2851
  // src/presets.ts
2790
- var import_core14 = require("@hwp-editor/core");
2791
- var COMPOSE_PRESETS = [...import_core14.DOCUMENT_PROFILE_NAMES];
2792
- var PRESET_PROFILES = import_core14.DOCUMENT_PROFILES;
2852
+ var import_core15 = require("@hwp-editor/core");
2853
+ var COMPOSE_PRESETS = [...import_core15.DOCUMENT_PROFILE_NAMES];
2854
+ var PRESET_PROFILES = import_core15.DOCUMENT_PROFILES;
2793
2855
  function bodyToBlocks(body) {
2794
2856
  const blocks = [];
2795
2857
  for (const chunk of body.split(/\n\s*\n/)) {
@@ -2850,17 +2912,17 @@ function buildDocumentSpec(preset, input) {
2850
2912
  },
2851
2913
  heading: {
2852
2914
  font_family: profile.fontFamily,
2853
- font_size_pt: profile.bodySizePt + import_core14.COMPOSE_STYLE_ROWS.heading.sizeDeltaPt,
2915
+ font_size_pt: profile.bodySizePt + import_core15.COMPOSE_STYLE_ROWS.heading.sizeDeltaPt,
2854
2916
  bold: true,
2855
- spacing_before_pt: import_core14.COMPOSE_STYLE_ROWS.heading.spacingBeforePt,
2856
- spacing_after_pt: import_core14.COMPOSE_STYLE_ROWS.heading.spacingAfterPt
2917
+ spacing_before_pt: import_core15.COMPOSE_STYLE_ROWS.heading.spacingBeforePt,
2918
+ spacing_after_pt: import_core15.COMPOSE_STYLE_ROWS.heading.spacingAfterPt
2857
2919
  },
2858
2920
  title: {
2859
2921
  font_family: profile.fontFamily,
2860
- font_size_pt: profile.bodySizePt + import_core14.COMPOSE_STYLE_ROWS.title.sizeDeltaPt,
2922
+ font_size_pt: profile.bodySizePt + import_core15.COMPOSE_STYLE_ROWS.title.sizeDeltaPt,
2861
2923
  bold: true,
2862
2924
  align: "center",
2863
- spacing_after_pt: import_core14.COMPOSE_STYLE_ROWS.title.spacingAfterPt
2925
+ spacing_after_pt: import_core15.COMPOSE_STYLE_ROWS.title.spacingAfterPt
2864
2926
  }
2865
2927
  },
2866
2928
  sections: [section]
@@ -2869,7 +2931,7 @@ function buildDocumentSpec(preset, input) {
2869
2931
  }
2870
2932
 
2871
2933
  // src/components-support.ts
2872
- var import_core15 = require("@hwp-editor/core");
2934
+ var import_core16 = require("@hwp-editor/core");
2873
2935
  function millimetres(value) {
2874
2936
  return `${Math.round(value * 10) / 10} mm`;
2875
2937
  }
@@ -2925,7 +2987,7 @@ function insertionPreflight(blocks, documentName, t) {
2925
2987
  function insertionOf(error) {
2926
2988
  if (typeof error !== "object" || error === null) return void 0;
2927
2989
  const value = error.insertion;
2928
- return (0, import_core15.isInsertionRefusal)(value) ? value : void 0;
2990
+ return (0, import_core16.isInsertionRefusal)(value) ? value : void 0;
2929
2991
  }
2930
2992
  function overflowingBodyMm(error) {
2931
2993
  const insertion = insertionOf(error);
@@ -2935,13 +2997,15 @@ function isLayoutOverflow(error) {
2935
2997
  return insertionOf(error)?.kind === "layout_overflow";
2936
2998
  }
2937
2999
  function wrapped(error, text, detail) {
2938
- if ((0, import_core15.isHwpEngineError)(error)) {
3000
+ if ((0, import_core16.isHwpEngineError)(error)) {
2939
3001
  const insertion = insertionOf(error);
3002
+ const refusal = engineRefusalKind(error);
2940
3003
  return Object.assign(
2941
- new import_core15.HwpEngineError(error.code, text, {
3004
+ new import_core16.HwpEngineError(error.code, text, {
2942
3005
  ...error.status === void 0 ? {} : { status: error.status },
2943
3006
  cause: error,
2944
3007
  ...insertion === void 0 ? {} : { insertion },
3008
+ ...refusal === void 0 ? {} : { refusal },
2945
3009
  // The marker survives too: a host's `onError` branches on the same
2946
3010
  // structured fields the transport set, so a wrapper that drops one
2947
3011
  // hands it a failure it cannot classify (#91).
@@ -2959,11 +3023,14 @@ function refusalReason(message) {
2959
3023
  }
2960
3024
  function isRefusedInsertion(error) {
2961
3025
  if (insertionOf(error) !== void 0) return true;
2962
- return (0, import_core15.isHwpEngineError)(error) && (error.code === "failed" || error.code === "bad_request");
3026
+ return (0, import_core16.isHwpEngineError)(error) && (error.code === "failed" || error.code === "bad_request");
2963
3027
  }
2964
3028
  function insertionFailure(error, blocks, t) {
2965
3029
  const message = error instanceof Error ? error.message : String(error);
2966
3030
  if (!isRefusedInsertion(error)) return error;
3031
+ if (engineRefusalKind(error) === "output_validation") {
3032
+ return wrapped(error, t("error.outputValidation"), message);
3033
+ }
2967
3034
  const insertion = insertionOf(error);
2968
3035
  if (insertion?.kind === "unknown_style") {
2969
3036
  return wrapped(error, t("components.unknownStyle", { style: insertion.style }), message);
@@ -2987,7 +3054,7 @@ var STEP_KEYS = {
2987
3054
  component_load: "components.step.load"
2988
3055
  };
2989
3056
  function componentRefusalText(error, t) {
2990
- if (!(0, import_core15.isComponentError)(error)) return null;
3057
+ if (!(0, import_core16.isComponentError)(error)) return null;
2991
3058
  return t("components.refused", {
2992
3059
  code: error.code,
2993
3060
  source: error.source,
@@ -3016,7 +3083,7 @@ var PRESET_LABEL_KEYS = {
3016
3083
  };
3017
3084
  function ComposePanel(props) {
3018
3085
  const { engine, t, onError, store, state, loadEpoch, loadSettled, loadInFlight } = useHwpEditorContext();
3019
- const components = props.components ?? import_core16.STARTER_COMPONENTS;
3086
+ const components = props.components ?? import_core17.STARTER_COMPONENTS;
3020
3087
  const [preset, setPreset] = (0, import_react11.useState)("official");
3021
3088
  const [title, setTitle] = (0, import_react11.useState)("");
3022
3089
  const [author, setAuthor] = (0, import_react11.useState)("");
@@ -3138,7 +3205,7 @@ function ComposePanel(props) {
3138
3205
  const source = store.getState().document;
3139
3206
  try {
3140
3207
  const written = title.trim();
3141
- const spec = assembling ? (0, import_core16.documentSpecFromComponents)(
3208
+ const spec = assembling ? (0, import_core17.documentSpecFromComponents)(
3142
3209
  assembly.map((row) => ({ component: row.component, values: row.values })),
3143
3210
  written === "" ? {} : { title: written }
3144
3211
  ) : buildDocumentSpec(preset, { title, author, body });
@@ -3383,18 +3450,18 @@ function ComposePanel(props) {
3383
3450
 
3384
3451
  // src/ComponentsPanel.tsx
3385
3452
  var import_react12 = require("react");
3386
- var import_core17 = require("@hwp-editor/core");
3453
+ var import_core18 = require("@hwp-editor/core");
3387
3454
  var import_jsx_runtime12 = require("react/jsx-runtime");
3388
3455
  function componentsSupportReason(capabilities, t) {
3389
3456
  if (capabilities === null) return "";
3390
3457
  if (capabilities.editOps !== "v1") return t("components.disabledChannel");
3391
- if (!(0, import_core17.editReportSupported)(capabilities.version)) {
3392
- return t("components.disabledVersion", { version: import_core17.EDIT_REPORT_MIN_VERSION });
3458
+ if (!(0, import_core18.editReportSupported)(capabilities.version)) {
3459
+ return t("components.disabledVersion", { version: import_core18.EDIT_REPORT_MIN_VERSION });
3393
3460
  }
3394
3461
  return "";
3395
3462
  }
3396
3463
  function ComponentsPanel({
3397
- components = import_core17.STARTER_COMPONENTS,
3464
+ components = import_core18.STARTER_COMPONENTS,
3398
3465
  disabledReason = "",
3399
3466
  beforeDisabledReason = "",
3400
3467
  refusalFor,
@@ -3618,7 +3685,7 @@ function pendingTargetLabel(op, t, resolvers = {}, envelope = null) {
3618
3685
  }
3619
3686
 
3620
3687
  // src/messages.ts
3621
- var import_core18 = require("@hwp-editor/core");
3688
+ var import_core19 = require("@hwp-editor/core");
3622
3689
  var en = {
3623
3690
  // Toolbar
3624
3691
  "toolbar.toolsAria": "Editing tools",
@@ -3988,13 +4055,14 @@ var en = {
3988
4055
  "error.staleReplacement": "The document changed during template work; reopen it and try again.",
3989
4056
  "error.staleCompose": "The open document changed while the new document was being composed; compose again.",
3990
4057
  "error.stalePreview": "The document changed while this preview was being made; preview again on the open document.",
4058
+ "error.outputValidation": "The engine's output preservation check refused to write the edited file. One known cause is HWPX font metadata (typeInfo) that HWP5 cannot represent. In that case, save as .hwpx to preserve the metadata. If you accept its loss, explicitly run ahwp convert in.hwpx -o in.hwp without --strict, reopen in.hwp, and retry. For other causes, rerun the same edit with the engine's --verify option and inspect the verification report.",
3991
4059
  "error.unmatchedReplace": "The engine found no text to replace in that paragraph, so nothing was applied. Select the text again and retry.",
3992
4060
  "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.",
3993
4061
  "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.",
3994
4062
  "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.",
3995
- "error.stoppedWithoutDiagnostic": import_core18.STOPPED_WITHOUT_DIAGNOSTIC,
4063
+ "error.stoppedWithoutDiagnostic": import_core19.STOPPED_WITHOUT_DIAGNOSTIC,
3996
4064
  // The same rule for a failure with stderr but no Error: line (#140).
3997
- "error.noEngineCause": import_core18.NO_ENGINE_CAUSE,
4065
+ "error.noEngineCause": import_core19.NO_ENGINE_CAUSE,
3998
4066
  "error.badEnvelope": (p) => `The host returned a document read this editor cannot use: ${p.detail}. Have the host emit segment-envelope-v2 data, then reload.`,
3999
4067
  "error.readOnlyReplacement": "The editor became read-only before the template could be applied; switch to an editable document and retry.",
4000
4068
  "error.pendingReplacement": "Pending edits would be lost; apply or clear them before replacing this document.",
@@ -4070,14 +4138,14 @@ var en = {
4070
4138
  "image.insert": "Insert image",
4071
4139
  "image.delete": "Delete image",
4072
4140
  "image.file": "PNG/JPEG image file",
4073
- "image.fileHint": `Choose a PNG or JPEG image up to ${import_core18.MAX_IMAGE_ASSET_MIB} MiB.`,
4141
+ "image.fileHint": `Choose a PNG or JPEG image up to ${import_core19.MAX_IMAGE_ASSET_MIB} MiB.`,
4074
4142
  "image.unsupported": "Image upload needs a host with an upload command. With the agent-hwp engine, use 1.4.0 or newer, the version this editor verifies uploads against. A supplied engine has to advertise image-asset uploads. The desktop app has no upload command, and this editor cannot insert a picture from a local file, so insert the picture in a word processor and reopen the document here.",
4075
4143
  "image.anchor": "Insert after paragraph text",
4076
4144
  "image.deleteAnchor": "Image anchor text to delete",
4077
4145
  "image.width": "Width (mm)",
4078
4146
  "image.height": "Height (mm)",
4079
4147
  "image.reading": "Reading image...",
4080
- "image.invalidSize": `The image is empty or exceeds ${import_core18.MAX_IMAGE_ASSET_MIB} MiB; choose a smaller PNG/JPEG file.`,
4148
+ "image.invalidSize": `The image is empty or exceeds ${import_core19.MAX_IMAGE_ASSET_MIB} MiB; choose a smaller PNG/JPEG file.`,
4081
4149
  "image.invalidType": "The file is not PNG/JPEG image data; choose a supported image.",
4082
4150
  "image.readError": "The image could not be read; select the file again.",
4083
4151
  "error.prefix.image": "Image operation failed",
@@ -4446,6 +4514,7 @@ var ko = {
4446
4514
  "error.staleReplacement": "\uD15C\uD50C\uB9BF \uC791\uC5C5 \uC911 \uBB38\uC11C\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uBB38\uC11C\uB97C \uB2E4\uC2DC \uC5F0 \uB4A4 \uC7AC\uC2DC\uB3C4\uD558\uC138\uC694.",
4447
4515
  "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.",
4448
4516
  "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.",
4517
+ "error.outputValidation": "\uC5D4\uC9C4\uC758 \uCD9C\uB825 \uBCF4\uC874 \uAC80\uC0AC\uC5D0\uC11C \uD3B8\uC9D1 \uACB0\uACFC \uD30C\uC77C \uC4F0\uAE30\uB97C \uAC70\uBD80\uD588\uC2B5\uB2C8\uB2E4. \uC54C\uB824\uC9C4 \uC6D0\uC778 \uC911 \uD558\uB098\uB294 HWP5\uB85C \uD45C\uD604\uD560 \uC218 \uC5C6\uB294 HWPX \uAE00\uAF34 \uBA54\uD0C0\uB370\uC774\uD130(typeInfo)\uC785\uB2C8\uB2E4. \uC774 \uACBD\uC6B0 \uBA54\uD0C0\uB370\uC774\uD130\uB97C \uBCF4\uC874\uD558\uB824\uBA74 .hwpx\uB85C \uC800\uC7A5\uD558\uC138\uC694. \uBA54\uD0C0\uB370\uC774\uD130 \uC190\uC2E4\uC744 \uC218\uC6A9\uD55C\uB2E4\uBA74 ahwp convert in.hwpx -o in.hwp\uB97C --strict \uC5C6\uC774 \uBA85\uC2DC\uC801\uC73C\uB85C \uC2E4\uD589\uD558\uACE0, in.hwp\uB97C \uB2E4\uC2DC \uC5F0 \uB4A4 \uC7AC\uC2DC\uB3C4\uD558\uC138\uC694. \uB2E4\uB978 \uC6D0\uC778\uC774\uBA74 \uAC19\uC740 \uD3B8\uC9D1\uC744 \uC5D4\uC9C4\uC758 --verify \uC635\uC158\uACFC \uD568\uAED8 \uB2E4\uC2DC \uC2E4\uD589\uD558\uACE0 \uAC80\uC99D \uBCF4\uACE0\uC11C\uB97C \uD655\uC778\uD558\uC138\uC694.",
4449
4518
  "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.",
4450
4519
  "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.",
4451
4520
  "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.",
@@ -4527,14 +4596,14 @@ var ko = {
4527
4596
  "image.insert": "\uC774\uBBF8\uC9C0 \uC0BD\uC785",
4528
4597
  "image.delete": "\uC774\uBBF8\uC9C0 \uC0AD\uC81C",
4529
4598
  "image.file": "PNG/JPEG \uC774\uBBF8\uC9C0 \uD30C\uC77C",
4530
- "image.fileHint": `${import_core18.MAX_IMAGE_ASSET_MIB} MiB \uC774\uD558\uC758 PNG \uB610\uB294 JPEG \uC774\uBBF8\uC9C0\uB97C \uC120\uD0DD\uD558\uC138\uC694.`,
4599
+ "image.fileHint": `${import_core19.MAX_IMAGE_ASSET_MIB} MiB \uC774\uD558\uC758 PNG \uB610\uB294 JPEG \uC774\uBBF8\uC9C0\uB97C \uC120\uD0DD\uD558\uC138\uC694.`,
4531
4600
  "image.unsupported": "\uC774\uBBF8\uC9C0 \uC5C5\uB85C\uB4DC\uB294 \uC5C5\uB85C\uB4DC \uBA85\uB839\uC774 \uC788\uB294 \uD638\uC2A4\uD2B8\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. agent-hwp \uC5D4\uC9C4\uC774\uB77C\uBA74 \uC774 \uD3B8\uC9D1\uAE30\uAC00 \uC5C5\uB85C\uB4DC\uB97C \uAC80\uC99D\uD55C 1.4.0 \uC774\uC0C1\uC744 \uC0AC\uC6A9\uD558\uC138\uC694. \uC5D4\uC9C4\uC744 \uC9C1\uC811 \uACF5\uAE09\uD558\uB294 \uD638\uC2A4\uD2B8\uB294 \uC774\uBBF8\uC9C0 \uC790\uC0B0 \uC5C5\uB85C\uB4DC\uB97C \uC9C0\uC6D0\uD55C\uB2E4\uACE0 \uC54C\uB824\uC57C \uD569\uB2C8\uB2E4. \uB370\uC2A4\uD06C\uD1B1 \uC571\uC5D0\uB294 \uC5C5\uB85C\uB4DC \uBA85\uB839\uC774 \uC5C6\uACE0 \uC774 \uD3B8\uC9D1\uAE30\uB294 \uB85C\uCEEC \uD30C\uC77C\uC5D0\uC11C \uADF8\uB9BC\uC744 \uB123\uC744 \uC218 \uC5C6\uC73C\uBBC0\uB85C, \uC6CC\uB4DC\uD504\uB85C\uC138\uC11C\uC5D0\uC11C \uADF8\uB9BC\uC744 \uB123\uC740 \uB4A4 \uC774 \uD3B8\uC9D1\uAE30\uC5D0\uC11C \uBB38\uC11C\uB97C \uB2E4\uC2DC \uC5EC\uC138\uC694.",
4532
4601
  "image.anchor": "\uC774 \uBB38\uB2E8 \uBB38\uC790\uC5F4 \uB4A4\uC5D0 \uC0BD\uC785",
4533
4602
  "image.deleteAnchor": "\uC0AD\uC81C\uD560 \uC774\uBBF8\uC9C0\uC758 \uC704\uCE58 \uBB38\uC790\uC5F4",
4534
4603
  "image.width": "\uB108\uBE44 (mm)",
4535
4604
  "image.height": "\uB192\uC774 (mm)",
4536
4605
  "image.reading": "\uC774\uBBF8\uC9C0 \uC77D\uB294 \uC911...",
4537
- "image.invalidSize": `\uC774\uBBF8\uC9C0\uAC00 \uBE44\uC5B4 \uC788\uAC70\uB098 ${import_core18.MAX_IMAGE_ASSET_MIB} MiB\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4. \uB354 \uC791\uC740 PNG/JPEG \uD30C\uC77C\uC744 \uC120\uD0DD\uD558\uC138\uC694.`,
4606
+ "image.invalidSize": `\uC774\uBBF8\uC9C0\uAC00 \uBE44\uC5B4 \uC788\uAC70\uB098 ${import_core19.MAX_IMAGE_ASSET_MIB} MiB\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4. \uB354 \uC791\uC740 PNG/JPEG \uD30C\uC77C\uC744 \uC120\uD0DD\uD558\uC138\uC694.`,
4538
4607
  "image.invalidType": "PNG/JPEG \uC774\uBBF8\uC9C0 \uB370\uC774\uD130\uAC00 \uC544\uB2D9\uB2C8\uB2E4. \uC9C0\uC6D0\uD558\uB294 \uC774\uBBF8\uC9C0 \uD30C\uC77C\uC744 \uC120\uD0DD\uD558\uC138\uC694.",
4539
4608
  "image.readError": "\uC774\uBBF8\uC9C0\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC744 \uB2E4\uC2DC \uC120\uD0DD\uD558\uC138\uC694.",
4540
4609
  "error.prefix.image": "\uC774\uBBF8\uC9C0 \uC791\uC5C5 \uC2E4\uD328",
@@ -4597,16 +4666,16 @@ function selectionParagraphs(envelope, state) {
4597
4666
  const seen = /* @__PURE__ */ new Set();
4598
4667
  const paragraphs = [];
4599
4668
  for (const ref of refs) {
4600
- const segment = (0, import_core19.segmentAtRef)(envelope, ref);
4669
+ const segment = (0, import_core20.segmentAtRef)(envelope, ref);
4601
4670
  if (segment === void 0) continue;
4602
- for (const paragraph of (0, import_core19.resolveSelection)(envelope, segment).paragraphs) {
4671
+ for (const paragraph of (0, import_core20.resolveSelection)(envelope, segment).paragraphs) {
4603
4672
  const key = paragraph.id ?? `${paragraph.section}:${paragraph.para}:${paragraph.start}`;
4604
4673
  if (seen.has(key)) continue;
4605
4674
  seen.add(key);
4606
4675
  paragraphs.push(paragraph);
4607
4676
  }
4608
4677
  }
4609
- return paragraphs.sort(import_core19.compareSegments);
4678
+ return paragraphs.sort(import_core20.compareSegments);
4610
4679
  }
4611
4680
  function insertAnchor(covered, position) {
4612
4681
  const paragraph = position === "before" ? covered[0] : covered[covered.length - 1];
@@ -4626,7 +4695,8 @@ var REFUSAL_MESSAGE = {
4626
4695
  unmatched_replace: "error.unmatchedReplace",
4627
4696
  run_range_conflict: "error.runRangeConflict",
4628
4697
  range_tag: "error.rangeTagRefused",
4629
- unresolvable_address: "error.unresolvableAddress"
4698
+ unresolvable_address: "error.unresolvableAddress",
4699
+ output_validation: "error.outputValidation"
4630
4700
  };
4631
4701
  var HwpEditor = (0, import_react13.forwardRef)(
4632
4702
  function HwpEditor2(props, ref) {
@@ -4644,7 +4714,7 @@ var HwpEditor = (0, import_react13.forwardRef)(
4644
4714
  className,
4645
4715
  style
4646
4716
  } = props;
4647
- const catalogue = components ?? import_core19.STARTER_COMPONENTS;
4717
+ const catalogue = components ?? import_core20.STARTER_COMPONENTS;
4648
4718
  const cbRef = (0, import_react13.useRef)({ onReady, onError, onChange, onDirtyChange });
4649
4719
  (0, import_react13.useEffect)(() => {
4650
4720
  cbRef.current = { onReady, onError, onChange, onDirtyChange };
@@ -4662,7 +4732,7 @@ var HwpEditor = (0, import_react13.forwardRef)(
4662
4732
  cbRef.current.onDirtyChange?.(dirty2);
4663
4733
  }, []);
4664
4734
  const t = (0, import_react13.useMemo)(() => createT(locale, messages), [locale, messages]);
4665
- const [store] = (0, import_react13.useState)(() => (0, import_core19.createStore)());
4735
+ const [store] = (0, import_react13.useState)(() => (0, import_core20.createStore)());
4666
4736
  const state = (0, import_react13.useSyncExternalStore)(store.subscribe, store.getState, store.getState);
4667
4737
  const [envelope, setEnvelope] = (0, import_react13.useState)(null);
4668
4738
  const [capabilities, setCapabilities] = (0, import_react13.useState)(null);
@@ -4715,9 +4785,9 @@ var HwpEditor = (0, import_react13.forwardRef)(
4715
4785
  const readEnvelope = (0, import_react13.useCallback)(async (target) => {
4716
4786
  const read = await engine.read(target);
4717
4787
  try {
4718
- return (0, import_core19.validateCatEnvelope)(read);
4788
+ return (0, import_core20.validateCatEnvelope)(read);
4719
4789
  } catch (e) {
4720
- throw new import_core19.HwpEngineError(
4790
+ throw new import_core20.HwpEngineError(
4721
4791
  "failed",
4722
4792
  latestT.current("error.badEnvelope", { detail: envelopeProblem(e instanceof Error ? e.message : String(e)) }),
4723
4793
  { cause: e }
@@ -4736,7 +4806,7 @@ var HwpEditor = (0, import_react13.forwardRef)(
4736
4806
  insertedTriedFor.current = envelope;
4737
4807
  const hidden = [];
4738
4808
  const paragraphs = pendingInserted.flatMap((id) => {
4739
- const segment = (0, import_core19.segmentAtRef)(envelope, { id, section: 0, para: 0 });
4809
+ const segment = (0, import_core20.segmentAtRef)(envelope, { id, section: 0, para: 0 });
4740
4810
  if (segment === void 0) hidden.push(id);
4741
4811
  return segment === void 0 || segment.kind !== "para" ? [] : [segment];
4742
4812
  });
@@ -4744,9 +4814,9 @@ var HwpEditor = (0, import_react13.forwardRef)(
4744
4814
  if (paragraphs.length === 0) return;
4745
4815
  store.dispatch({
4746
4816
  type: "selectMany",
4747
- selections: paragraphs.map(import_core19.segmentRef),
4748
- primary: (0, import_core19.segmentRef)(paragraphs[0]),
4749
- anchor: (0, import_core19.segmentRef)(paragraphs[0])
4817
+ selections: paragraphs.map(import_core20.segmentRef),
4818
+ primary: (0, import_core20.segmentRef)(paragraphs[0]),
4819
+ anchor: (0, import_core20.segmentRef)(paragraphs[0])
4750
4820
  });
4751
4821
  setInsertedHidden(hidden.length === 0 ? null : { envelope, selections: store.getState().selections, ids: hidden });
4752
4822
  }, [envelope, pendingInserted, store]);
@@ -4827,22 +4897,22 @@ var HwpEditor = (0, import_react13.forwardRef)(
4827
4897
  const queueImage = (0, import_react13.useCallback)((op, asset) => {
4828
4898
  const snapshot = store.getState();
4829
4899
  if (!editable || envelope === null || snapshot.document === null || snapshot.status === "applying") return;
4830
- if (capabilities?.imageAssets !== "v1") throw new import_core19.HwpEngineError("unavailable", t("image.unsupported"));
4831
- if (op.path !== asset.name) throw new import_core19.HwpEngineError("bad_request", t("image.referenceMismatch"));
4900
+ if (capabilities?.imageAssets !== "v1") throw new import_core20.HwpEngineError("unavailable", t("image.unsupported"));
4901
+ if (op.path !== asset.name) throw new import_core20.HwpEngineError("bad_request", t("image.referenceMismatch"));
4832
4902
  const active = snapshot.pendingOps.flatMap((pending) => {
4833
4903
  const stored = imageAssets.current.get(pending);
4834
4904
  return stored === void 0 ? [] : [stored];
4835
4905
  });
4836
- if (active.length + 1 > import_core19.MAX_IMAGE_ASSETS || active.reduce((bytes, image) => bytes + image.data.length, asset.data.length) > import_core19.MAX_IMAGE_ASSETS_TOTAL_BYTES) {
4837
- throw new import_core19.HwpEngineError("bad_request", t("image.queueLimit", { count: import_core19.MAX_IMAGE_ASSETS, miB: import_core19.MAX_IMAGE_ASSETS_TOTAL_BYTES / (1024 * 1024) }));
4906
+ if (active.length + 1 > import_core20.MAX_IMAGE_ASSETS || active.reduce((bytes, image) => bytes + image.data.length, asset.data.length) > import_core20.MAX_IMAGE_ASSETS_TOTAL_BYTES) {
4907
+ throw new import_core20.HwpEngineError("bad_request", t("image.queueLimit", { count: import_core20.MAX_IMAGE_ASSETS, miB: import_core20.MAX_IMAGE_ASSETS_TOTAL_BYTES / (1024 * 1024) }));
4838
4908
  }
4839
- (0, import_core19.validateImageAssets)([...active, asset]);
4909
+ (0, import_core20.validateImageAssets)([...active, asset]);
4840
4910
  imageAssets.current.set(op, { name: asset.name, data: asset.data.slice() });
4841
4911
  store.dispatch({ type: "queueOp", op });
4842
4912
  }, [editable, envelope, capabilities, store, t]);
4843
4913
  (0, import_react13.useEffect)(() => {
4844
4914
  if (envelope === null || state.selection === null) return;
4845
- const segment = (0, import_core19.segmentAtRef)(envelope, state.selection);
4915
+ const segment = (0, import_core20.segmentAtRef)(envelope, state.selection);
4846
4916
  if (segment === void 0) return;
4847
4917
  const next = hostsTable(envelope, segment) ? "table" : "para";
4848
4918
  setTab((current) => current === "components" || current === "styles" ? current : next);
@@ -4989,17 +5059,17 @@ var HwpEditor = (0, import_react13.forwardRef)(
4989
5059
  noteRefusal(t("error.loadingReplacement"));
4990
5060
  return;
4991
5061
  }
4992
- const segment = snapshot.selection === null ? void 0 : (0, import_core19.segmentAtRef)(envelope, snapshot.selection);
5062
+ const segment = snapshot.selection === null ? void 0 : (0, import_core20.segmentAtRef)(envelope, snapshot.selection);
4993
5063
  const covered = selectionParagraphs(envelope, snapshot);
4994
5064
  const address = segment === void 0 ? null : insertAnchor(covered, position);
4995
5065
  if (address === null) return;
4996
- if (position === "before" && covered[0] !== void 0 && (0, import_core19.isSectionDefinitionParagraph)(covered[0])) return;
5066
+ if (position === "before" && covered[0] !== void 0 && (0, import_core20.isSectionDefinitionParagraph)(covered[0])) return;
4997
5067
  let blocks;
4998
5068
  let styles2;
4999
5069
  try {
5000
- const filled = (0, import_core19.substituteParams)(component, values, component.name);
5070
+ const filled = (0, import_core20.substituteParams)(component, values, component.name);
5001
5071
  blocks = filled.blocks;
5002
- styles2 = (0, import_core19.componentStyleMap)([filled]);
5072
+ styles2 = (0, import_core20.componentStyleMap)([filled]);
5003
5073
  } catch (error) {
5004
5074
  const shown = toError(componentFailure(error, t));
5005
5075
  noteNotice(shown.message, shown);
@@ -5052,7 +5122,7 @@ var HwpEditor = (0, import_react13.forwardRef)(
5052
5122
  }
5053
5123
  let ids;
5054
5124
  try {
5055
- ids = (0, import_core19.insertedParagraphIds)(outcome);
5125
+ ids = (0, import_core20.insertedParagraphIds)(outcome);
5056
5126
  } catch (error) {
5057
5127
  noteNotice(toError(error).message, error);
5058
5128
  return;
@@ -5063,12 +5133,12 @@ var HwpEditor = (0, import_react13.forwardRef)(
5063
5133
  [emitError, engine, envelope, noteRefusal, replaceDocument, store, t]
5064
5134
  );
5065
5135
  const selectedTargets = envelope === null || state.selection === null ? void 0 : (() => {
5066
- const segment = (0, import_core19.segmentAtRef)(envelope, state.selection);
5067
- return segment === void 0 ? void 0 : (0, import_core19.resolveSelection)(envelope, segment);
5136
+ const segment = (0, import_core20.segmentAtRef)(envelope, state.selection);
5137
+ return segment === void 0 ? void 0 : (0, import_core20.resolveSelection)(envelope, segment);
5068
5138
  })();
5069
5139
  const componentAnchor = envelope === null || state.selection === null ? [] : selectionParagraphs(envelope, state);
5070
5140
  const selectedAddress = insertAnchor(componentAnchor, "after");
5071
- const beforeDisabledReason = componentAnchor[0] !== void 0 && (0, import_core19.isSectionDefinitionParagraph)(componentAnchor[0]) ? t("components.positionBeforeLocked") : "";
5141
+ const beforeDisabledReason = componentAnchor[0] !== void 0 && (0, import_core20.isSectionDefinitionParagraph)(componentAnchor[0]) ? t("components.positionBeforeLocked") : "";
5072
5142
  const componentDisabled = componentsSupportReason(capabilities, t) || (state.pendingOps.length > 0 ? t("components.pendingEdits") : "") || (state.selection === null ? t("components.noSelection") : selectedAddress !== null ? "" : selectedTargets?.refusal === "cell" ? t("segment.cellSelectionRefused") : selectedTargets?.refusal === "table" ? t("segment.tableSelectionRefused") : selectedTargets?.refusal === "container" ? t("segment.containerSelectionRefused") : t("components.noAddress"));
5073
5143
  const componentRefusal = (0, import_react13.useCallback)(
5074
5144
  (component) => insertionPreflight(component.blocks, state.document?.name ?? "", t),