oasis-editor 0.0.133 → 0.0.134

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 { E as EMPTY_PROJECTION_CONTEXT, d as domTextMeasurer, g as getProjectedParagraphBlockHeight, e as estimateTableBlockHeight, p as projectParagraphLayout, a as getPageBodyBottom, b as getPageBodyTop, c as getPageContentWidth, F as FOOTNOTE_MARKER_GUTTER_PX, f as getParagraphText, h as getBlockParagraphs, i as getRunFootnoteReference, l as listReferencedEndnotes, j as getFootnoteDisplayMarker, k as createProjectionContext, m as getDocumentSections, n as getRunField, o as projectBlocksLayout, v as visitRun, q as assertNever, r as createSignal, s as createEditorStateFromDocument, t as createInitialEditorState, S as STANDARD_FONT_SIZES_PT, u as fontSizePxToPt, w as probeLocalFontFamilies, x as createEditorParagraphFromRuns, y as isInlineObjectRun, z as createEditorStyledRun, A as getParagraphLength, B as getActiveZone, C as getActiveSectionIndex, D as getParagraphs, G as normalizeSelection, H as positionToParagraphOffset, I as paragraphOffsetToPosition, J as clampPosition, K as findParagraphIndex, L as createCollapsedSelection, M as isSelectionCollapsed, N as parseFontSizePtToPx, O as formatFontSizePt, P as createEditorParagraph, Q as resolveDefaultParagraphStyleId, R as findParagraphTableLocation, T as buildTableCellLayout, U as PT_PER_PX, V as createEditorTableCell, W as createEditorTableRow, X as createEditorTable, Y as PX_PER_POINT, Z as underlineStyleToCssDecorationStyle, _ as resolveImageSrc, $ as listKindForTag, a0 as isParagraphTag, a1 as collectInlineRuns, a2 as parseParagraphStyle, a3 as getRunImage, a4 as InlineShell, a5 as BalloonShell, a6 as DocumentShell, a7 as createMemo, a8 as getCaretRectFromSnapshot, a9 as getParagraphRectFromSnapshot, aa as createComponent, ab as CaretOverlay, ac as Show, ad as createRenderEffect, ae as style, af as setStyleProperty, ag as setAttribute, ah as memo, ai as template, aj as Button, ak as useI18n, al as createEffect, am as TextField, an as Dialog, ao as insert, ap as className, aq as Grid, ar as SelectField, as as StatusText, at as ColorField, au as UNDERLINE_STYLE_OPTIONS, av as Stack, aw as For, ax as ToggleChip, ay as FormField, az as Checkbox, aA as FieldGroup, aB as Tabs, aC as NumberField, aD as parseNumber, aE as createStore, aF as reconcile, aG as RadioGroup, aH as Radio$1, aI as TextAreaField, aJ as Text, aK as SurfaceButton, aL as delegateEvents, aM as onMount, aN as onCleanup, aO as CheckIcon, aP as use, aQ as PluginUiHost, aR as OasisEditorEditor, aS as OasisBrandMark, aT as setPreciseFontPreference, aU as setWelcomeSeen, aV as enablePreciseFontMode, aW as TWIPS_PER_POINT, aX as PX_PER_INCH, aY as TWIPS_PER_INCH, aZ as normalizeHex6, a_ as TABLE_CONDITIONAL_FLAG_ATTRIBUTES, a$ as TABLE_BORDER_EDGE_KEYS, b0 as resolveEffectiveParagraphStyle, b1 as resolveEffectiveTextStyleForParagraph, b2 as EMU_PER_PT, b3 as OOXML_ROTATION_UNITS, b4 as OOXML_PERCENT_DENOMINATOR, b5 as parseHexColorToRgb255, b6 as EMU_PER_PX, b7 as getRunEndnoteReference, b8 as iterateFootnoteReferenceRuns, b9 as iterateEndnoteReferenceRuns, ba as imageContentTypeDefaults, bb as getRunFieldChar, bc as getRunFieldInstruction, bd as createEditorRun, be as JSZip, bf as imageExtensionFromMime, bg as pxToPt$1, bh as resolveFloatingObjectRect, bi as getTextBoxFloatingGeometry, bj as getPresetPathSegments, bk as buildListLabels, bl as DEFAULT_FONT_SIZE_PX, bm as resolveGradientAxis, bn as isDoubleUnderlineStyle, bo as isWavyUnderlineStyle, bp as underlineStyleLineWidthPx, bq as WAVY_UNDERLINE_WAVELENGTH_PX, br as WAVY_UNDERLINE_AMPLITUDE_PX, bs as DOUBLE_UNDERLINE_OFFSET_PX, bt as underlineStyleDashArray, bu as EMPHASIS_GLYPH, bv as rgb255ToHex, bw as getImageFloatingGeometry, bx as textStyleToFontSizePt, by as resolveTextAlignmentBaselineOffset, bz as TEXT_BASELINE_RATIO, bA as resolveOpenTypeFeatureTags, bB as resolveDecorationLineY, bC as DOUBLE_STRIKE_OFFSET_PX, bD as resolveListLabel, bE as getListLabelInset, bF as getAlignedListLabelInset, bG as getParagraphBorderInsets, bH as buildSegmentTable, bI as buildCanvasTableLayout, bJ as resolveCanvasTableWidth, bK as resolveFloatingTableRect, bL as paragraphBetweenBorderMatches, bM as normalizeFamily, bN as ROBOTO_FONT_FILES, bO as loadFontAsset, bP as OFFICE_COMPAT_FONT_FAMILIES, bQ as BinaryReader, bR as buildSfnt, bS as defaultFontDecoderRegistry, bT as SfntFontProgram, bU as collectPdfFontFamilies, bV as outlineFrom, bW as getPageHeaderZoneTop, bX as getPageColumnRects, bY as findFootnoteReference, bZ as resolveImporterForFile, b_ as roundTo, b$ as getDocumentSectionsCanonical, c0 as getDocumentParagraphsCanonical, c1 as getDocumentParagraphs, c2 as getDocumentPageSettings, c3 as getTableCellContentWidthForParagraph, c4 as layoutMetricsEpoch, c5 as bumpLayoutMetricsEpoch, c6 as createCanvasLayoutSnapshotProvider, c7 as on, c8 as debounce, c9 as unwrap, ca as perfTimer, cb as getRunTextBox, cc as createEditorDocument, cd as resolveResizedDimensions, ce as resolveTextBoxRenderHeight, cf as getToolbarStyleState, cg as VERTICAL_HIT_WEIGHT, ch as getCachedCanvasImage, ci as measureParagraphMinContentWidthPx, cj as getEditableBlocksForZone, ck as findParagraphLocation, cl as createSectionBoundaryParagraph, cm as normalizePageSettings, cn as DEFAULT_EDITOR_PAGE_SETTINGS, co as markStart, cp as markEnd, cq as getParagraphEntries, cr as getParagraphById, cs as createEditorFootnote, ct as createFootnoteReferenceRun, cu as renumberFootnotes, cv as preciseFontModeVersion, cw as isPreciseFontModeEnabled, cx as getHeadingLevel, cy as togglePreciseFontMode, cz as nextFontSizePt, cA as previousFontSizePt, cB as fontSizePtToPx, cC as resolveNamedTextStyle, cD as createDefaultToolbarPreset, cE as MenuRegistry, cF as createToolbarRegistry, cG as Editor, cH as resolveCommandRef, cI as commandRefName, cJ as createOasisEditorClient, cK as createEditorZoom, cL as startLongTaskObserver, cM as installGlobalReport, cN as applyStoredPreciseFontPreference, cO as getWelcomeSeen, cP as isLocalFontAccessSupported, cQ as EDITOR_SCROLL_PADDING_PX, cR as Toolbar, cS as OasisEditorLoading, cT as I18nProvider, cU as createTranslator, cV as registerDomStatsSurface, cW as createEditorLogger } from "./index-BOj4-NlJ.js";
4
+ import { E as EMPTY_PROJECTION_CONTEXT, d as domTextMeasurer, g as getProjectedParagraphBlockHeight, e as estimateTableBlockHeight, p as projectParagraphLayout, a as getPageBodyBottom, b as getPageBodyTop, c as getPageContentWidth, F as FOOTNOTE_MARKER_GUTTER_PX, f as getParagraphText, h as getBlockParagraphs, i as getRunFootnoteReference, l as listReferencedEndnotes, j as getFootnoteDisplayMarker, k as createProjectionContext, m as getDocumentSections, n as getRunField, o as projectBlocksLayout, v as visitRun, q as assertNever, r as createSignal, s as createEditorStateFromDocument, t as createInitialEditorState, S as STANDARD_FONT_SIZES_PT, u as fontSizePxToPt, w as probeLocalFontFamilies, x as createEditorParagraphFromRuns, y as isInlineObjectRun, z as createEditorStyledRun, A as getParagraphLength, B as getActiveZone, C as getActiveSectionIndex, D as getParagraphs, G as normalizeSelection, H as positionToParagraphOffset, I as paragraphOffsetToPosition, J as clampPosition, K as findParagraphIndex, L as createCollapsedSelection, M as isSelectionCollapsed, N as parseFontSizePtToPx, O as formatFontSizePt, P as createEditorParagraph, Q as resolveDefaultParagraphStyleId, R as findParagraphTableLocation, T as buildTableCellLayout, U as PT_PER_PX, V as createEditorTableCell, W as createEditorTableRow, X as createEditorTable, Y as PX_PER_POINT, Z as underlineStyleToCssDecorationStyle, _ as resolveImageSrc, $ as listKindForTag, a0 as isParagraphTag, a1 as collectInlineRuns, a2 as parseParagraphStyle, a3 as getRunImage, a4 as InlineShell, a5 as BalloonShell, a6 as DocumentShell, a7 as createMemo, a8 as getCaretRectFromSnapshot, a9 as getParagraphRectFromSnapshot, aa as createComponent, ab as CaretOverlay, ac as Show, ad as createRenderEffect, ae as style, af as setStyleProperty, ag as setAttribute, ah as memo, ai as template, aj as Button, ak as useI18n, al as createEffect, am as TextField, an as Dialog, ao as insert, ap as className, aq as Grid, ar as SelectField, as as StatusText, at as ColorField, au as UNDERLINE_STYLE_OPTIONS, av as Stack, aw as For, ax as ToggleChip, ay as FormField, az as Checkbox, aA as FieldGroup, aB as Tabs, aC as NumberField, aD as parseNumber, aE as createStore, aF as reconcile, aG as RadioGroup, aH as Radio$1, aI as TextAreaField, aJ as Text, aK as SurfaceButton, aL as delegateEvents, aM as onMount, aN as onCleanup, aO as CheckIcon, aP as use, aQ as PluginUiHost, aR as OasisEditorEditor, aS as OasisBrandMark, aT as setPreciseFontPreference, aU as setWelcomeSeen, aV as enablePreciseFontMode, aW as TWIPS_PER_POINT, aX as PX_PER_INCH, aY as TWIPS_PER_INCH, aZ as normalizeHex6, a_ as TABLE_CONDITIONAL_FLAG_ATTRIBUTES, a$ as TABLE_BORDER_EDGE_KEYS, b0 as resolveEffectiveParagraphStyle, b1 as resolveEffectiveTextStyleForParagraph, b2 as EMU_PER_PT, b3 as OOXML_ROTATION_UNITS, b4 as OOXML_PERCENT_DENOMINATOR, b5 as parseHexColorToRgb255, b6 as EMU_PER_PX, b7 as getRunEndnoteReference, b8 as iterateFootnoteReferenceRuns, b9 as iterateEndnoteReferenceRuns, ba as imageContentTypeDefaults, bb as getRunFieldChar, bc as getRunFieldInstruction, bd as createEditorRun, be as JSZip, bf as imageExtensionFromMime, bg as pxToPt$1, bh as resolveFloatingObjectRect, bi as getTextBoxFloatingGeometry, bj as getPresetPathSegments, bk as buildListLabels, bl as DEFAULT_FONT_SIZE_PX, bm as resolveGradientAxis, bn as isDoubleUnderlineStyle, bo as isWavyUnderlineStyle, bp as underlineStyleLineWidthPx, bq as WAVY_UNDERLINE_WAVELENGTH_PX, br as WAVY_UNDERLINE_AMPLITUDE_PX, bs as DOUBLE_UNDERLINE_OFFSET_PX, bt as underlineStyleDashArray, bu as EMPHASIS_GLYPH, bv as rgb255ToHex, bw as getImageFloatingGeometry, bx as textStyleToFontSizePt, by as resolveTextAlignmentBaselineOffset, bz as TEXT_BASELINE_RATIO, bA as resolveOpenTypeFeatureTags, bB as resolveDecorationLineY, bC as DOUBLE_STRIKE_OFFSET_PX, bD as resolveListLabel, bE as getListLabelInset, bF as getAlignedListLabelInset, bG as getParagraphBorderInsets, bH as buildSegmentTable, bI as buildCanvasTableLayout, bJ as resolveCanvasTableWidth, bK as resolveFloatingTableRect, bL as paragraphBetweenBorderMatches, bM as normalizeFamily, bN as ROBOTO_FONT_FILES, bO as loadFontAsset, bP as OFFICE_COMPAT_FONT_FAMILIES, bQ as BinaryReader, bR as buildSfnt, bS as defaultFontDecoderRegistry, bT as SfntFontProgram, bU as collectPdfFontFamilies, bV as outlineFrom, bW as getPageHeaderZoneTop, bX as getPageColumnRects, bY as findFootnoteReference, bZ as resolveImporterForFile, b_ as roundTo, b$ as getDocumentSectionsCanonical, c0 as getDocumentParagraphsCanonical, c1 as getDocumentParagraphs, c2 as PX_PER_CM, c3 as getDocumentPageSettings, c4 as getTableCellContentWidthForParagraph, c5 as layoutMetricsEpoch, c6 as bumpLayoutMetricsEpoch, c7 as createCanvasLayoutSnapshotProvider, c8 as on, c9 as debounce, ca as unwrap, cb as perfTimer, cc as getRunTextBox, cd as createEditorDocument, ce as resolveResizedDimensions, cf as axisSignForDirection, cg as clamp$1, ch as MIN_RESIZE_SIZE_PX, ci as resolveTextBoxRenderHeight, cj as getToolbarStyleState, ck as VERTICAL_HIT_WEIGHT, cl as getCachedCanvasImage, cm as measureParagraphMinContentWidthPx, cn as getEditableBlocksForZone, co as findParagraphLocation, cp as createSectionBoundaryParagraph, cq as normalizePageSettings, cr as DEFAULT_EDITOR_PAGE_SETTINGS, cs as markStart, ct as markEnd, cu as getParagraphEntries, cv as getParagraphById, cw as createEditorFootnote, cx as createFootnoteReferenceRun, cy as renumberFootnotes, cz as preciseFontModeVersion, cA as isPreciseFontModeEnabled, cB as getHeadingLevel, cC as togglePreciseFontMode, cD as nextFontSizePt, cE as previousFontSizePt, cF as fontSizePtToPx, cG as resolveNamedTextStyle, cH as createDefaultToolbarPreset, cI as MenuRegistry, cJ as createToolbarRegistry, cK as Editor, cL as resolveCommandRef, cM as commandRefName, cN as createOasisEditorClient, cO as createEditorZoom, cP as startLongTaskObserver, cQ as installGlobalReport, cR as applyStoredPreciseFontPreference, cS as getWelcomeSeen, cT as isLocalFontAccessSupported, cU as EDITOR_SCROLL_PADDING_PX, cV as Toolbar, cW as OasisEditorLoading, cX as I18nProvider, cY as createTranslator, cZ as registerDomStatsSurface, c_ as createEditorLogger } from "./index-ogn0S9F3.js";
5
5
  function projectHeaderFooterBlocksWithDependencies(blocks, deps, context = {}) {
6
6
  const {
7
7
  pageIndex,
@@ -27476,6 +27476,7 @@ const MERGE_KEYS = {
27476
27476
  link: "link",
27477
27477
  imageAlt: "imageAlt",
27478
27478
  imageCaption: "imageCaption",
27479
+ imageResize: "imageResize",
27479
27480
  layoutWrapPolygon: "layoutWrapPolygon",
27480
27481
  specialIndent: "specialIndent",
27481
27482
  paraBorders: "paraBorders",
@@ -35938,7 +35939,8 @@ function buildEditorViewProps(ctx) {
35938
35939
  hoveredRevision: overlayInput.hoveredRevision,
35939
35940
  focused: overlayInput.focused,
35940
35941
  showCaret: overlayInput.showCaret,
35941
- importProgress: overlayInput.importProgress
35942
+ importProgress: overlayInput.importProgress,
35943
+ imageCropMode: overlayInput.imageCropMode
35942
35944
  };
35943
35945
  const refs = {
35944
35946
  onViewportRef: (element) => {
@@ -35980,6 +35982,7 @@ function buildEditorViewProps(ctx) {
35980
35982
  onImageResizeHandleMouseDown: surface.handleImageResizeHandleMouseDown,
35981
35983
  onTextBoxResizeHandleMouseDown: surface.handleTextBoxResizeHandleMouseDown,
35982
35984
  onImageRotateHandleMouseDown: surface.handleImageRotateHandleMouseDown,
35985
+ onImageCropHandleMouseDown: surface.handleImageCropHandleMouseDown,
35983
35986
  onTextBoxRotateHandleMouseDown: surface.handleTextBoxRotateHandleMouseDown,
35984
35987
  onTableDragHandleMouseDown: surface.tableDrag.handleMouseDown,
35985
35988
  onTableCornerResizeHandleMouseDown: surface.tableCornerResize.handleMouseDown,
@@ -39927,7 +39930,8 @@ async function drawFloatingImagesForParagraph(options) {
39927
39930
  y: pxToPt$1(rect.y),
39928
39931
  width: pxToPt$1(rect.width),
39929
39932
  height: pxToPt$1(rect.height),
39930
- rotation: image.rotation
39933
+ rotation: image.rotation,
39934
+ crop: image.crop
39931
39935
  });
39932
39936
  }
39933
39937
  }
@@ -40030,7 +40034,8 @@ async function drawFragmentText(writer, pageIndex, paragraph, line, fragment, do
40030
40034
  y: pxToPt$1(originY + line.top + line.height - fragment.image.height),
40031
40035
  width: pxToPt$1(fragment.image.width),
40032
40036
  height: pxToPt$1(fragment.image.height),
40033
- rotation: fragment.image.rotation
40037
+ rotation: fragment.image.rotation,
40038
+ crop: fragment.image.crop
40034
40039
  });
40035
40040
  return;
40036
40041
  }
@@ -41278,6 +41283,20 @@ class PdfContentStream {
41278
41283
  page.imageResourceNames.add(options.resourceName);
41279
41284
  const bottom = page.height - options.y - options.height;
41280
41285
  const rotation = Number.isFinite(options.rotation) ? options.rotation ?? 0 : 0;
41286
+ const crop = options.crop;
41287
+ const cl = (crop == null ? void 0 : crop.left) ?? 0;
41288
+ const ct = (crop == null ? void 0 : crop.top) ?? 0;
41289
+ const cr = (crop == null ? void 0 : crop.right) ?? 0;
41290
+ const cb = (crop == null ? void 0 : crop.bottom) ?? 0;
41291
+ if (crop && (cl > 0 || ct > 0 || cr > 0 || cb > 0)) {
41292
+ this.drawCroppedImage(options, bottom, rotation, {
41293
+ left: cl,
41294
+ top: ct,
41295
+ right: cr,
41296
+ bottom: cb
41297
+ });
41298
+ return;
41299
+ }
41281
41300
  if (rotation === 0) {
41282
41301
  page.commands.push(
41283
41302
  [
@@ -41323,6 +41342,63 @@ class PdfContentStream {
41323
41342
  ].join("\n")
41324
41343
  );
41325
41344
  }
41345
+ /**
41346
+ * Draws a cropped image by clipping to the displayed box and scaling the full
41347
+ * image so only the cropped sub-region fills it — mirroring the canvas/DOCX
41348
+ * `a:srcRect` semantics. Works with rotation by mapping box-local space to the
41349
+ * page (rotation about the box centre), clipping in that space, then drawing
41350
+ * the enlarged image behind the clip.
41351
+ */
41352
+ drawCroppedImage(options, bottom, rotation, crop) {
41353
+ const page = this.page;
41354
+ const { width, height } = options;
41355
+ const fw = Math.max(1e-4, 1 - crop.left - crop.right);
41356
+ const fh = Math.max(1e-4, 1 - crop.top - crop.bottom);
41357
+ const fullWidth = width / fw;
41358
+ const fullHeight = height / fh;
41359
+ const originX = -crop.left * fullWidth;
41360
+ const originY = -crop.bottom * fullHeight;
41361
+ const radians = -rotation * Math.PI / 180;
41362
+ const cos = Math.cos(radians);
41363
+ const sin = Math.sin(radians);
41364
+ const cxl = width / 2;
41365
+ const cyl = height / 2;
41366
+ const cxPage = options.x + width / 2;
41367
+ const cyPage = bottom + height / 2;
41368
+ const ra = cos;
41369
+ const rb = sin;
41370
+ const rc = -sin;
41371
+ const rd = cos;
41372
+ const re = cxPage - (ra * cxl + rc * cyl);
41373
+ const rf = cyPage - (rb * cxl + rd * cyl);
41374
+ page.commands.push(
41375
+ [
41376
+ "q",
41377
+ [
41378
+ formatNumber(ra),
41379
+ formatNumber(rb),
41380
+ formatNumber(rc),
41381
+ formatNumber(rd),
41382
+ formatNumber(re),
41383
+ formatNumber(rf),
41384
+ "cm"
41385
+ ].join(" "),
41386
+ // Clip to the displayed box (box-local coordinates).
41387
+ `0 0 ${formatNumber(width)} ${formatNumber(height)} re W n`,
41388
+ [
41389
+ formatNumber(fullWidth),
41390
+ "0",
41391
+ "0",
41392
+ formatNumber(fullHeight),
41393
+ formatNumber(originX),
41394
+ formatNumber(originY),
41395
+ "cm"
41396
+ ].join(" "),
41397
+ `/${options.resourceName} Do`,
41398
+ "Q"
41399
+ ].join("\n")
41400
+ );
41401
+ }
41326
41402
  }
41327
41403
  function parseExtensionLookup(reader, offset, parseSubtable2) {
41328
41404
  reader.seek(offset);
@@ -44233,6 +44309,118 @@ function resizeSelectedImage(state, width, height) {
44233
44309
  height: Math.max(24, Math.round(height))
44234
44310
  }));
44235
44311
  }
44312
+ const MIN_IMAGE_PX = 24;
44313
+ function getSelectedImageSizeCm(state) {
44314
+ const selectedImage = getSelectedImageRun(state);
44315
+ const image = selectedImage && getRunImage(selectedImage.run);
44316
+ if (!image) {
44317
+ return null;
44318
+ }
44319
+ return {
44320
+ width: image.width / PX_PER_CM,
44321
+ height: image.height / PX_PER_CM
44322
+ };
44323
+ }
44324
+ function setSelectedImageWidthCm(state, cm) {
44325
+ const selectedImage = getSelectedImageRun(state);
44326
+ const image = selectedImage && getRunImage(selectedImage.run);
44327
+ if (!image || !Number.isFinite(cm) || cm <= 0) {
44328
+ return state;
44329
+ }
44330
+ const nextWidth = Math.max(MIN_IMAGE_PX, Math.round(cm * PX_PER_CM));
44331
+ const ratio = image.height / image.width;
44332
+ return resizeSelectedImage(state, nextWidth, nextWidth * ratio);
44333
+ }
44334
+ function setSelectedImageHeightCm(state, cm) {
44335
+ const selectedImage = getSelectedImageRun(state);
44336
+ const image = selectedImage && getRunImage(selectedImage.run);
44337
+ if (!image || !Number.isFinite(cm) || cm <= 0) {
44338
+ return state;
44339
+ }
44340
+ const nextHeight = Math.max(MIN_IMAGE_PX, Math.round(cm * PX_PER_CM));
44341
+ const ratio = image.width / image.height;
44342
+ return resizeSelectedImage(state, nextHeight * ratio, nextHeight);
44343
+ }
44344
+ function clampCropSide(value) {
44345
+ if (!value || !Number.isFinite(value) || value < 0) {
44346
+ return 0;
44347
+ }
44348
+ return Math.min(value, 0.99);
44349
+ }
44350
+ function normalizeCrop(crop) {
44351
+ if (!crop) {
44352
+ return void 0;
44353
+ }
44354
+ const left = clampCropSide(crop.left);
44355
+ const top = clampCropSide(crop.top);
44356
+ const right = clampCropSide(crop.right);
44357
+ const bottom = clampCropSide(crop.bottom);
44358
+ const safeRight = Math.min(right, Math.max(0, 0.99 - left));
44359
+ const safeBottom = Math.min(bottom, Math.max(0, 0.99 - top));
44360
+ if (left === 0 && top === 0 && safeRight === 0 && safeBottom === 0) {
44361
+ return void 0;
44362
+ }
44363
+ return { left, top, right: safeRight, bottom: safeBottom };
44364
+ }
44365
+ function setSelectedImageCrop(state, update) {
44366
+ return patchSelectedImage(state, (image) => {
44367
+ const next = { ...image };
44368
+ const crop = normalizeCrop(update.crop);
44369
+ if (crop) {
44370
+ next.crop = crop;
44371
+ } else {
44372
+ delete next.crop;
44373
+ }
44374
+ if (update.width !== void 0) {
44375
+ next.width = Math.max(MIN_IMAGE_PX, Math.round(update.width));
44376
+ }
44377
+ if (update.height !== void 0) {
44378
+ next.height = Math.max(MIN_IMAGE_PX, Math.round(update.height));
44379
+ }
44380
+ return next;
44381
+ });
44382
+ }
44383
+ function computeImageAspectCrop(image, mode) {
44384
+ if (mode === "reset") {
44385
+ return { crop: null };
44386
+ }
44387
+ const target = mode;
44388
+ const { width: W, height: H } = image;
44389
+ if (!Number.isFinite(target) || target <= 0 || W <= 0 || H <= 0) {
44390
+ return { crop: image.crop ?? null };
44391
+ }
44392
+ const c = image.crop ?? {};
44393
+ const l0 = clampCropSide(c.left);
44394
+ const t0 = clampCropSide(c.top);
44395
+ const r0 = clampCropSide(c.right);
44396
+ const b0 = clampCropSide(c.bottom);
44397
+ const currentRatio = W / H;
44398
+ if (currentRatio > target) {
44399
+ const newWidth = H * target;
44400
+ const addFrac = (W - newWidth) / W * Math.max(0, 1 - l0 - r0);
44401
+ return {
44402
+ crop: { ...c, left: l0 + addFrac / 2, right: r0 + addFrac / 2 },
44403
+ width: newWidth
44404
+ };
44405
+ }
44406
+ if (currentRatio < target) {
44407
+ const newHeight = W / target;
44408
+ const addFrac = (H - newHeight) / H * Math.max(0, 1 - t0 - b0);
44409
+ return {
44410
+ crop: { ...c, top: t0 + addFrac / 2, bottom: b0 + addFrac / 2 },
44411
+ height: newHeight
44412
+ };
44413
+ }
44414
+ return { crop: image.crop ?? null };
44415
+ }
44416
+ function applySelectedImageCropAspect(state, mode) {
44417
+ const selectedImage = getSelectedImageRun(state);
44418
+ const image = selectedImage && getRunImage(selectedImage.run);
44419
+ if (!image) {
44420
+ return state;
44421
+ }
44422
+ return setSelectedImageCrop(state, computeImageAspectCrop(image, mode));
44423
+ }
44236
44424
  function normalizeRotation$1(rotation) {
44237
44425
  if (!Number.isFinite(rotation)) {
44238
44426
  return void 0;
@@ -47710,12 +47898,168 @@ function createRotateSession(adapter, deps) {
47710
47898
  };
47711
47899
  return { start, stop };
47712
47900
  }
47901
+ function visibleFraction(near, far) {
47902
+ return Math.max(1e-4, 1 - near - far);
47903
+ }
47904
+ function resolveCroppedImage(geometry, deltaX, deltaY) {
47905
+ const { startWidth: W, startHeight: H } = geometry;
47906
+ const l0 = geometry.startCrop.left ?? 0;
47907
+ const t0 = geometry.startCrop.top ?? 0;
47908
+ const r0 = geometry.startCrop.right ?? 0;
47909
+ const b0 = geometry.startCrop.bottom ?? 0;
47910
+ const kx = visibleFraction(l0, r0) / W;
47911
+ const ky = visibleFraction(t0, b0) / H;
47912
+ const xs = axisSignForDirection(geometry.handleDirection, "x");
47913
+ const ys = axisSignForDirection(geometry.handleDirection, "y");
47914
+ let left = l0;
47915
+ let right = r0;
47916
+ let width = W;
47917
+ if (xs !== 0) {
47918
+ const maxGrow = (xs > 0 ? r0 : l0) / kx;
47919
+ width = clamp$1(W + deltaX * xs, MIN_RESIZE_SIZE_PX, W + maxGrow);
47920
+ const widthDelta = width - W;
47921
+ if (xs > 0) {
47922
+ right = Math.max(0, r0 - widthDelta * kx);
47923
+ } else {
47924
+ left = Math.max(0, l0 - widthDelta * kx);
47925
+ }
47926
+ }
47927
+ let top = t0;
47928
+ let bottom = b0;
47929
+ let height = H;
47930
+ if (ys !== 0) {
47931
+ const maxGrow = (ys > 0 ? b0 : t0) / ky;
47932
+ height = clamp$1(H + deltaY * ys, MIN_RESIZE_SIZE_PX, H + maxGrow);
47933
+ const heightDelta = height - H;
47934
+ if (ys > 0) {
47935
+ bottom = Math.max(0, b0 - heightDelta * ky);
47936
+ } else {
47937
+ top = Math.max(0, t0 - heightDelta * ky);
47938
+ }
47939
+ }
47940
+ return {
47941
+ crop: { left, top, right, bottom },
47942
+ width: Math.round(width),
47943
+ height: Math.round(height)
47944
+ };
47945
+ }
47946
+ function createCropSession(deps) {
47947
+ let active = null;
47948
+ const selectionForObject = (state, paragraphId, paragraphOffset) => {
47949
+ const paragraph = getParagraphs(state).find(
47950
+ (p) => p.id === paragraphId
47951
+ );
47952
+ if (!paragraph) {
47953
+ return null;
47954
+ }
47955
+ return {
47956
+ anchor: paragraphOffsetToPosition(paragraph, paragraphOffset),
47957
+ focus: paragraphOffsetToPosition(paragraph, paragraphOffset + 1)
47958
+ };
47959
+ };
47960
+ const withSelection2 = (state, selection) => ({
47961
+ ...state,
47962
+ selection: {
47963
+ anchor: { ...selection.anchor },
47964
+ focus: { ...selection.focus }
47965
+ }
47966
+ });
47967
+ const handleMouseMove = (event) => {
47968
+ var _a2;
47969
+ const crop = active;
47970
+ if (!crop) {
47971
+ return;
47972
+ }
47973
+ const z = ((_a2 = deps.zoomFactor) == null ? void 0 : _a2.call(deps)) ?? 1;
47974
+ const deltaX = (event.clientX - crop.startClientX) / z;
47975
+ const deltaY = (event.clientY - crop.startClientY) / z;
47976
+ const result = resolveCroppedImage(crop, deltaX, deltaY);
47977
+ const selection = selectionForObject(
47978
+ deps.state,
47979
+ crop.paragraphId,
47980
+ crop.paragraphOffset
47981
+ );
47982
+ if (!selection) {
47983
+ deps.logger.warn("image crop:missing paragraph", crop);
47984
+ return;
47985
+ }
47986
+ deps.applyState(
47987
+ setSelectedImageCrop(withSelection2(deps.state, selection), {
47988
+ crop: result.crop,
47989
+ width: result.width,
47990
+ height: result.height
47991
+ })
47992
+ );
47993
+ };
47994
+ const handleMouseUp = () => {
47995
+ const crop = active;
47996
+ if (crop) {
47997
+ deps.updateHistoryState((current) => ({
47998
+ ...current,
47999
+ undoStack: [...current.undoStack, deps.cloneState(crop.initialState)],
48000
+ redoStack: []
48001
+ }));
48002
+ }
48003
+ stop();
48004
+ deps.focusInput();
48005
+ };
48006
+ const stop = () => {
48007
+ active = null;
48008
+ window.removeEventListener("mousemove", handleMouseMove);
48009
+ window.removeEventListener("mouseup", handleMouseUp);
48010
+ };
48011
+ const start = (paragraphId, paragraphOffset, handleDirection, event, initialState) => {
48012
+ const selection = selectionForObject(
48013
+ initialState,
48014
+ paragraphId,
48015
+ paragraphOffset
48016
+ );
48017
+ if (!selection) {
48018
+ return;
48019
+ }
48020
+ const selected = getSelectedImageRun(
48021
+ withSelection2(initialState, selection)
48022
+ );
48023
+ const image = selected && getRunImage(selected.run);
48024
+ if (!image) {
48025
+ return;
48026
+ }
48027
+ const startCrop = { ...image.crop ?? {} };
48028
+ active = {
48029
+ paragraphId,
48030
+ paragraphOffset,
48031
+ handleDirection,
48032
+ startClientX: event.clientX,
48033
+ startClientY: event.clientY,
48034
+ startWidth: image.width,
48035
+ startHeight: image.height,
48036
+ startCrop,
48037
+ initialState: deps.cloneState(initialState)
48038
+ };
48039
+ window.addEventListener("mousemove", handleMouseMove);
48040
+ window.addEventListener("mouseup", handleMouseUp);
48041
+ };
48042
+ return { start, stop };
48043
+ }
47713
48044
  function createEditorImageOperations(deps) {
47714
48045
  return createEditorImageOperationsImpl(deps);
47715
48046
  }
47716
48047
  function createEditorImageOperationsImpl(deps) {
47717
48048
  const [dragging, setDragging] = createSignal(false);
48049
+ const [cropMode, setCropMode] = createSignal(false);
47718
48050
  const [draggedImageInfo, setDraggedImageInfo] = createSignal(null);
48051
+ createEffect(() => {
48052
+ if (cropMode() && !getSelectedImageInfo(deps.state)) {
48053
+ setCropMode(false);
48054
+ }
48055
+ });
48056
+ const toggleCropMode = () => {
48057
+ if (!getSelectedImageInfo(deps.state)) {
48058
+ setCropMode(false);
48059
+ return;
48060
+ }
48061
+ setCropMode((value) => !value);
48062
+ };
47719
48063
  const [mousePos, setMousePos] = createSignal({ x: 0, y: 0 });
47720
48064
  const [dropTargetPos, setDropTargetPos] = createSignal(
47721
48065
  null
@@ -47817,6 +48161,15 @@ function createEditorImageOperationsImpl(deps) {
47817
48161
  logger: deps.logger
47818
48162
  }
47819
48163
  );
48164
+ const imageCropSession = createCropSession({
48165
+ state: deps.state,
48166
+ applyState: deps.applyState,
48167
+ updateHistoryState: deps.updateHistoryState,
48168
+ cloneState: deps.cloneState,
48169
+ focusInput: deps.focusInput,
48170
+ logger: deps.logger,
48171
+ zoomFactor: deps.zoomFactor
48172
+ });
47820
48173
  const handleImageDragMouseMove = (event) => {
47821
48174
  let dragState = activeImageDrag;
47822
48175
  if (!dragState) {
@@ -47945,8 +48298,22 @@ function createEditorImageOperationsImpl(deps) {
47945
48298
  event.stopPropagation();
47946
48299
  imageRotateSession.start(paragraphId, paragraphOffset, event, deps.state);
47947
48300
  };
48301
+ const handleImageCropHandleMouseDown = (paragraphId, paragraphOffset, direction, event) => {
48302
+ event.preventDefault();
48303
+ event.stopPropagation();
48304
+ imageCropSession.start(
48305
+ paragraphId,
48306
+ paragraphOffset,
48307
+ direction,
48308
+ event,
48309
+ deps.state
48310
+ );
48311
+ };
47948
48312
  return {
47949
48313
  dragging,
48314
+ cropMode,
48315
+ setCropMode,
48316
+ toggleCropMode,
47950
48317
  draggedImageInfo,
47951
48318
  mousePos,
47952
48319
  dropTargetPos,
@@ -47955,9 +48322,11 @@ function createEditorImageOperationsImpl(deps) {
47955
48322
  stopImageDrag,
47956
48323
  stopImageResize: imageResizeSession.stop,
47957
48324
  stopImageRotate: imageRotateSession.stop,
48325
+ stopImageCrop: imageCropSession.stop,
47958
48326
  handleImageMouseDown,
47959
48327
  handleImageResizeHandleMouseDown,
47960
- handleImageRotateHandleMouseDown
48328
+ handleImageRotateHandleMouseDown,
48329
+ handleImageCropHandleMouseDown
47961
48330
  };
47962
48331
  }
47963
48332
  function createEditorTextBoxOperations(deps) {
@@ -51992,6 +52361,22 @@ function setParagraphListStartAt(state, startAt) {
51992
52361
  preserveSelectionByParagraphOffsets(nextParagraphs, normalized)
51993
52362
  );
51994
52363
  }
52364
+ function parseCropAspectPreset(preset) {
52365
+ const value = preset.trim().toLowerCase();
52366
+ if (value === "reset" || value === "fit" || value === "fill") {
52367
+ return "reset";
52368
+ }
52369
+ const parts = value.split(/[:/]/);
52370
+ if (parts.length !== 2) {
52371
+ return null;
52372
+ }
52373
+ const a = Number(parts[0]);
52374
+ const b = Number(parts[1]);
52375
+ if (!Number.isFinite(a) || !Number.isFinite(b) || a <= 0 || b <= 0) {
52376
+ return null;
52377
+ }
52378
+ return a / b;
52379
+ }
51995
52380
  function createEditorCommandsController(deps) {
51996
52381
  return createEditorCommandsControllerImpl(deps);
51997
52382
  }
@@ -52286,6 +52671,36 @@ function createEditorCommandsControllerImpl(deps) {
52286
52671
  }
52287
52672
  deps.openImageCaptionDialog(getSelectedImageCaption(state) ?? "");
52288
52673
  };
52674
+ const applyImageWidthCmCommand = (cm) => {
52675
+ if (!selectedImageRun() || !Number.isFinite(cm) || cm <= 0) {
52676
+ return;
52677
+ }
52678
+ execTransactional(
52679
+ (current) => setSelectedImageWidthCm(current, cm),
52680
+ { mergeKey: MERGE_KEYS.imageResize }
52681
+ );
52682
+ };
52683
+ const applyImageHeightCmCommand = (cm) => {
52684
+ if (!selectedImageRun() || !Number.isFinite(cm) || cm <= 0) {
52685
+ return;
52686
+ }
52687
+ execTransactional(
52688
+ (current) => setSelectedImageHeightCm(current, cm),
52689
+ { mergeKey: MERGE_KEYS.imageResize }
52690
+ );
52691
+ };
52692
+ const applyImageCropAspectCommand = (preset) => {
52693
+ if (!selectedImageRun()) {
52694
+ return;
52695
+ }
52696
+ const mode = parseCropAspectPreset(preset);
52697
+ if (mode === null) {
52698
+ return;
52699
+ }
52700
+ execTransactional(
52701
+ (current) => applySelectedImageCropAspect(current, mode)
52702
+ );
52703
+ };
52289
52704
  return {
52290
52705
  applyBooleanStyleCommand,
52291
52706
  applyValueStyleCommand,
@@ -52314,6 +52729,9 @@ function createEditorCommandsControllerImpl(deps) {
52314
52729
  promptForImageAlt,
52315
52730
  applyImageCaptionCommand,
52316
52731
  promptForImageCaption,
52732
+ applyImageWidthCmCommand,
52733
+ applyImageHeightCmCommand,
52734
+ applyImageCropAspectCommand,
52317
52735
  handleListTab,
52318
52736
  handleListEnter,
52319
52737
  handleListBoundaryBackspace
@@ -53317,6 +53735,53 @@ function buildDocumentAndBrowserCommands({
53317
53735
  isEnabled: image.isSelected(),
53318
53736
  isActive: image.isSelected()
53319
53737
  })
53738
+ ),
53739
+ imageContext: actionCommand(
53740
+ "imageContext",
53741
+ () => {
53742
+ },
53743
+ () => ({
53744
+ isEnabled: image.isSelected(),
53745
+ isActive: image.isSelected(),
53746
+ value: null
53747
+ })
53748
+ ),
53749
+ imageWidthCm: actionCommand(
53750
+ "imageWidthCm",
53751
+ (p) => image.setWidthCm(Number(p)),
53752
+ () => {
53753
+ var _a2;
53754
+ return {
53755
+ isEnabled: image.isSelected(),
53756
+ value: ((_a2 = image.getSizeCm()) == null ? void 0 : _a2.width) ?? null
53757
+ };
53758
+ }
53759
+ ),
53760
+ imageHeightCm: actionCommand(
53761
+ "imageHeightCm",
53762
+ (p) => image.setHeightCm(Number(p)),
53763
+ () => {
53764
+ var _a2;
53765
+ return {
53766
+ isEnabled: image.isSelected(),
53767
+ value: ((_a2 = image.getSizeCm()) == null ? void 0 : _a2.height) ?? null
53768
+ };
53769
+ }
53770
+ ),
53771
+ imageCrop: actionCommand(
53772
+ "imageCrop",
53773
+ () => image.toggleCrop(),
53774
+ () => ({
53775
+ isEnabled: image.isSelected(),
53776
+ isActive: image.isCropActive()
53777
+ })
53778
+ ),
53779
+ imageCropAspect: actionCommand(
53780
+ "imageCropAspect",
53781
+ (p) => image.applyCropAspect(String(p)),
53782
+ () => ({
53783
+ isEnabled: image.isSelected()
53784
+ })
53320
53785
  )
53321
53786
  };
53322
53787
  }
@@ -54134,7 +54599,13 @@ function buildEssentialsImage(options) {
54134
54599
  return {
54135
54600
  promptAlt: () => options.commandsController.promptForImageAlt(),
54136
54601
  promptCaption: () => options.commandsController.promptForImageCaption(),
54137
- isSelected: () => Boolean(options.selectedImageRun())
54602
+ isSelected: () => Boolean(options.selectedImageRun()),
54603
+ getSizeCm: () => getSelectedImageSizeCm(options.state()),
54604
+ setWidthCm: (cm) => options.commandsController.applyImageWidthCmCommand(cm),
54605
+ setHeightCm: (cm) => options.commandsController.applyImageHeightCmCommand(cm),
54606
+ isCropActive: () => options.imageCropMode.isActive(),
54607
+ toggleCrop: () => options.imageCropMode.toggle(),
54608
+ applyCropAspect: (preset) => options.commandsController.applyImageCropAspectCommand(preset)
54138
54609
  };
54139
54610
  }
54140
54611
  function buildEssentialsBrowser() {
@@ -55083,6 +55554,10 @@ function createEditorCommandRuntimeImpl(deps) {
55083
55554
  importInputRef: deps.importInputRef,
55084
55555
  imageInputRef: deps.imageInputRef,
55085
55556
  selectedImageRun: interaction.selectedImageRun,
55557
+ imageCropMode: {
55558
+ isActive: interaction.imageOps.cropMode,
55559
+ toggle: interaction.imageOps.toggleCropMode
55560
+ },
55086
55561
  selectionBoxes: doc.selectionBoxes,
55087
55562
  focusInput: deps.focusInput,
55088
55563
  applyState: deps.applyState,
@@ -55409,7 +55884,8 @@ function OasisEditorApp(props = {}) {
55409
55884
  hoveredRevision: revisionController.hoveredRevision,
55410
55885
  focused,
55411
55886
  showCaret: shouldShowCaret,
55412
- importProgress: docIO.importProgress
55887
+ importProgress: docIO.importProgress,
55888
+ imageCropMode: imageOps.cropMode
55413
55889
  },
55414
55890
  refs: {
55415
55891
  focusController
@@ -55430,6 +55906,7 @@ function OasisEditorApp(props = {}) {
55430
55906
  handleImageResizeHandleMouseDown: imageOps.handleImageResizeHandleMouseDown,
55431
55907
  handleTextBoxResizeHandleMouseDown: textBoxOps.handleTextBoxResizeHandleMouseDown,
55432
55908
  handleImageRotateHandleMouseDown: imageOps.handleImageRotateHandleMouseDown,
55909
+ handleImageCropHandleMouseDown: imageOps.handleImageCropHandleMouseDown,
55433
55910
  handleTextBoxRotateHandleMouseDown: textBoxOps.handleTextBoxRotateHandleMouseDown,
55434
55911
  handleEditorContextMenu
55435
55912
  },
@@ -55458,6 +55935,7 @@ function OasisEditorApp(props = {}) {
55458
55935
  textDrag.stopDrag();
55459
55936
  imageOps.stopImageDrag();
55460
55937
  imageOps.stopImageResize();
55938
+ imageOps.stopImageCrop();
55461
55939
  textBoxOps.stopTextBoxResize();
55462
55940
  stopIconObserver();
55463
55941
  });
@@ -44,6 +44,9 @@ declare function createEditorCommandsControllerImpl(deps: EditorCommandsControll
44
44
  promptForImageAlt: () => void;
45
45
  applyImageCaptionCommand: (caption: string) => void;
46
46
  promptForImageCaption: () => void;
47
+ applyImageWidthCmCommand: (cm: number) => void;
48
+ applyImageHeightCmCommand: (cm: number) => void;
49
+ applyImageCropAspectCommand: (preset: string) => void;
47
50
  handleListTab: (direction: "indent" | "outdent") => boolean;
48
51
  handleListEnter: () => boolean;
49
52
  handleListBoundaryBackspace: (event: KeyboardEvent & {