oasis-editor 0.0.40 → 0.0.42

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.
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { n as normalizeSelection, g as getParagraphs, c as createEditorParagraphFromRuns, a as getParagraphLength, b as createEditorRun, d as getDocumentSections, e as createEditorStyledRun, f as getParagraphText, h as getActiveZone, i as getActiveSectionIndex, p as positionToParagraphOffset, j as paragraphOffsetToPosition, k as clampPosition, l as findParagraphIndex, m as createCollapsedSelection, o as isSelectionCollapsed, q as createEditorParagraph, r as getBlockParagraphs, s as findParagraphTableLocation, t as buildTableCellLayout, u as createEditorTableCell, v as createEditorTableRow, w as createEditorTable, x as underlineStyleToCssDecorationStyle, y as resolveImageSrc, z as createEditorFootnote, A as createFootnoteReferenceRun, B as renumberFootnotes, C as iterateFootnoteReferenceRuns, D as getFootnoteDisplayMarker, E as createSignal, F as createEffect, G as onCleanup, H as buildCanvasLayoutSnapshot, I as on, J as onMount, K as debounce, L as unwrap, M as getDocumentParagraphs, N as getDocumentSectionsCanonical, O as createEditorDocument, P as getPageContentWidth, Q as getDocumentPageSettings, R as getTableCellContentWidthForParagraph, S as resolveResizedDimensions, T as resolveTextBoxRenderHeight, U as resolveEffectiveParagraphStyle, V as resolveEffectiveTextStyleForParagraph, W as iterateEndnoteReferenceRuns, X as JSZip, Y as imageContentTypeDefaults, Z as imageExtensionFromMime, _ as pxToPt$1, $ as resolveFloatingObjectRect, a0 as getTextBoxFloatingGeometry, a1 as getPresetPathSegments, a2 as projectBlocksLayout, a3 as buildListLabels, a4 as textStyleToFontSizePt, a5 as PX_PER_POINT, a6 as DEFAULT_FONT_SIZE_PX, a7 as isDoubleUnderlineStyle, a8 as isWavyUnderlineStyle, a9 as underlineStyleLineWidthPx, aa as underlineStyleDashArray, ab as resolveListLabel, ac as getListLabelInset, ad as getAlignedListLabelInset, ae as getParagraphBorderInsets, af as buildSegmentTable, ag as buildCanvasTableLayout, ah as normalizeFamily, ai as ROBOTO_FONT_FILES, aj as loadFontAsset, ak as OFFICE_COMPAT_FONT_FAMILIES, al as buildSfnt, am as defaultFontDecoderRegistry, an as SfntFontProgram, ao as collectPdfFontFamilies, ap as projectDocumentLayout, aq as getPageHeaderZoneTop, ar as getPageBodyTop, as as getPageColumnRects, at as findFootnoteReference, au as FOOTNOTE_MARKER_GUTTER_PX, av as resolveImporterForFile, aw as createEditorStateFromDocument, ax as getDocumentParagraphsCanonical, ay as getToolbarStyleState, az as STANDARD_FONT_SIZES_PT, aA as fontSizePxToPt, aB as probeLocalFontFamilies, aC as createInitialEditorState, aD as parseFontSizePtToPx, aE as formatFontSizePt, aF as listKindForTag, aG as isParagraphTag, aH as collectInlineRuns, aI as parseParagraphStyle, aJ as getHeadingLevel, aK as preciseFontModeVersion, aL as isPreciseFontModeEnabled, aM as togglePreciseFontMode, aN as nextFontSizePt, aO as previousFontSizePt, aP as fontSizePtToPx, aQ as createDefaultToolbarPreset, aR as MenuRegistry, aS as createToolbarRegistry, aT as Editor, aU as resolveCommandRef, aV as commandRefName, aW as InlineShell, aX as BalloonShell, aY as DocumentShell, aZ as createMemo, a_ as getCaretRectFromSnapshot, a$ as getParagraphRectFromSnapshot, b0 as createComponent, b1 as CaretOverlay, b2 as Show, b3 as createRenderEffect, b4 as style, b5 as setAttribute, b6 as setStyleProperty, b7 as memo, b8 as template, b9 as useI18n, ba as insert, bb as use, bc as addEventListener, bd as Dialog, be as delegateEvents, bf as className, bg as For, bh as UNDERLINE_STYLE_OPTIONS, bi as Tabs, bj as measureParagraphMinContentWidthPx, bk as getEditableBlocksForZone, bl as findParagraphLocation, bm as createSectionBoundaryParagraph, bn as normalizePageSettings, bo as DEFAULT_EDITOR_PAGE_SETTINGS, bp as markStart, bq as markEnd, br as getParagraphEntries, bs as getParagraphById, bt as PluginUiHost, bu as OasisEditorEditor, bv as perfTimer, bw as OasisBrandMark, bx as setPreciseFontPreference, by as setWelcomeSeen, bz as enablePreciseFontMode, bA as createOasisEditorClient, bB as createEditorZoom, bC as startLongTaskObserver, bD as installGlobalReport, bE as applyStoredPreciseFontPreference, bF as getWelcomeSeen, bG as isLocalFontAccessSupported, bH as EDITOR_SCROLL_PADDING_PX, bI as Toolbar, bJ as OasisEditorLoading, bK as I18nProvider, bL as createEditorLogger, bM as createTranslator, bN as getCachedCanvasImage, bO as registerDomStatsSurface } from "./index-XgdC48U9.js";
4
+ import { n as normalizeSelection, g as getParagraphs, c as createEditorParagraphFromRuns, a as getParagraphLength, b as createEditorRun, d as getDocumentSections, e as createEditorStyledRun, f as getParagraphText, h as getActiveZone, i as getActiveSectionIndex, p as positionToParagraphOffset, j as paragraphOffsetToPosition, k as clampPosition, l as findParagraphIndex, m as createCollapsedSelection, o as isSelectionCollapsed, q as createEditorParagraph, r as getBlockParagraphs, s as findParagraphTableLocation, t as buildTableCellLayout, u as createEditorTableCell, v as createEditorTableRow, w as createEditorTable, x as underlineStyleToCssDecorationStyle, y as resolveImageSrc, z as createEditorFootnote, A as createFootnoteReferenceRun, B as renumberFootnotes, C as iterateFootnoteReferenceRuns, D as getFootnoteDisplayMarker, E as createSignal, F as createEffect, G as onCleanup, H as buildCanvasLayoutSnapshot, I as on, J as onMount, K as debounce, L as unwrap, M as getDocumentParagraphs, N as getDocumentSectionsCanonical, O as createEditorDocument, P as getPageContentWidth, Q as getDocumentPageSettings, R as getTableCellContentWidthForParagraph, S as resolveResizedDimensions, T as resolveTextBoxRenderHeight, U as resolveEffectiveParagraphStyle, V as resolveEffectiveTextStyleForParagraph, W as iterateEndnoteReferenceRuns, X as JSZip, Y as imageContentTypeDefaults, Z as imageExtensionFromMime, _ as pxToPt$1, $ as resolveFloatingObjectRect, a0 as getTextBoxFloatingGeometry, a1 as getPresetPathSegments, a2 as projectBlocksLayout, a3 as buildListLabels, a4 as textStyleToFontSizePt, a5 as PX_PER_POINT, a6 as DEFAULT_FONT_SIZE_PX, a7 as isDoubleUnderlineStyle, a8 as isWavyUnderlineStyle, a9 as underlineStyleLineWidthPx, aa as underlineStyleDashArray, ab as resolveListLabel, ac as getListLabelInset, ad as getAlignedListLabelInset, ae as getParagraphBorderInsets, af as buildSegmentTable, ag as buildCanvasTableLayout, ah as normalizeFamily, ai as ROBOTO_FONT_FILES, aj as loadFontAsset, ak as OFFICE_COMPAT_FONT_FAMILIES, al as buildSfnt, am as defaultFontDecoderRegistry, an as SfntFontProgram, ao as collectPdfFontFamilies, ap as projectDocumentLayout, aq as getPageHeaderZoneTop, ar as getPageBodyTop, as as getPageColumnRects, at as findFootnoteReference, au as FOOTNOTE_MARKER_GUTTER_PX, av as resolveImporterForFile, aw as createEditorStateFromDocument, ax as getDocumentParagraphsCanonical, ay as getToolbarStyleState, az as STANDARD_FONT_SIZES_PT, aA as fontSizePxToPt, aB as probeLocalFontFamilies, aC as createInitialEditorState, aD as parseFontSizePtToPx, aE as formatFontSizePt, aF as listKindForTag, aG as isParagraphTag, aH as collectInlineRuns, aI as parseParagraphStyle, aJ as getCachedCanvasImage, aK as getHeadingLevel, aL as preciseFontModeVersion, aM as isPreciseFontModeEnabled, aN as togglePreciseFontMode, aO as nextFontSizePt, aP as previousFontSizePt, aQ as fontSizePtToPx, aR as createDefaultToolbarPreset, aS as MenuRegistry, aT as createToolbarRegistry, aU as Editor, aV as resolveCommandRef, aW as commandRefName, aX as InlineShell, aY as BalloonShell, aZ as DocumentShell, a_ as createMemo, a$ as getCaretRectFromSnapshot, b0 as getParagraphRectFromSnapshot, b1 as createComponent, b2 as CaretOverlay, b3 as Show, b4 as createRenderEffect, b5 as style, b6 as setAttribute, b7 as setStyleProperty, b8 as memo, b9 as template, ba as useI18n, bb as insert, bc as use, bd as addEventListener, be as Dialog, bf as delegateEvents, bg as className, bh as For, bi as UNDERLINE_STYLE_OPTIONS, bj as Tabs, bk as measureParagraphMinContentWidthPx, bl as getEditableBlocksForZone, bm as findParagraphLocation, bn as createSectionBoundaryParagraph, bo as normalizePageSettings, bp as DEFAULT_EDITOR_PAGE_SETTINGS, bq as markStart, br as markEnd, bs as getParagraphEntries, bt as getParagraphById, bu as PluginUiHost, bv as OasisEditorEditor, bw as perfTimer, bx as OasisBrandMark, by as setPreciseFontPreference, bz as setWelcomeSeen, bA as enablePreciseFontMode, bB as createOasisEditorClient, bC as createEditorZoom, bD as startLongTaskObserver, bE as installGlobalReport, bF as applyStoredPreciseFontPreference, bG as getWelcomeSeen, bH as isLocalFontAccessSupported, bI as EDITOR_SCROLL_PADDING_PX, bJ as Toolbar, bK as OasisEditorLoading, bL as I18nProvider, bM as createEditorLogger, bN as createTranslator, bO as registerDomStatsSurface } from "./index-CTh8mekn.js";
5
5
  function getSelectedObjectRun(state, predicate) {
6
6
  const normalized = normalizeSelection(state);
7
7
  if (normalized.isCollapsed || normalized.startIndex !== normalized.endIndex || normalized.endParagraphOffset - normalized.startParagraphOffset !== 1) {
@@ -3840,186 +3840,6 @@ function insertFootnote(state) {
3840
3840
  activeFootnoteId: footnote.id
3841
3841
  };
3842
3842
  }
3843
- const RECTANGLE = [
3844
- { x: 0, y: 0 },
3845
- { x: 1, y: 0 },
3846
- { x: 1, y: 1 },
3847
- { x: 0, y: 1 }
3848
- ];
3849
- const MAX_SIDE = 256;
3850
- const ALPHA_THRESHOLD = 128;
3851
- const OPAQUE_RATIO = 0.985;
3852
- const MAX_POINTS = 40;
3853
- const contourCache = /* @__PURE__ */ new Map();
3854
- function rectangle() {
3855
- return RECTANGLE.map((point) => ({ ...point }));
3856
- }
3857
- const DIRS = [
3858
- [0, -1],
3859
- [1, -1],
3860
- [1, 0],
3861
- [1, 1],
3862
- [0, 1],
3863
- [-1, 1],
3864
- [-1, 0],
3865
- [-1, -1]
3866
- ];
3867
- function buildAlphaMask(img) {
3868
- const naturalW = img.naturalWidth || img.width;
3869
- const naturalH = img.naturalHeight || img.height;
3870
- if (!naturalW || !naturalH) {
3871
- return null;
3872
- }
3873
- const scale = Math.min(1, MAX_SIDE / Math.max(naturalW, naturalH));
3874
- const width = Math.max(1, Math.round(naturalW * scale));
3875
- const height = Math.max(1, Math.round(naturalH * scale));
3876
- const canvas = document.createElement("canvas");
3877
- canvas.width = width;
3878
- canvas.height = height;
3879
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
3880
- if (!ctx) {
3881
- return null;
3882
- }
3883
- ctx.drawImage(img, 0, 0, width, height);
3884
- let pixels;
3885
- try {
3886
- pixels = ctx.getImageData(0, 0, width, height).data;
3887
- } catch {
3888
- return null;
3889
- }
3890
- const mask = new Uint8Array(width * height);
3891
- let foreground = 0;
3892
- for (let i = 0; i < mask.length; i += 1) {
3893
- if (pixels[i * 4 + 3] >= ALPHA_THRESHOLD) {
3894
- mask[i] = 1;
3895
- foreground += 1;
3896
- }
3897
- }
3898
- return { mask, width, height, foreground };
3899
- }
3900
- function traceBoundary(mask, width, height) {
3901
- const at = (x, y) => x >= 0 && x < width && y >= 0 && y < height && mask[y * width + x] === 1;
3902
- let startX = -1;
3903
- let startY = -1;
3904
- for (let y = 0; y < height && startY < 0; y += 1) {
3905
- for (let x = 0; x < width; x += 1) {
3906
- if (mask[y * width + x] === 1) {
3907
- startX = x;
3908
- startY = y;
3909
- break;
3910
- }
3911
- }
3912
- }
3913
- if (startX < 0) {
3914
- return [];
3915
- }
3916
- const contour = [];
3917
- let bx = startX;
3918
- let by = startY;
3919
- let backtrack = 6;
3920
- const maxSteps = width * height * 4;
3921
- for (let step = 0; step < maxSteps; step += 1) {
3922
- let moved = false;
3923
- for (let k = 1; k <= 8; k += 1) {
3924
- const dir = (backtrack + k) % 8;
3925
- const [dx, dy] = DIRS[dir];
3926
- const nx = bx + dx;
3927
- const ny = by + dy;
3928
- if (at(nx, ny)) {
3929
- contour.push([bx, by]);
3930
- backtrack = (dir + 4) % 8;
3931
- bx = nx;
3932
- by = ny;
3933
- moved = true;
3934
- break;
3935
- }
3936
- }
3937
- if (!moved) {
3938
- contour.push([bx, by]);
3939
- break;
3940
- }
3941
- if (bx === startX && by === startY && contour.length > 1) {
3942
- break;
3943
- }
3944
- }
3945
- return contour;
3946
- }
3947
- function perpendicularDistance(point, lineStart, lineEnd) {
3948
- const [px, py] = point;
3949
- const [ax, ay] = lineStart;
3950
- const [bx, by] = lineEnd;
3951
- const dx = bx - ax;
3952
- const dy = by - ay;
3953
- const lengthSq = dx * dx + dy * dy;
3954
- if (lengthSq === 0) {
3955
- return Math.hypot(px - ax, py - ay);
3956
- }
3957
- const cross = Math.abs(dx * (ay - py) - dy * (ax - px));
3958
- return cross / Math.sqrt(lengthSq);
3959
- }
3960
- function douglasPeucker(points, tolerance) {
3961
- if (points.length < 3) {
3962
- return points.slice();
3963
- }
3964
- let maxDist = 0;
3965
- let index = 0;
3966
- const first = points[0];
3967
- const last = points[points.length - 1];
3968
- for (let i = 1; i < points.length - 1; i += 1) {
3969
- const dist = perpendicularDistance(points[i], first, last);
3970
- if (dist > maxDist) {
3971
- maxDist = dist;
3972
- index = i;
3973
- }
3974
- }
3975
- if (maxDist <= tolerance) {
3976
- return [first, last];
3977
- }
3978
- const left = douglasPeucker(points.slice(0, index + 1), tolerance);
3979
- const right = douglasPeucker(points.slice(index), tolerance);
3980
- return [...left.slice(0, -1), ...right];
3981
- }
3982
- function traceAlphaMaskContour(mask, width, height) {
3983
- let foreground = 0;
3984
- for (let i = 0; i < mask.length; i += 1) {
3985
- if (mask[i] === 1) foreground += 1;
3986
- }
3987
- if (width <= 0 || height <= 0 || foreground === 0 || foreground / (width * height) >= OPAQUE_RATIO) {
3988
- return rectangle();
3989
- }
3990
- const boundary = traceBoundary(mask, width, height);
3991
- if (boundary.length < 3) {
3992
- return rectangle();
3993
- }
3994
- const tolerance = Math.max(width, height) * 0.012;
3995
- let simplified = douglasPeucker(boundary, tolerance);
3996
- let extraTolerance = tolerance;
3997
- while (simplified.length > MAX_POINTS) {
3998
- extraTolerance *= 1.6;
3999
- simplified = douglasPeucker(boundary, extraTolerance);
4000
- }
4001
- if (simplified.length < 3) {
4002
- return rectangle();
4003
- }
4004
- return simplified.map(([x, y]) => ({
4005
- x: Math.min(1, Math.max(0, x / width)),
4006
- y: Math.min(1, Math.max(0, y / height))
4007
- }));
4008
- }
4009
- function traceImageAlphaContour(img) {
4010
- const cacheKey = img.src;
4011
- const cached = contourCache.get(cacheKey);
4012
- if (cached) {
4013
- return cached.map((point) => ({ ...point }));
4014
- }
4015
- const built = buildAlphaMask(img);
4016
- if (!built) {
4017
- return rectangle();
4018
- }
4019
- const polygon = traceAlphaMaskContour(built.mask, built.width, built.height);
4020
- contourCache.set(cacheKey, polygon);
4021
- return polygon.map((point) => ({ ...point }));
4022
- }
4023
3843
  function cloneBlock(block) {
4024
3844
  return block.type === "paragraph" ? {
4025
3845
  ...block,
@@ -40872,6 +40692,293 @@ function createEditorContextMenuClipboard(deps) {
40872
40692
  programmaticPaste
40873
40693
  };
40874
40694
  }
40695
+ function createEditorTableContextMenuActions(deps) {
40696
+ const { state, tableOps, applyTableContextCommand } = deps;
40697
+ return {
40698
+ isInsideTable: deps.isInsideTable,
40699
+ canMerge: () => tableOps.canMergeSelectedTable(state()),
40700
+ canSplit: () => tableOps.canSplitSelectedTable(state()),
40701
+ canEditColumn: () => tableOps.canEditSelectedTableColumn(state()),
40702
+ canEditRow: () => tableOps.canEditSelectedTableRow(state()),
40703
+ openProperties: () => deps.openTablePropertiesDialog("table"),
40704
+ openBordersAndShading: () => deps.openTablePropertiesDialog("cell"),
40705
+ merge: () => applyTableContextCommand(
40706
+ (current) => tableOps.mergeSelectedTable(current),
40707
+ "mergeTable"
40708
+ ),
40709
+ split: () => applyTableContextCommand(
40710
+ (current) => tableOps.splitSelectedTable(current),
40711
+ "splitTable"
40712
+ ),
40713
+ insertColumnBefore: () => applyTableContextCommand(
40714
+ (current) => tableOps.insertSelectedTableColumn(current, -1),
40715
+ "insertTableColumn"
40716
+ ),
40717
+ insertColumnAfter: () => applyTableContextCommand(
40718
+ (current) => tableOps.insertSelectedTableColumn(current, 1),
40719
+ "insertTableColumn"
40720
+ ),
40721
+ deleteColumn: () => applyTableContextCommand(
40722
+ (current) => tableOps.deleteSelectedTableColumn(current),
40723
+ "deleteTableColumn"
40724
+ ),
40725
+ insertRowBefore: () => applyTableContextCommand(
40726
+ (current) => tableOps.insertSelectedTableRow(current, -1),
40727
+ "insertTableRow"
40728
+ ),
40729
+ insertRowAfter: () => applyTableContextCommand(
40730
+ (current) => tableOps.insertSelectedTableRow(current, 1),
40731
+ "insertTableRow"
40732
+ ),
40733
+ deleteRow: () => applyTableContextCommand(
40734
+ (current) => tableOps.deleteSelectedTableRow(current),
40735
+ "deleteTableRow"
40736
+ )
40737
+ };
40738
+ }
40739
+ const RECTANGLE = [
40740
+ { x: 0, y: 0 },
40741
+ { x: 1, y: 0 },
40742
+ { x: 1, y: 1 },
40743
+ { x: 0, y: 1 }
40744
+ ];
40745
+ const MAX_SIDE = 256;
40746
+ const ALPHA_THRESHOLD = 128;
40747
+ const OPAQUE_RATIO = 0.985;
40748
+ const MAX_POINTS = 40;
40749
+ const contourCache = /* @__PURE__ */ new Map();
40750
+ function rectangle() {
40751
+ return RECTANGLE.map((point) => ({ ...point }));
40752
+ }
40753
+ const DIRS = [
40754
+ [0, -1],
40755
+ [1, -1],
40756
+ [1, 0],
40757
+ [1, 1],
40758
+ [0, 1],
40759
+ [-1, 1],
40760
+ [-1, 0],
40761
+ [-1, -1]
40762
+ ];
40763
+ function buildAlphaMask(img) {
40764
+ const naturalW = img.naturalWidth || img.width;
40765
+ const naturalH = img.naturalHeight || img.height;
40766
+ if (!naturalW || !naturalH) {
40767
+ return null;
40768
+ }
40769
+ const scale = Math.min(1, MAX_SIDE / Math.max(naturalW, naturalH));
40770
+ const width = Math.max(1, Math.round(naturalW * scale));
40771
+ const height = Math.max(1, Math.round(naturalH * scale));
40772
+ const canvas = document.createElement("canvas");
40773
+ canvas.width = width;
40774
+ canvas.height = height;
40775
+ const ctx = canvas.getContext("2d", { willReadFrequently: true });
40776
+ if (!ctx) {
40777
+ return null;
40778
+ }
40779
+ ctx.drawImage(img, 0, 0, width, height);
40780
+ let pixels;
40781
+ try {
40782
+ pixels = ctx.getImageData(0, 0, width, height).data;
40783
+ } catch {
40784
+ return null;
40785
+ }
40786
+ const mask = new Uint8Array(width * height);
40787
+ let foreground = 0;
40788
+ for (let i = 0; i < mask.length; i += 1) {
40789
+ if (pixels[i * 4 + 3] >= ALPHA_THRESHOLD) {
40790
+ mask[i] = 1;
40791
+ foreground += 1;
40792
+ }
40793
+ }
40794
+ return { mask, width, height, foreground };
40795
+ }
40796
+ function traceBoundary(mask, width, height) {
40797
+ const at = (x, y) => x >= 0 && x < width && y >= 0 && y < height && mask[y * width + x] === 1;
40798
+ let startX = -1;
40799
+ let startY = -1;
40800
+ for (let y = 0; y < height && startY < 0; y += 1) {
40801
+ for (let x = 0; x < width; x += 1) {
40802
+ if (mask[y * width + x] === 1) {
40803
+ startX = x;
40804
+ startY = y;
40805
+ break;
40806
+ }
40807
+ }
40808
+ }
40809
+ if (startX < 0) {
40810
+ return [];
40811
+ }
40812
+ const contour = [];
40813
+ let bx = startX;
40814
+ let by = startY;
40815
+ let backtrack = 6;
40816
+ const maxSteps = width * height * 4;
40817
+ for (let step = 0; step < maxSteps; step += 1) {
40818
+ let moved = false;
40819
+ for (let k = 1; k <= 8; k += 1) {
40820
+ const dir = (backtrack + k) % 8;
40821
+ const [dx, dy] = DIRS[dir];
40822
+ const nx = bx + dx;
40823
+ const ny = by + dy;
40824
+ if (at(nx, ny)) {
40825
+ contour.push([bx, by]);
40826
+ backtrack = (dir + 4) % 8;
40827
+ bx = nx;
40828
+ by = ny;
40829
+ moved = true;
40830
+ break;
40831
+ }
40832
+ }
40833
+ if (!moved) {
40834
+ contour.push([bx, by]);
40835
+ break;
40836
+ }
40837
+ if (bx === startX && by === startY && contour.length > 1) {
40838
+ break;
40839
+ }
40840
+ }
40841
+ return contour;
40842
+ }
40843
+ function perpendicularDistance(point, lineStart, lineEnd) {
40844
+ const [px, py] = point;
40845
+ const [ax, ay] = lineStart;
40846
+ const [bx, by] = lineEnd;
40847
+ const dx = bx - ax;
40848
+ const dy = by - ay;
40849
+ const lengthSq = dx * dx + dy * dy;
40850
+ if (lengthSq === 0) {
40851
+ return Math.hypot(px - ax, py - ay);
40852
+ }
40853
+ const cross = Math.abs(dx * (ay - py) - dy * (ax - px));
40854
+ return cross / Math.sqrt(lengthSq);
40855
+ }
40856
+ function douglasPeucker(points, tolerance) {
40857
+ if (points.length < 3) {
40858
+ return points.slice();
40859
+ }
40860
+ let maxDist = 0;
40861
+ let index = 0;
40862
+ const first = points[0];
40863
+ const last = points[points.length - 1];
40864
+ for (let i = 1; i < points.length - 1; i += 1) {
40865
+ const dist = perpendicularDistance(points[i], first, last);
40866
+ if (dist > maxDist) {
40867
+ maxDist = dist;
40868
+ index = i;
40869
+ }
40870
+ }
40871
+ if (maxDist <= tolerance) {
40872
+ return [first, last];
40873
+ }
40874
+ const left = douglasPeucker(points.slice(0, index + 1), tolerance);
40875
+ const right = douglasPeucker(points.slice(index), tolerance);
40876
+ return [...left.slice(0, -1), ...right];
40877
+ }
40878
+ function traceAlphaMaskContour(mask, width, height) {
40879
+ let foreground = 0;
40880
+ for (let i = 0; i < mask.length; i += 1) {
40881
+ if (mask[i] === 1) foreground += 1;
40882
+ }
40883
+ if (width <= 0 || height <= 0 || foreground === 0 || foreground / (width * height) >= OPAQUE_RATIO) {
40884
+ return rectangle();
40885
+ }
40886
+ const boundary = traceBoundary(mask, width, height);
40887
+ if (boundary.length < 3) {
40888
+ return rectangle();
40889
+ }
40890
+ const tolerance = Math.max(width, height) * 0.012;
40891
+ let simplified = douglasPeucker(boundary, tolerance);
40892
+ let extraTolerance = tolerance;
40893
+ while (simplified.length > MAX_POINTS) {
40894
+ extraTolerance *= 1.6;
40895
+ simplified = douglasPeucker(boundary, extraTolerance);
40896
+ }
40897
+ if (simplified.length < 3) {
40898
+ return rectangle();
40899
+ }
40900
+ return simplified.map(([x, y]) => ({
40901
+ x: Math.min(1, Math.max(0, x / width)),
40902
+ y: Math.min(1, Math.max(0, y / height))
40903
+ }));
40904
+ }
40905
+ function traceImageAlphaContour(img) {
40906
+ const cacheKey = img.src;
40907
+ const cached = contourCache.get(cacheKey);
40908
+ if (cached) {
40909
+ return cached.map((point) => ({ ...point }));
40910
+ }
40911
+ const built = buildAlphaMask(img);
40912
+ if (!built) {
40913
+ return rectangle();
40914
+ }
40915
+ const polygon = traceAlphaMaskContour(built.mask, built.width, built.height);
40916
+ contourCache.set(cacheKey, polygon);
40917
+ return polygon.map((point) => ({ ...point }));
40918
+ }
40919
+ function createEditorLayoutOptionsController(deps) {
40920
+ const { state, resetTransactionGrouping, applyTransactionalState, focusInput } = deps;
40921
+ const layoutOptionsTarget = () => {
40922
+ if (getSelectedImageRun(state())) return "image";
40923
+ if (getSelectedTextBoxRun(state())) return "textBox";
40924
+ return null;
40925
+ };
40926
+ const applyLayoutOptionPatch = (mergeKey, apply) => {
40927
+ const target = layoutOptionsTarget();
40928
+ if (!target) return;
40929
+ resetTransactionGrouping();
40930
+ applyTransactionalState((current) => apply(current, target), { mergeKey });
40931
+ focusInput();
40932
+ };
40933
+ const ensureImageWrapContour = (runId, src) => {
40934
+ const resolved = resolveImageSrc(state().document, src);
40935
+ const applyContour = (img2) => {
40936
+ const polygon = traceImageAlphaContour(img2);
40937
+ applyTransactionalState(
40938
+ (current) => setImageWrapPolygon(current, runId, polygon),
40939
+ { mergeKey: "layoutWrapPolygon" }
40940
+ );
40941
+ };
40942
+ const img = getCachedCanvasImage(resolved, () => {
40943
+ if (img.naturalWidth > 0) applyContour(img);
40944
+ });
40945
+ if (img.complete && img.naturalWidth > 0) {
40946
+ applyContour(img);
40947
+ }
40948
+ };
40949
+ return {
40950
+ target: layoutOptionsTarget,
40951
+ preset: () => {
40952
+ const target = layoutOptionsTarget();
40953
+ if (target === "image") return getSelectedImageWrapPreset(state());
40954
+ if (target === "textBox") return getSelectedTextBoxWrapPreset(state());
40955
+ return null;
40956
+ },
40957
+ fixedPosition: () => {
40958
+ const target = layoutOptionsTarget();
40959
+ if (target === "image") return isSelectedImageFixedPosition(state());
40960
+ if (target === "textBox") return isSelectedTextBoxFixedPosition(state());
40961
+ return false;
40962
+ },
40963
+ setPreset: (preset) => {
40964
+ applyLayoutOptionPatch(
40965
+ "layoutWrapPreset",
40966
+ (current, target) => target === "image" ? setSelectedImageWrapPreset(current, preset) : setSelectedTextBoxWrapPreset(current, preset)
40967
+ );
40968
+ if (preset === "tight" || preset === "through") {
40969
+ const selected = getSelectedImageRun(state());
40970
+ const image = selected == null ? void 0 : selected.run.image;
40971
+ if (image && !image.wrapPolygon) {
40972
+ ensureImageWrapContour(selected.run.id, image.src);
40973
+ }
40974
+ }
40975
+ },
40976
+ setFixedPosition: (fixed) => applyLayoutOptionPatch(
40977
+ "layoutFixedPosition",
40978
+ (current, target) => target === "image" ? setSelectedImageFixedPosition(current, fixed) : setSelectedTextBoxFixedPosition(current, fixed)
40979
+ )
40980
+ };
40981
+ }
40875
40982
  const DEFAULT_TOC_MAX_LEVEL = 3;
40876
40983
  const TOC_INSTRUCTION = ' TOC \\o "1-3" \\h \\z \\u ';
40877
40984
  const PX_TO_PT = 72 / 96;
@@ -47085,62 +47192,12 @@ function OasisEditorApp(props = {}) {
47085
47192
  runtimeClient.emit("selectionChange", snapshot.selection);
47086
47193
  });
47087
47194
  const selectedImageRun = () => getSelectedImageRun(state);
47088
- const selectedTextBoxRun = () => getSelectedTextBoxRun(state);
47089
- const layoutOptionsTarget = () => {
47090
- if (selectedImageRun()) return "image";
47091
- if (selectedTextBoxRun()) return "textBox";
47092
- return null;
47093
- };
47094
- const applyLayoutOptionPatch = (mergeKey, apply) => {
47095
- const target = layoutOptionsTarget();
47096
- if (!target) return;
47097
- resetTransactionGrouping();
47098
- applyTransactionalState((current) => apply(current, target), {
47099
- mergeKey
47100
- });
47101
- focusInput();
47102
- };
47103
- const ensureImageWrapContour = (runId, src) => {
47104
- const resolved = resolveImageSrc(state.document, src);
47105
- const applyContour = (img2) => {
47106
- const polygon = traceImageAlphaContour(img2);
47107
- applyTransactionalState((current) => setImageWrapPolygon(current, runId, polygon), {
47108
- mergeKey: "layoutWrapPolygon"
47109
- });
47110
- };
47111
- const img = getCachedCanvasImage(resolved, () => {
47112
- if (img.naturalWidth > 0) applyContour(img);
47113
- });
47114
- if (img.complete && img.naturalWidth > 0) {
47115
- applyContour(img);
47116
- }
47117
- };
47118
- const layoutOptionsOverlay = {
47119
- target: layoutOptionsTarget,
47120
- preset: () => {
47121
- const target = layoutOptionsTarget();
47122
- if (target === "image") return getSelectedImageWrapPreset(state);
47123
- if (target === "textBox") return getSelectedTextBoxWrapPreset(state);
47124
- return null;
47125
- },
47126
- fixedPosition: () => {
47127
- const target = layoutOptionsTarget();
47128
- if (target === "image") return isSelectedImageFixedPosition(state);
47129
- if (target === "textBox") return isSelectedTextBoxFixedPosition(state);
47130
- return false;
47131
- },
47132
- setPreset: (preset) => {
47133
- applyLayoutOptionPatch("layoutWrapPreset", (current, target) => target === "image" ? setSelectedImageWrapPreset(current, preset) : setSelectedTextBoxWrapPreset(current, preset));
47134
- if (preset === "tight" || preset === "through") {
47135
- const selected = getSelectedImageRun(state);
47136
- const image = selected == null ? void 0 : selected.run.image;
47137
- if (image && !image.wrapPolygon) {
47138
- ensureImageWrapContour(selected.run.id, image.src);
47139
- }
47140
- }
47141
- },
47142
- setFixedPosition: (fixed) => applyLayoutOptionPatch("layoutFixedPosition", (current, target) => target === "image" ? setSelectedImageFixedPosition(current, fixed) : setSelectedTextBoxFixedPosition(current, fixed))
47143
- };
47195
+ const layoutOptionsOverlay = createEditorLayoutOptionsController({
47196
+ state: () => state,
47197
+ resetTransactionGrouping,
47198
+ applyTransactionalState,
47199
+ focusInput
47200
+ });
47144
47201
  const canvasHitResolver = createCanvasSurfaceHitResolver({
47145
47202
  state: () => state,
47146
47203
  surfaceRef: () => surfaceRef() ?? null,
@@ -47501,23 +47558,13 @@ function OasisEditorApp(props = {}) {
47501
47558
  promptForLink: commandsController.promptForLink,
47502
47559
  openFontDialog,
47503
47560
  openParagraphDialog,
47504
- table: {
47561
+ table: createEditorTableContextMenuActions({
47562
+ state: () => state,
47563
+ tableOps,
47505
47564
  isInsideTable: tablePropertiesDialogBridge.isInsideTable,
47506
- canMerge: () => tableOps.canMergeSelectedTable(state),
47507
- canSplit: () => tableOps.canSplitSelectedTable(state),
47508
- canEditColumn: () => tableOps.canEditSelectedTableColumn(state),
47509
- canEditRow: () => tableOps.canEditSelectedTableRow(state),
47510
- openProperties: () => openTablePropertiesDialog("table"),
47511
- openBordersAndShading: () => openTablePropertiesDialog("cell"),
47512
- merge: () => applyTableContextCommand((current) => tableOps.mergeSelectedTable(current), "mergeTable"),
47513
- split: () => applyTableContextCommand((current) => tableOps.splitSelectedTable(current), "splitTable"),
47514
- insertColumnBefore: () => applyTableContextCommand((current) => tableOps.insertSelectedTableColumn(current, -1), "insertTableColumn"),
47515
- insertColumnAfter: () => applyTableContextCommand((current) => tableOps.insertSelectedTableColumn(current, 1), "insertTableColumn"),
47516
- deleteColumn: () => applyTableContextCommand((current) => tableOps.deleteSelectedTableColumn(current), "deleteTableColumn"),
47517
- insertRowBefore: () => applyTableContextCommand((current) => tableOps.insertSelectedTableRow(current, -1), "insertTableRow"),
47518
- insertRowAfter: () => applyTableContextCommand((current) => tableOps.insertSelectedTableRow(current, 1), "insertTableRow"),
47519
- deleteRow: () => applyTableContextCommand((current) => tableOps.deleteSelectedTableRow(current), "deleteTableRow")
47520
- }
47565
+ openTablePropertiesDialog,
47566
+ applyTableContextCommand
47567
+ })
47521
47568
  });
47522
47569
  const buildContextMenuItems = contextMenuClipboard.buildContextMenuItems;
47523
47570
  const handleEditorContextMenu = contextMenuClipboard.handleEditorContextMenu;