@haklex/rich-renderer-image 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"ImageEditContext.d.ts","sourceRoot":"","sources":["../src/ImageEditContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAsBvC,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB,+BA4DA"}
1
+ {"version":3,"file":"ImageEditContext.d.ts","sourceRoot":"","sources":["../src/ImageEditContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAExE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAwBvC,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAkEA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ImageEditRenderer.d.ts","sourceRoot":"","sources":["../src/ImageEditRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAqCxE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,+BAQ1D"}
1
+ {"version":3,"file":"ImageEditRenderer.d.ts","sourceRoot":"","sources":["../src/ImageEditRenderer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAuCxE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,kBAAkB,+BAQ1D"}
@@ -1,3 +1,3 @@
1
- export declare function ImageSizeControl(): import("react").JSX.Element;
1
+ export { ImageSizeControl } from './ImageSizeControl';
2
2
  export declare function ImageLayoutControl(): import("react").JSX.Element;
3
3
  //# sourceMappingURL=ImageLayoutControls.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImageLayoutControls.d.ts","sourceRoot":"","sources":["../src/ImageLayoutControls.tsx"],"names":[],"mappings":"AAyDA,wBAAgB,gBAAgB,gCA6F/B;AAED,wBAAgB,kBAAkB,gCAmCjC"}
1
+ {"version":3,"file":"ImageLayoutControls.d.ts","sourceRoot":"","sources":["../src/ImageLayoutControls.tsx"],"names":[],"mappings":"AAgBA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAiBtD,wBAAgB,kBAAkB,gCAmCjC"}
@@ -1,3 +1,4 @@
1
+ import { imageDisplayCssVars, imageDisplayDataAttr, resolveImageDisplaySize } from "@haklex/rich-editor/nodes";
1
2
  import { vars } from "@haklex/rich-editor/static";
2
3
  import { useMemo, useRef, useState } from "react";
3
4
  import { decodeThumbHash } from "@haklex/rich-editor/renderers";
@@ -61,12 +62,17 @@ var resizeHandleLeft = "_1n94osf10";
61
62
  var resizeHandleRight = "_1n94osf11";
62
63
  var resizeHandleVisible = "_1n94osf12";
63
64
  var controlPanel = "_1n94osf13";
64
- var sizeSliderWrap = "_1n94osf14";
65
- var sizeSlider = "_1n94osf15";
66
- var sizeSliderTick = "_1n94osf16";
67
- var sizeValue = "_1n94osf17";
68
- var sizeOption = "_1n94osf18";
69
- var editToolbarButtonActive = "_1n94osf19";
65
+ var sizePanel = "_1n94osf14";
66
+ var sizePanelRow = "_1n94osf15";
67
+ var sizeReadout = "_1n94osf16";
68
+ var sizeSliderWrap = "_1n94osf17";
69
+ var sizeSlider = "_1n94osf18";
70
+ var sizeSliderTick = "_1n94osf19";
71
+ var sizeValueField = "_1n94osf1a";
72
+ var sizePxInput = "_1n94osf1b";
73
+ var sizeEquiv = "_1n94osf1c";
74
+ var sizeOption = "_1n94osf1d";
75
+ var editToolbarButtonActive = "_1n94osf1e";
70
76
  //#endregion
71
77
  //#region src/ImageRenderer.tsx
72
78
  var frameStateSemanticClass = {
@@ -79,19 +85,26 @@ function getCaptionText(altText, caption) {
79
85
  if (!altText) return void 0;
80
86
  if (altText.startsWith("!") || altText.startsWith("¡")) return altText.slice(1);
81
87
  }
82
- function ImageRenderer({ src, altText, width, height, caption: caption$1, thumbhash, accent, displayWidth, layout, onActivate, ref }) {
88
+ function ImageRenderer({ src, altText, width, height, caption: caption$1, thumbhash, accent, displayWidth, fixedWidth, fixedHeight, layout, onActivate, ref }) {
83
89
  const [state, setState] = useState("loading");
84
90
  const imgRef = useRef(null);
85
91
  const captionText = useMemo(() => getCaptionText(altText, caption$1), [altText, caption$1]);
86
92
  const placeholderUrl = useMemo(() => thumbhash ? decodeThumbHash(thumbhash) : void 0, [thumbhash]);
87
93
  if (!src) return null;
88
94
  const interactive = Boolean(onActivate);
95
+ const displaySize = resolveImageDisplaySize({
96
+ displayWidth,
97
+ fixedWidth,
98
+ fixedHeight
99
+ });
100
+ const fillsFigure = displaySize.mode === "percent" || displaySize.mode === "fixed-width";
89
101
  const frameStyle = {
90
102
  backgroundColor: state !== "loaded" && !placeholderUrl ? accent || vars.color.bgTertiary : "transparent",
91
103
  backgroundImage: placeholderUrl && state !== "loaded" ? `url(${placeholderUrl})` : void 0,
92
104
  backgroundSize: "cover",
93
- width: displayWidth !== void 0 ? "100%" : width ? Math.min(width, 1200) : void 0,
105
+ width: fillsFigure ? "100%" : width ? Math.min(width, 1200) : void 0,
94
106
  maxWidth: "100%",
107
+ maxHeight: displaySize.mode === "fixed-height" ? `${displaySize.px}px` : void 0,
95
108
  ...width && height ? { aspectRatio: `${width} / ${height}` } : {}
96
109
  };
97
110
  const activate = () => {
@@ -102,9 +115,10 @@ function ImageRenderer({ src, altText, width, height, caption: caption$1, thumbh
102
115
  e.preventDefault();
103
116
  activate();
104
117
  };
105
- const figureStyle = displayWidth ? { "--rich-image-display-width": `${displayWidth}%` } : void 0;
118
+ const figureStyle = imageDisplayCssVars(displaySize);
106
119
  return /* @__PURE__ */ jsxs("figure", {
107
120
  className: `${root} ${semanticClassNames.root}`,
121
+ "data-display": imageDisplayDataAttr(displaySize),
108
122
  "data-layout": layout,
109
123
  ref,
110
124
  style: figureStyle,
@@ -125,11 +139,16 @@ function ImageRenderer({ src, altText, width, height, caption: caption$1, thumbh
125
139
  loading: "lazy",
126
140
  ref: imgRef,
127
141
  src,
128
- style: width && height ? {
142
+ width,
143
+ style: displaySize.mode === "fixed-height" ? {
144
+ width: "auto",
145
+ height: "auto",
146
+ maxWidth: "100%",
147
+ maxHeight: "100%"
148
+ } : width && height ? {
129
149
  height: "100%",
130
150
  objectFit: "cover"
131
151
  } : void 0,
132
- width,
133
152
  onError: () => setState("error"),
134
153
  onLoad: () => setState("loaded")
135
154
  }),
@@ -146,4 +165,4 @@ function ImageRenderer({ src, altText, width, height, caption: caption$1, thumbh
146
165
  });
147
166
  }
148
167
  //#endregion
149
- export { root as A, panelHint as C, resizeHandleLeft as D, resizeHandle as E, sizeSliderWrap as F, sizeValue as I, sizeOption as M, sizeSlider as N, resizeHandleRight as O, sizeSliderTick as P, loader as S, replaceUploadArea as T, frameEditMode as _, editFieldIcon as a, imageState as b, editPlaceholder as c, editToolbarButtonActive as d, editToolbarButtonDanger as f, frame as g, errorBadge as h, editField as i, semanticClassNames as j, resizeHandleVisible as k, editToolbar as l, editTrigger as m, caption as n, editInput as o, editToolbarVisible as p, controlPanel as r, editPanel as s, ImageRenderer as t, editToolbarButton as u, image as v, replacePreview as w, imageVisible as x, imageDragging as y };
168
+ export { root as A, sizeSliderWrap as B, panelHint as C, resizeHandleLeft as D, resizeHandle as E, sizePanelRow as F, sizePxInput as I, sizeReadout as L, sizeEquiv as M, sizeOption as N, resizeHandleRight as O, sizePanel as P, sizeSlider as R, loader as S, replaceUploadArea as T, sizeValueField as V, frameEditMode as _, editFieldIcon as a, imageState as b, editPlaceholder as c, editToolbarButtonActive as d, editToolbarButtonDanger as f, frame as g, errorBadge as h, editField as i, semanticClassNames as j, resizeHandleVisible as k, editToolbar as l, editTrigger as m, caption as n, editInput as o, editToolbarVisible as p, controlPanel as r, editPanel as s, ImageRenderer as t, editToolbarButton as u, image as v, replacePreview as w, imageVisible as x, imageDragging as y, sizeSliderTick as z };
@@ -4,5 +4,5 @@ export interface ImageRendererInteractiveProps extends ImageRendererProps {
4
4
  onActivate?: (target: HTMLElement) => void;
5
5
  ref?: Ref<HTMLElement>;
6
6
  }
7
- export declare function ImageRenderer({ src, altText, width, height, caption, thumbhash, accent, displayWidth, layout, onActivate, ref, }: ImageRendererInteractiveProps): import("react").JSX.Element | null;
7
+ export declare function ImageRenderer({ src, altText, width, height, caption, thumbhash, accent, displayWidth, fixedWidth, fixedHeight, layout, onActivate, ref, }: ImageRendererInteractiveProps): import("react").JSX.Element | null;
8
8
  //# sourceMappingURL=ImageRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ImageRenderer.d.ts","sourceRoot":"","sources":["../src/ImageRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEzF,OAAO,KAAK,EAAgC,GAAG,EAAE,MAAM,OAAO,CAAC;AAsB/D,MAAM,WAAW,6BAA8B,SAAQ,kBAAkB;IACvE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,OAAO,EACP,KAAK,EACL,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,YAAY,EACZ,MAAM,EACN,UAAU,EACV,GAAG,GACJ,EAAE,6BAA6B,sCAuF/B"}
1
+ {"version":3,"file":"ImageRenderer.d.ts","sourceRoot":"","sources":["../src/ImageRenderer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEzF,OAAO,KAAK,EAAgC,GAAG,EAAE,MAAM,OAAO,CAAC;AAsB/D,MAAM,WAAW,6BAA8B,SAAQ,kBAAkB;IACvE,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC3C,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;CACxB;AAED,wBAAgB,aAAa,CAAC,EAC5B,GAAG,EACH,OAAO,EACP,KAAK,EACL,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAM,EACN,YAAY,EACZ,UAAU,EACV,WAAW,EACX,MAAM,EACN,UAAU,EACV,GAAG,GACJ,EAAE,6BAA6B,sCA+F/B"}
@@ -0,0 +1,2 @@
1
+ export declare function ImageSizeControl(): import("react").JSX.Element;
2
+ //# sourceMappingURL=ImageSizeControl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageSizeControl.d.ts","sourceRoot":"","sources":["../src/ImageSizeControl.tsx"],"names":[],"mappings":"AAwDA,wBAAgB,gBAAgB,gCA8M/B"}
package/dist/atoms.d.ts CHANGED
@@ -26,6 +26,12 @@ export declare const accentAtom: import('jotai').PrimitiveAtom<string | undefine
26
26
  export declare const displayWidthAtom: import('jotai').PrimitiveAtom<number | undefined> & {
27
27
  init: number | undefined;
28
28
  };
29
+ export declare const fixedWidthAtom: import('jotai').PrimitiveAtom<number | undefined> & {
30
+ init: number | undefined;
31
+ };
32
+ export declare const fixedHeightAtom: import('jotai').PrimitiveAtom<number | undefined> & {
33
+ init: number | undefined;
34
+ };
29
35
  export declare const layoutAtom: import('jotai').PrimitiveAtom<ImageLayout | undefined> & {
30
36
  init: ImageLayout | undefined;
31
37
  };
@@ -1 +1 @@
1
- {"version":3,"file":"atoms.d.ts","sourceRoot":"","sources":["../src/atoms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC5D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,CAAC;AAY3C,eAAO,MAAM,OAAO;;CAAW,CAAC;AAChC,eAAO,MAAM,WAAW;;CAAW,CAAC;AACpC,eAAO,MAAM,SAAS;;CAA6B,CAAC;AACpD,eAAO,MAAM,UAAU;;CAA6B,CAAC;AACrD,eAAO,MAAM,WAAW;;CAA6B,CAAC;AACtD,eAAO,MAAM,aAAa;;CAA6B,CAAC;AACxD,eAAO,MAAM,UAAU;;CAA6B,CAAC;AACrD,eAAO,MAAM,gBAAgB;;CAA6B,CAAC;AAC3D,eAAO,MAAM,UAAU;;CAAkC,CAAC;AAG1D,eAAO,MAAM,aAAa;;CAAkC,CAAC;AAC7D,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,eAAe;;CAAc,CAAC;AAC3C,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,cAAc;;CAAc,CAAC;AAC1C,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,kBAAkB,+BAQ9B,CAAC;AAEF,eAAO,MAAM,sBAAsB;;CAAc,CAAC;AAGlD,eAAO,MAAM,WAAW;;CAAW,CAAC;AACpC,eAAO,MAAM,eAAe;;CAAW,CAAC;AACxC,eAAO,MAAM,eAAe;;CAAW,CAAC;AAGxC,eAAO,MAAM,eAAe;;CAA8B,CAAC;AAC3D,eAAO,MAAM,cAAc;;CAAW,CAAC;AACvC,eAAO,MAAM,kBAAkB;;CAA4B,CAAC;AAC5D,eAAO,MAAM,eAAe;WACnB,MAAM;YACL,MAAM;;;eADP,MAAM;gBACL,MAAM;;CACD,CAAC;AAChB,eAAO,MAAM,kBAAkB;;CAAc,CAAC;AAC9C,eAAO,MAAM,gBAAgB;;CAA4B,CAAC;AAG1D,eAAO,MAAM,cAAc;;CAEzB,CAAC;AACH,eAAO,MAAM,gBAAgB;;CAE3B,CAAC;AAGH,eAAO,MAAM,kBAAkB,0CAG7B,CAAC;AAEH,eAAO,MAAM,eAAe,0CAAoE,CAAC;AAEjG,eAAO,MAAM,cAAc,qCAiBzB,CAAC"}
1
+ {"version":3,"file":"atoms.d.ts","sourceRoot":"","sources":["../src/atoms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAC5D,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,CAAC;AAY3C,eAAO,MAAM,OAAO;;CAAW,CAAC;AAChC,eAAO,MAAM,WAAW;;CAAW,CAAC;AACpC,eAAO,MAAM,SAAS;;CAA6B,CAAC;AACpD,eAAO,MAAM,UAAU;;CAA6B,CAAC;AACrD,eAAO,MAAM,WAAW;;CAA6B,CAAC;AACtD,eAAO,MAAM,aAAa;;CAA6B,CAAC;AACxD,eAAO,MAAM,UAAU;;CAA6B,CAAC;AACrD,eAAO,MAAM,gBAAgB;;CAA6B,CAAC;AAC3D,eAAO,MAAM,cAAc;;CAA6B,CAAC;AACzD,eAAO,MAAM,eAAe;;CAA6B,CAAC;AAC1D,eAAO,MAAM,UAAU;;CAAkC,CAAC;AAG1D,eAAO,MAAM,aAAa;;CAAkC,CAAC;AAC7D,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,eAAe;;CAAc,CAAC;AAC3C,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,cAAc;;CAAc,CAAC;AAC1C,eAAO,MAAM,YAAY;;CAAc,CAAC;AACxC,eAAO,MAAM,kBAAkB,+BAQ9B,CAAC;AAEF,eAAO,MAAM,sBAAsB;;CAAc,CAAC;AAGlD,eAAO,MAAM,WAAW;;CAAW,CAAC;AACpC,eAAO,MAAM,eAAe;;CAAW,CAAC;AACxC,eAAO,MAAM,eAAe;;CAAW,CAAC;AAGxC,eAAO,MAAM,eAAe;;CAA8B,CAAC;AAC3D,eAAO,MAAM,cAAc;;CAAW,CAAC;AACvC,eAAO,MAAM,kBAAkB;;CAA4B,CAAC;AAC5D,eAAO,MAAM,eAAe;WACnB,MAAM;YACL,MAAM;;;eADP,MAAM;gBACL,MAAM;;CACD,CAAC;AAChB,eAAO,MAAM,kBAAkB;;CAAc,CAAC;AAC9C,eAAO,MAAM,gBAAgB;;CAA4B,CAAC;AAG1D,eAAO,MAAM,cAAc;;CAEzB,CAAC;AACH,eAAO,MAAM,gBAAgB;;CAE3B,CAAC;AAGH,eAAO,MAAM,kBAAkB,0CAG7B,CAAC;AAEH,eAAO,MAAM,eAAe,0CAAoE,CAAC;AAEjG,eAAO,MAAM,cAAc,qCAqBzB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { ImageDisplayConvertContext, ImageDisplaySize } from '@haklex/rich-editor/nodes';
2
+ export declare function measureImageContainerWidth(trigger: HTMLElement): number | undefined;
3
+ export declare function readImageDisplayContext(trigger: HTMLElement | null, intrinsicWidth?: number, intrinsicHeight?: number): ImageDisplayConvertContext | undefined;
4
+ export declare function applyImageSizePreview(trigger: HTMLElement, layout: string | undefined, next: ImageDisplaySize): void;
5
+ //# sourceMappingURL=image-metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-metrics.d.ts","sourceRoot":"","sources":["../src/image-metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,0BAA0B,EAE/B,KAAK,gBAAgB,EACtB,MAAM,2BAA2B,CAAC;AAInC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAUnF;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,WAAW,GAAG,IAAI,EAC3B,cAAc,CAAC,EAAE,MAAM,EACvB,eAAe,CAAC,EAAE,MAAM,GACvB,0BAA0B,GAAG,SAAS,CAaxC;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,gBAAgB,QA+BvB"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { A as root, D as resizeHandleLeft, E as resizeHandle, F as sizeSliderWrap, I as sizeValue, M as sizeOption, N as sizeSlider, O as resizeHandleRight, P as sizeSliderTick, T as replaceUploadArea, _ as frameEditMode, a as editFieldIcon, b as imageState, c as editPlaceholder, d as editToolbarButtonActive, f as editToolbarButtonDanger, g as frame, i as editField, j as semanticClassNames, k as resizeHandleVisible, l as editToolbar, m as editTrigger, o as editInput, p as editToolbarVisible, r as controlPanel, s as editPanel, t as ImageRenderer, u as editToolbarButton, v as image, x as imageVisible, y as imageDragging } from "./ImageRenderer-BUWdit_Y.js";
1
+ import { A as root, B as sizeSliderWrap, D as resizeHandleLeft, E as resizeHandle, F as sizePanelRow, I as sizePxInput, L as sizeReadout, M as sizeEquiv, N as sizeOption, O as resizeHandleRight, P as sizePanel, R as sizeSlider, T as replaceUploadArea, V as sizeValueField, _ as frameEditMode, a as editFieldIcon, b as imageState, c as editPlaceholder, d as editToolbarButtonActive, f as editToolbarButtonDanger, g as frame, i as editField, j as semanticClassNames, k as resizeHandleVisible, l as editToolbar, m as editTrigger, o as editInput, p as editToolbarVisible, r as controlPanel, s as editPanel, t as ImageRenderer, u as editToolbarButton, v as image, x as imageVisible, y as imageDragging, z as sizeSliderTick } from "./ImageRenderer-B3TgOJEU.js";
2
+ import { $createImageNode, $isImageNode, computeImageDisplayPixels, convertImageDisplaySize, imageDisplayCssVars, imageDisplayDataAttr, imageDisplaySizeAfterResize, resolveImageDisplaySize, sanitizeImageDisplayWidth, sanitizeImageFixedPx } from "@haklex/rich-editor/nodes";
2
3
  import { useRendererMode } from "@haklex/rich-editor/static";
3
4
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
4
5
  import { Provider, atom, createStore, useAtom, useAtomValue, useSetAtom, useStore } from "jotai";
@@ -8,7 +9,6 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
9
  import { ActionBar, ActionButton, Popover, PopoverPanel, PopoverTrigger, SegmentedControl } from "@haklex/rich-editor-ui";
9
10
  import { $getNearestNodeFromDOMNode, $getNodeByKey, COMMAND_PRIORITY_HIGH, DRAGOVER_COMMAND, DRAGSTART_COMMAND, DROP_COMMAND, REDO_COMMAND, UNDO_COMMAND } from "lexical";
10
11
  import { AlignCenterVertical, AlignEndVertical, AlignStartVertical, ArrowLeftToLine, ArrowRightToLine, Captions, CopyPlus, Download, ExternalLink, ImageIcon, Loader2, Replace, Scaling, Trash2, Type, Upload } from "lucide-react";
11
- import { $createImageNode, $isImageNode, sanitizeImageDisplayWidth } from "@haklex/rich-editor/nodes";
12
12
  import { useImageUpload } from "@haklex/rich-editor/plugins";
13
13
  //#region src/atoms.ts
14
14
  function getCaptionText(altText, caption) {
@@ -24,6 +24,8 @@ var captionAtom = atom();
24
24
  var thumbhashAtom = atom();
25
25
  var accentAtom = atom();
26
26
  var displayWidthAtom = atom();
27
+ var fixedWidthAtom = atom();
28
+ var fixedHeightAtom = atom();
27
29
  var layoutAtom = atom();
28
30
  var loadStateAtom = atom("loading");
29
31
  var hoveringAtom = atom(false);
@@ -57,12 +59,16 @@ var frameStyleAtom = atom((get) => {
57
59
  const width = get(widthAtom);
58
60
  const height = get(heightAtom);
59
61
  const displayWidth = get(displayWidthAtom);
62
+ const fixedWidth = get(fixedWidthAtom);
63
+ const fixedHeight = get(fixedHeightAtom);
64
+ const fillsFigure = displayWidth !== void 0 || fixedWidth !== void 0;
60
65
  return {
61
66
  backgroundColor: loadState !== "loaded" && !placeholderUrl ? accent || "#f5f5f5" : "transparent",
62
67
  backgroundImage: placeholderUrl && loadState !== "loaded" ? `url(${placeholderUrl})` : void 0,
63
68
  backgroundSize: "cover",
64
- width: displayWidth !== void 0 ? "100%" : width ? Math.min(width, 1200) : void 0,
69
+ width: fillsFigure ? "100%" : width ? Math.min(width, 1200) : void 0,
65
70
  maxWidth: "100%",
71
+ maxHeight: fixedHeight !== void 0 ? `${fixedHeight}px` : void 0,
66
72
  ...width && height ? { aspectRatio: `${width} / ${height}` } : {}
67
73
  };
68
74
  });
@@ -81,6 +87,8 @@ function ImageEditProvider({ props, children }) {
81
87
  s.set(thumbhashAtom, props.thumbhash);
82
88
  s.set(accentAtom, props.accent);
83
89
  s.set(displayWidthAtom, props.displayWidth);
90
+ s.set(fixedWidthAtom, props.fixedWidth);
91
+ s.set(fixedHeightAtom, props.fixedHeight);
84
92
  s.set(layoutAtom, props.layout);
85
93
  s.set(editSrcAtom, props.src);
86
94
  s.set(editAltTextAtom, props.altText);
@@ -100,6 +108,8 @@ function ImageEditProvider({ props, children }) {
100
108
  store.set(thumbhashAtom, props.thumbhash);
101
109
  store.set(accentAtom, props.accent);
102
110
  store.set(displayWidthAtom, props.displayWidth);
111
+ store.set(fixedWidthAtom, props.fixedWidth);
112
+ store.set(fixedHeightAtom, props.fixedHeight);
103
113
  store.set(layoutAtom, props.layout);
104
114
  }, [
105
115
  store,
@@ -111,6 +121,8 @@ function ImageEditProvider({ props, children }) {
111
121
  props.thumbhash,
112
122
  props.accent,
113
123
  props.displayWidth,
124
+ props.fixedWidth,
125
+ props.fixedHeight,
114
126
  props.layout
115
127
  ]);
116
128
  useEffect(() => {
@@ -226,14 +238,16 @@ function useImageActions() {
226
238
  thumbhash: node.getThumbhash(),
227
239
  accent: node.getAccent(),
228
240
  displayWidth: node.getDisplayWidth(),
241
+ fixedWidth: node.getFixedWidth(),
242
+ fixedHeight: node.getFixedHeight(),
229
243
  layout: node.getLayout()
230
244
  });
231
245
  node.insertAfter(copy);
232
246
  });
233
247
  }, [withImageNode]);
234
- const handleSetDisplayWidth = useCallback((width) => {
248
+ const handleSetDisplaySize = useCallback((size) => {
235
249
  withImageNode((node) => {
236
- node.setDisplayWidth(width);
250
+ node.applyDisplaySize(size);
237
251
  });
238
252
  }, [withImageNode]);
239
253
  const handleSetLayout = useCallback((layout) => {
@@ -316,7 +330,7 @@ function useImageActions() {
316
330
  handleDuplicate,
317
331
  handleDownload,
318
332
  handleDelete,
319
- handleSetDisplayWidth,
333
+ handleSetDisplaySize,
320
334
  handleSetLayout
321
335
  };
322
336
  }
@@ -404,7 +418,56 @@ function getEditorHistoryShortcut(event, options = {}) {
404
418
  return null;
405
419
  }
406
420
  //#endregion
407
- //#region src/ImageLayoutControls.tsx
421
+ //#region src/image-metrics.ts
422
+ function measureImageContainerWidth(trigger) {
423
+ const container = trigger.closest(".rich-image-wrapper")?.parentElement ?? trigger.parentElement;
424
+ if (!container) return void 0;
425
+ const style = getComputedStyle(container);
426
+ const width = container.clientWidth - Number.parseFloat(style.paddingLeft) - Number.parseFloat(style.paddingRight);
427
+ return width > 0 ? width : void 0;
428
+ }
429
+ function readImageDisplayContext(trigger, intrinsicWidth, intrinsicHeight) {
430
+ if (!trigger) return void 0;
431
+ const containerWidth = measureImageContainerWidth(trigger);
432
+ if (!containerWidth) return void 0;
433
+ const rect = trigger.querySelector(`.${semanticClassNames.frame}`)?.getBoundingClientRect();
434
+ return {
435
+ containerWidth,
436
+ intrinsicWidth,
437
+ intrinsicHeight,
438
+ renderedWidth: rect?.width,
439
+ renderedHeight: rect?.height
440
+ };
441
+ }
442
+ function applyImageSizePreview(trigger, layout, next) {
443
+ const figure = trigger.querySelector("figure");
444
+ const isFloat = layout === "float-left" || layout === "float-right";
445
+ const blockWrapper = trigger.closest(".rich-image-wrapper");
446
+ figure?.style.removeProperty("--rich-image-display-width");
447
+ figure?.style.removeProperty("--rich-image-display-height");
448
+ const vars = imageDisplayCssVars(next);
449
+ if (figure && vars) for (const [key, value] of Object.entries(vars)) figure.style.setProperty(key, value);
450
+ if (!isFloat || !blockWrapper) return;
451
+ if (next.mode === "percent") {
452
+ blockWrapper.style.width = `${next.value}%`;
453
+ blockWrapper.style.maxWidth = "";
454
+ return;
455
+ }
456
+ if (next.mode === "fixed-width") {
457
+ blockWrapper.style.width = `${next.px}px`;
458
+ blockWrapper.style.maxWidth = "100%";
459
+ return;
460
+ }
461
+ if (next.mode === "fixed-height") {
462
+ blockWrapper.style.width = "auto";
463
+ blockWrapper.style.maxWidth = "100%";
464
+ return;
465
+ }
466
+ blockWrapper.style.width = "";
467
+ blockWrapper.style.maxWidth = "";
468
+ }
469
+ //#endregion
470
+ //#region src/ImageSizeControl.tsx
408
471
  var SIZE_PRESETS = [
409
472
  25,
410
473
  50,
@@ -414,6 +477,23 @@ var SIZE_PRESETS = [
414
477
  var SIZE_MIN = 10;
415
478
  var SIZE_MAX = 100;
416
479
  var SNAP_THRESHOLD = 3;
480
+ var MODE_BUTTONS = [
481
+ [
482
+ "percent",
483
+ "%",
484
+ "Percent of content width"
485
+ ],
486
+ [
487
+ "fixed-width",
488
+ "W",
489
+ "Fixed width"
490
+ ],
491
+ [
492
+ "fixed-height",
493
+ "H",
494
+ "Fixed height"
495
+ ]
496
+ ];
417
497
  function snapToPreset(value) {
418
498
  for (const preset of SIZE_PRESETS) if (Math.abs(value - preset) <= SNAP_THRESHOLD) return preset;
419
499
  return value;
@@ -421,35 +501,8 @@ function snapToPreset(value) {
421
501
  function tickLeft(preset) {
422
502
  return `calc(6px + ${(preset - SIZE_MIN) / 90} * (100% - 12px))`;
423
503
  }
424
- var LAYOUT_OPTIONS = [
425
- {
426
- value: void 0,
427
- label: "Center",
428
- Icon: AlignCenterVertical
429
- },
430
- {
431
- value: "align-left",
432
- label: "Align left",
433
- Icon: AlignStartVertical
434
- },
435
- {
436
- value: "align-right",
437
- label: "Align right",
438
- Icon: AlignEndVertical
439
- },
440
- {
441
- value: "float-left",
442
- label: "Wrap left",
443
- Icon: ArrowLeftToLine
444
- },
445
- {
446
- value: "float-right",
447
- label: "Wrap right",
448
- Icon: ArrowRightToLine
449
- }
450
- ];
451
- var toolbarButtonClass = `${editToolbarButton} ${semanticClassNames.editToolbarButton}`;
452
- var stopMouseDown = (e) => {
504
+ var toolbarButtonClass$1 = `${editToolbarButton} ${semanticClassNames.editToolbarButton}`;
505
+ var stopMouseDown$1 = (e) => {
453
506
  e.preventDefault();
454
507
  e.stopPropagation();
455
508
  };
@@ -457,56 +510,162 @@ function ImageSizeControl() {
457
510
  const store = useStore();
458
511
  const [sizeOpen, setSizeOpen] = useAtom(sizeOpenAtom);
459
512
  const displayWidth = useAtomValue(displayWidthAtom);
460
- const { handleSetDisplayWidth } = useImageActions();
513
+ const fixedWidth = useAtomValue(fixedWidthAtom);
514
+ const fixedHeight = useAtomValue(fixedHeightAtom);
515
+ const intrinsicWidth = useAtomValue(widthAtom);
516
+ const intrinsicHeight = useAtomValue(heightAtom);
517
+ const { handleSetDisplaySize } = useImageActions();
461
518
  const [dragValue, setDragValue] = useState(null);
519
+ const [fixedDraft, setFixedDraft] = useState(null);
520
+ const size = resolveImageDisplaySize({
521
+ displayWidth,
522
+ fixedWidth,
523
+ fixedHeight
524
+ });
462
525
  useEffect(() => {
463
526
  setDragValue(null);
464
- }, [displayWidth]);
465
- const value = dragValue ?? displayWidth ?? SIZE_MAX;
466
- const applyPreview = (percent) => {
527
+ setFixedDraft(null);
528
+ }, [
529
+ displayWidth,
530
+ fixedWidth,
531
+ fixedHeight
532
+ ]);
533
+ const ctx = readImageDisplayContext(store.get(wrapperRefAtom).current, intrinsicWidth, intrinsicHeight);
534
+ const committedPixels = ctx ? computeImageDisplayPixels(size, ctx) : {};
535
+ const committedPercent = size.mode === "percent" ? size.value : ctx && committedPixels.widthPx ? sanitizeImageDisplayWidth(committedPixels.widthPx / ctx.containerWidth * 100) : void 0;
536
+ const previewSize = dragValue === null ? size : size.mode === "fixed-width" || size.mode === "fixed-height" ? ctx ? convertImageDisplaySize({
537
+ mode: "percent",
538
+ value: dragValue
539
+ }, size.mode, ctx) : size : {
540
+ mode: "percent",
541
+ value: dragValue
542
+ };
543
+ const previewPixels = ctx ? computeImageDisplayPixels(previewSize, {
544
+ ...ctx,
545
+ renderedWidth: dragValue !== null ? void 0 : ctx.renderedWidth,
546
+ renderedHeight: dragValue !== null ? void 0 : ctx.renderedHeight
547
+ }) : {};
548
+ const previewPercent = dragValue ?? (ctx && previewPixels.widthPx ? sanitizeImageDisplayWidth(previewPixels.widthPx / ctx.containerWidth * 100) : committedPercent);
549
+ const applyPreview = (next) => {
467
550
  const trigger = store.get(wrapperRefAtom).current;
468
- if (!trigger) return;
469
- const layout = store.get(layoutAtom);
470
- if (layout === "float-left" || layout === "float-right") {
471
- const blockWrapper = trigger.closest(".rich-image-wrapper");
472
- if (blockWrapper) blockWrapper.style.width = `${percent}%`;
473
- } else trigger.querySelector("figure")?.style.setProperty("--rich-image-display-width", `${percent}%`);
551
+ if (trigger) applyImageSizePreview(trigger, store.get(layoutAtom), next);
552
+ };
553
+ const switchMode = (to) => {
554
+ if (to === size.mode) return;
555
+ if (to === "auto") {
556
+ handleSetDisplaySize({ mode: "auto" });
557
+ return;
558
+ }
559
+ if (!ctx) return;
560
+ handleSetDisplaySize(convertImageDisplaySize(size, to, ctx));
561
+ };
562
+ const commitSlider = () => {
563
+ if (dragValue === null) return;
564
+ if (size.mode === "fixed-width" || size.mode === "fixed-height") {
565
+ if (dragValue === committedPercent || !ctx) return;
566
+ handleSetDisplaySize(convertImageDisplaySize({
567
+ mode: "percent",
568
+ value: dragValue
569
+ }, size.mode, ctx));
570
+ return;
571
+ }
572
+ if (dragValue === (displayWidth ?? SIZE_MAX)) return;
573
+ handleSetDisplaySize({
574
+ mode: "percent",
575
+ value: dragValue
576
+ });
474
577
  };
475
578
  const handleSliderChange = (event) => {
476
579
  const next = snapToPreset(Number(event.target.value));
477
580
  setDragValue(next);
478
- applyPreview(next);
479
- };
480
- const commit = () => {
481
- if (dragValue === null || dragValue === (displayWidth ?? SIZE_MAX)) return;
482
- handleSetDisplayWidth(dragValue);
581
+ if (size.mode === "fixed-width" || size.mode === "fixed-height") {
582
+ if (!ctx) return;
583
+ applyPreview(convertImageDisplaySize({
584
+ mode: "percent",
585
+ value: next
586
+ }, size.mode, ctx));
587
+ return;
588
+ }
589
+ applyPreview({
590
+ mode: "percent",
591
+ value: next
592
+ });
483
593
  };
594
+ const value = dragValue ?? committedPercent ?? SIZE_MAX;
484
595
  const fill = `${(value - SIZE_MIN) / 90 * 100}%`;
596
+ const fixedPx = size.mode === "fixed-width" || size.mode === "fixed-height" ? size.px : void 0;
485
597
  return /* @__PURE__ */ jsxs(Popover, {
486
598
  open: sizeOpen,
487
599
  onOpenChange: setSizeOpen,
488
600
  children: [/* @__PURE__ */ jsx(PopoverTrigger, {
489
- className: toolbarButtonClass,
601
+ className: toolbarButtonClass$1,
490
602
  title: "Image size",
491
- onMouseDown: stopMouseDown,
603
+ onMouseDown: stopMouseDown$1,
492
604
  children: /* @__PURE__ */ jsx(Scaling, { size: 14 })
493
605
  }), /* @__PURE__ */ jsxs(PopoverPanel, {
494
- className: controlPanel,
606
+ className: sizePanel,
495
607
  side: "bottom",
496
608
  sideOffset: 8,
497
- children: [
498
- /* @__PURE__ */ jsx("button", {
499
- className: `${sizeOption} ${displayWidth === void 0 ? editToolbarButtonActive : ""}`.trim(),
500
- title: "Natural size",
501
- type: "button",
502
- onMouseDown: stopMouseDown,
503
- onClick: () => {
504
- handleSetDisplayWidth(void 0);
505
- setSizeOpen(false);
506
- },
507
- children: "Auto"
508
- }),
509
- /* @__PURE__ */ jsxs("span", {
609
+ children: [/* @__PURE__ */ jsxs("div", {
610
+ className: sizePanelRow,
611
+ children: [
612
+ /* @__PURE__ */ jsx("button", {
613
+ className: `${sizeOption} ${size.mode === "auto" ? editToolbarButtonActive : ""}`.trim(),
614
+ title: "Natural size",
615
+ type: "button",
616
+ onMouseDown: stopMouseDown$1,
617
+ onClick: () => {
618
+ handleSetDisplaySize({ mode: "auto" });
619
+ setSizeOpen(false);
620
+ },
621
+ children: "Auto"
622
+ }),
623
+ MODE_BUTTONS.map(([mode, label, title]) => /* @__PURE__ */ jsx("button", {
624
+ className: `${sizeOption} ${size.mode === mode ? editToolbarButtonActive : ""}`.trim(),
625
+ title,
626
+ type: "button",
627
+ onClick: () => switchMode(mode),
628
+ onMouseDown: stopMouseDown$1,
629
+ children: label
630
+ }, mode)),
631
+ /* @__PURE__ */ jsxs("span", {
632
+ className: sizeReadout,
633
+ children: [size.mode === "fixed-width" || size.mode === "fixed-height" ? /* @__PURE__ */ jsx("input", {
634
+ autoComplete: "off",
635
+ className: `${sizeValueField} ${sizePxInput}`,
636
+ inputMode: "numeric",
637
+ spellCheck: false,
638
+ "aria-label": size.mode === "fixed-width" ? "Image width in pixels" : "Image height in pixels",
639
+ value: fixedDraft ?? (previewSize.mode === "fixed-width" || previewSize.mode === "fixed-height" ? String(previewSize.px) : String(fixedPx ?? "")),
640
+ onMouseDown: (e) => e.stopPropagation(),
641
+ onBlur: () => {
642
+ const px = sanitizeImageFixedPx(Number(fixedDraft ?? fixedPx));
643
+ if (px && (size.mode === "fixed-width" || size.mode === "fixed-height")) handleSetDisplaySize({
644
+ mode: size.mode,
645
+ px
646
+ });
647
+ setFixedDraft(null);
648
+ },
649
+ onChange: (event) => {
650
+ setFixedDraft(event.target.value);
651
+ const px = sanitizeImageFixedPx(Number(event.target.value));
652
+ if (px && (size.mode === "fixed-width" || size.mode === "fixed-height")) applyPreview({
653
+ mode: size.mode,
654
+ px
655
+ });
656
+ }
657
+ }) : /* @__PURE__ */ jsx("span", {
658
+ className: sizeValueField,
659
+ children: previewSize.mode === "percent" ? `${previewSize.value}%` : "Auto"
660
+ }), /* @__PURE__ */ jsx("span", {
661
+ className: sizeEquiv,
662
+ children: previewSize.mode === "percent" ? previewPixels.widthPx ? `${previewPixels.widthPx}px` : "\xA0" : previewSize.mode === "auto" ? "\xA0" : previewPercent ? `${previewPercent}%` : "\xA0"
663
+ })]
664
+ })
665
+ ]
666
+ }), /* @__PURE__ */ jsx("div", {
667
+ className: sizePanelRow,
668
+ children: /* @__PURE__ */ jsxs("span", {
510
669
  className: sizeSliderWrap,
511
670
  children: [/* @__PURE__ */ jsx("input", {
512
671
  "aria-label": "Image width, percent of content width",
@@ -517,24 +676,54 @@ function ImageSizeControl() {
517
676
  style: { "--fill": fill },
518
677
  type: "range",
519
678
  value,
520
- onBlur: commit,
679
+ onBlur: commitSlider,
521
680
  onChange: handleSliderChange,
522
- onKeyUp: commit,
681
+ onKeyUp: commitSlider,
523
682
  onMouseDown: (e) => e.stopPropagation(),
524
- onPointerUp: commit
683
+ onPointerUp: commitSlider
525
684
  }), SIZE_PRESETS.map((preset) => /* @__PURE__ */ jsx("span", {
526
685
  className: sizeSliderTick,
527
686
  style: { left: tickLeft(preset) }
528
687
  }, preset))]
529
- }),
530
- /* @__PURE__ */ jsx("span", {
531
- className: sizeValue,
532
- children: displayWidth === void 0 && dragValue === null ? "Auto" : `${value}%`
533
688
  })
534
- ]
689
+ })]
535
690
  })]
536
691
  });
537
692
  }
693
+ //#endregion
694
+ //#region src/ImageLayoutControls.tsx
695
+ var LAYOUT_OPTIONS = [
696
+ {
697
+ value: void 0,
698
+ label: "Center",
699
+ Icon: AlignCenterVertical
700
+ },
701
+ {
702
+ value: "align-left",
703
+ label: "Align left",
704
+ Icon: AlignStartVertical
705
+ },
706
+ {
707
+ value: "align-right",
708
+ label: "Align right",
709
+ Icon: AlignEndVertical
710
+ },
711
+ {
712
+ value: "float-left",
713
+ label: "Wrap left",
714
+ Icon: ArrowLeftToLine
715
+ },
716
+ {
717
+ value: "float-right",
718
+ label: "Wrap right",
719
+ Icon: ArrowRightToLine
720
+ }
721
+ ];
722
+ var toolbarButtonClass = `${editToolbarButton} ${semanticClassNames.editToolbarButton}`;
723
+ var stopMouseDown = (e) => {
724
+ e.preventDefault();
725
+ e.stopPropagation();
726
+ };
538
727
  function ImageLayoutControl() {
539
728
  const [layoutOpen, setLayoutOpen] = useAtom(layoutOpenAtom);
540
729
  const layout = useAtomValue(layoutAtom);
@@ -826,10 +1015,8 @@ function useImageResize() {
826
1015
  const frame = trigger.querySelector(`.${semanticClassNames.frame}`);
827
1016
  const figure = trigger.querySelector("figure");
828
1017
  const blockWrapper = trigger.closest(".rich-image-wrapper");
829
- const container = blockWrapper?.parentElement ?? trigger.parentElement;
830
- if (!frame || !figure || !container) return;
831
- const containerStyle = getComputedStyle(container);
832
- const containerWidth = container.clientWidth - Number.parseFloat(containerStyle.paddingLeft) - Number.parseFloat(containerStyle.paddingRight);
1018
+ if (!frame || !figure) return;
1019
+ const containerWidth = measureImageContainerWidth(trigger);
833
1020
  const startWidth = frame.getBoundingClientRect().width;
834
1021
  if (!containerWidth || containerWidth <= 0 || !startWidth) return;
835
1022
  event.preventDefault();
@@ -845,17 +1032,42 @@ function useImageResize() {
845
1032
  store.set(resizingAtom, true);
846
1033
  const prevUserSelect = document.body.style.userSelect;
847
1034
  document.body.style.userSelect = "none";
848
- let percent = sanitizeImageDisplayWidth(startWidth / containerWidth * 100) ?? 100;
1035
+ const startHeight = frame.getBoundingClientRect().height;
1036
+ const aspect = startWidth > 0 && startHeight > 0 ? startHeight / startWidth : void 0;
1037
+ const currentMode = resolveImageDisplaySize({
1038
+ displayWidth: store.get(displayWidthAtom),
1039
+ fixedWidth: store.get(fixedWidthAtom),
1040
+ fixedHeight: store.get(fixedHeightAtom)
1041
+ }).mode;
1042
+ let nextSize = imageDisplaySizeAfterResize(currentMode, startWidth, startHeight, containerWidth);
849
1043
  let moved = false;
1044
+ const applyPreview = (size) => {
1045
+ const vars = imageDisplayCssVars(size);
1046
+ figure.style.removeProperty("--rich-image-display-width");
1047
+ figure.style.removeProperty("--rich-image-display-height");
1048
+ if (vars) for (const [key, value] of Object.entries(vars)) figure.style.setProperty(key, value);
1049
+ if (isFloat && blockWrapper) {
1050
+ if (size.mode === "percent") {
1051
+ blockWrapper.style.width = `${size.value}%`;
1052
+ blockWrapper.style.maxWidth = "";
1053
+ } else if (size.mode === "fixed-width") {
1054
+ blockWrapper.style.width = `${size.px}px`;
1055
+ blockWrapper.style.maxWidth = "100%";
1056
+ } else if (size.mode === "fixed-height") {
1057
+ blockWrapper.style.width = "auto";
1058
+ blockWrapper.style.maxWidth = "100%";
1059
+ }
1060
+ }
1061
+ };
850
1062
  const handleMove = (moveEvent) => {
851
1063
  const rawDelta = moveEvent.clientX - startX;
852
1064
  if (!moved && Math.abs(rawDelta) < MOVE_THRESHOLD_PX) return;
853
1065
  moved = true;
854
1066
  const outward = side === "left" ? -rawDelta : rawDelta;
855
1067
  const nextWidth = startWidth + outward * factor;
856
- percent = sanitizeImageDisplayWidth(nextWidth / containerWidth * 100) ?? percent;
857
- if (isFloat && blockWrapper) blockWrapper.style.width = `${percent}%`;
858
- else figure.style.setProperty("--rich-image-display-width", `${percent}%`);
1068
+ const nextHeight = aspect ? nextWidth * aspect : startHeight;
1069
+ nextSize = imageDisplaySizeAfterResize(currentMode, nextWidth, nextHeight, containerWidth);
1070
+ applyPreview(nextSize);
859
1071
  };
860
1072
  const finish = (commit) => {
861
1073
  if (finishRef.current !== finish) return;
@@ -868,7 +1080,7 @@ function useImageResize() {
868
1080
  if (!commit || !moved) return;
869
1081
  editor.update(() => {
870
1082
  const node = $getNearestNodeFromDOMNode(trigger);
871
- if ($isImageNode(node)) node.setDisplayWidth(percent);
1083
+ if ($isImageNode(node)) node.applyDisplaySize(nextSize);
872
1084
  }, { tag: "skip-scroll-into-view" });
873
1085
  };
874
1086
  const handleEnd = () => finish(true);
@@ -1105,7 +1317,14 @@ function ImageEditContent() {
1105
1317
  const setReplaceOpen = useSetAtom(replaceOpenAtom);
1106
1318
  const setFocusCaptionOnOpen = useSetAtom(focusCaptionOnOpenAtom);
1107
1319
  const displayWidth = useAtomValue(displayWidthAtom);
1320
+ const fixedWidth = useAtomValue(fixedWidthAtom);
1321
+ const fixedHeight = useAtomValue(fixedHeightAtom);
1108
1322
  const layout = useAtomValue(layoutAtom);
1323
+ const displaySize = resolveImageDisplaySize({
1324
+ displayWidth,
1325
+ fixedWidth,
1326
+ fixedHeight
1327
+ });
1109
1328
  const dragLayoutProps = useImageDragLayout();
1110
1329
  const handleCaptionClick = useCallback((e) => {
1111
1330
  e.stopPropagation();
@@ -1121,7 +1340,7 @@ function ImageEditContent() {
1121
1340
  setReplaceOpen,
1122
1341
  setFocusCaptionOnOpen
1123
1342
  ]);
1124
- const figureStyle = displayWidth ? { "--rich-image-display-width": `${displayWidth}%` } : void 0;
1343
+ const figureStyle = imageDisplayCssVars(displaySize);
1125
1344
  return /* @__PURE__ */ jsxs("div", {
1126
1345
  className: `${editTrigger} ${semanticClassNames.editTrigger}`,
1127
1346
  ref: wrapperRef,
@@ -1129,6 +1348,7 @@ function ImageEditContent() {
1129
1348
  onMouseLeave: () => setHovering(false),
1130
1349
  children: [src ? /* @__PURE__ */ jsxs("figure", {
1131
1350
  className: `${root} ${semanticClassNames.root}`,
1351
+ "data-display": imageDisplayDataAttr(displaySize),
1132
1352
  "data-layout": layout,
1133
1353
  style: figureStyle,
1134
1354
  ...dragLayoutProps,
@@ -1143,6 +1363,12 @@ function ImageEditContent() {
1143
1363
  loading: "lazy",
1144
1364
  src,
1145
1365
  width,
1366
+ style: displaySize.mode === "fixed-height" ? {
1367
+ width: "auto",
1368
+ height: "auto",
1369
+ maxWidth: "100%",
1370
+ maxHeight: "100%"
1371
+ } : void 0,
1146
1372
  onError: () => setLoadState("error"),
1147
1373
  onLoad: () => setLoadState("loaded")
1148
1374
  }),
@@ -1,2 +1,2 @@
1
- :root{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#2563eb;--rc-quote-bg:#f5f5f5;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.7;--rc-line-height-tight:1.4;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}:root.dark,[data-theme=dark]{--rc-text:#fafafa;--rc-text-secondary:#a3a3a3;--rc-text-tertiary:#737373;--rc-text-quaternary:#525252;--rc-bg:#0a0a0a;--rc-bg-secondary:#171717;--rc-bg-tertiary:#262626;--rc-fill:#2a2a2a;--rc-fill-secondary:#222;--rc-fill-tertiary:#1a1a1a;--rc-fill-quaternary:#141414;--rc-border:#262626;--rc-accent:#60a5fa;--rc-accent-light:#60a5fa20;--rc-link:#60a5fa;--rc-code-text:#d4d4d4;--rc-code-bg:#262626;--rc-hr-border:#262626;--rc-quote-border:#60a5fa;--rc-quote-bg:#262626;--rc-alert-info:#7db9e5;--rc-alert-warning:#da864a;--rc-alert-tip:#54da48;--rc-alert-caution:#e16973;--rc-alert-important:#9966e0;--rc-shadow-top-bar:0 8px 30px #00000073, 0 2px 8px #0000004d;--rc-shadow-modal:0 10px 15px -3px #0006, 0 4px 6px -4px #00000059;--rc-shadow-menu:0 1px 4px #00000040, 0 4px 16px #0006}._7eow1s0{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#2563eb;--rc-quote-bg:#f5f5f5;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.7;--rc-line-height-tight:1.4;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}._7eow1s1{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#2563eb;--rc-quote-bg:#f5f5f5;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.8;--rc-line-height-tight:1.4;--rc-font-family:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}._7eow1s2{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#a3a3a3;--rc-quote-bg:#fafafa;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:none;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:2px;--rc-space-sm:4px;--rc-space-md:10px;--rc-space-lg:16px;--rc-space-xl:20px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:14px;--rc-font-size-small:12px;--rc-line-height:1.5;--rc-line-height-tight:1.3;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:3px;--rc-radius-md:6px;--rc-radius-lg:12px}.dark ._7eow1s0,[data-theme=dark] ._7eow1s0,.dark._7eow1s0,[data-theme=dark]._7eow1s0,.dark ._7eow1s1,[data-theme=dark] ._7eow1s1,.dark._7eow1s1,[data-theme=dark]._7eow1s1,.dark ._7eow1s2,[data-theme=dark] ._7eow1s2,.dark._7eow1s2,[data-theme=dark]._7eow1s2{--rc-text:#fafafa;--rc-text-secondary:#a3a3a3;--rc-text-tertiary:#737373;--rc-text-quaternary:#525252;--rc-bg:#0a0a0a;--rc-bg-secondary:#171717;--rc-bg-tertiary:#262626;--rc-fill:#2a2a2a;--rc-fill-secondary:#222;--rc-fill-tertiary:#1a1a1a;--rc-fill-quaternary:#141414;--rc-border:#262626;--rc-accent:#60a5fa;--rc-accent-light:#60a5fa20;--rc-link:#60a5fa;--rc-code-text:#d4d4d4;--rc-code-bg:#262626;--rc-hr-border:#262626;--rc-quote-border:#60a5fa;--rc-quote-bg:#262626;--rc-alert-info:#7db9e5;--rc-alert-warning:#da864a;--rc-alert-tip:#54da48;--rc-alert-caution:#e16973;--rc-alert-important:#9966e0;--rc-shadow-top-bar:0 8px 30px #00000073, 0 2px 8px #0000004d;--rc-shadow-modal:0 10px 15px -3px #0006, 0 4px 6px -4px #00000059;--rc-shadow-menu:0 1px 4px #00000040, 0 4px 16px #0006}@keyframes _1n94osf2{0%{opacity:.16;-webkit-mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 150% 0/400% no-repeat;mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 150% 0/400% no-repeat}to{opacity:1;-webkit-mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 0/400% no-repeat;mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 0/400% no-repeat}}@keyframes _1n94osfb{to{transform:rotate(360deg)}}@keyframes _1n94osfj{0%{opacity:0}to{opacity:1}}._1n94osf0{text-align:center;width:var(--rich-image-display-width,auto);max-width:100%;margin:1.25rem auto}._1n94osf0[data-layout=align-left]{text-align:left;margin-left:0;margin-right:auto}._1n94osf0[data-layout=align-right]{text-align:right;margin-left:auto;margin-right:0}._1n94osf0[data-layout=float-left]{float:left;width:var(--rich-image-display-width,42%);margin:.125rem 1.25rem .875rem 0}._1n94osf0[data-layout=float-right]{float:right;width:var(--rich-image-display-width,42%);margin:.125rem 0 .875rem 1.25rem}.rich-image-wrapper[data-layout^=float] ._1n94osf0{float:none;width:auto;margin:0}._1n94osf1{opacity:.4}.rich-editor__content[data-rich-image-drop-side]{cursor:move}.rich-editor__content[data-rich-image-drop-side=left]{box-shadow:inset 3px 0 0 color-mix(in srgb, var(--rc-accent) 70%, transparent)}.rich-editor__content[data-rich-image-drop-side=right]{box-shadow:inset -3px 0 0 color-mix(in srgb, var(--rc-accent) 70%, transparent)}._1n94osf3{opacity:0;width:100%;max-width:100%;height:auto;display:block;border-radius:0!important}._1n94osf7{opacity:1;animation:.42s _1n94osf2}._1n94osf8{border-radius:var(--rc-radius-md);cursor:zoom-in;justify-content:center;align-items:center;min-height:3rem;transition:transform .25s;display:inline-flex;position:relative;overflow:hidden}._1n94osf8:hover{transform:translateY(-1px)}._1n94osf9{cursor:default}._1n94osf9:hover{transform:none}._1n94osfa{cursor:default}._1n94osfa:hover{transform:none}._1n94osfc{border:2px solid color-mix(in srgb, var(--rc-text-secondary) 45%, transparent);border-top-color:color-mix(in srgb, var(--rc-text) 65%, transparent);border-radius:50%;width:1.65rem;height:1.65rem;animation:.75s linear infinite _1n94osfb;position:absolute}._1n94osfd{white-space:nowrap;font-size:var(--rc-font-size-sm);letter-spacing:-.005em;color:#fff;-webkit-backdrop-filter:blur(20px)saturate(180%);background:#ff3b30a6;border-radius:6px;padding:.3rem .7rem;font-weight:500;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}._1n94osfe{color:var(--rc-text-tertiary);font-size:var(--rc-font-size-md);margin-top:.5rem;line-height:1.5}._1n94osff{margin:var(--rc-space-md) 0;text-align:center}._1n94osfg{border-radius:var(--rc-radius-md);transition:background-color .3s;display:inline-block;position:relative;overflow:hidden}._1n94osfh{opacity:0;transition:opacity .3s}._1n94osfi{opacity:1;transition:opacity .3s}._1n94osfk{z-index:9999;cursor:zoom-out;background-color:#000000d9;justify-content:center;align-items:center;animation:.2s _1n94osfj;display:flex;position:fixed;top:0;bottom:0;left:0;right:0}._1n94osfl{object-fit:contain;border-radius:0;max-width:90vw;max-height:90vh}.rich-image{margin:var(--rc-space-md) 0;text-align:center}.rich-image figcaption{font-size:var(--rc-font-size-md);color:var(--rc-text-secondary);margin-top:var(--rc-space-sm);line-height:var(--rc-line-height-tight)}.rich-image-container{border-radius:var(--rc-radius-md);transition:background-color .3s;display:inline-block;position:relative;overflow:hidden}.rich-image-hidden{opacity:0;transition:opacity .3s}.rich-image-visible{opacity:1;transition:opacity .3s}.rich-image-zoom-overlay{z-index:9999;cursor:zoom-out;background-color:#000000d9;justify-content:center;align-items:center;animation:.2s _1n94osfj;display:flex;position:fixed;top:0;bottom:0;left:0;right:0}.rich-image-zoom-img{object-fit:contain;border-radius:0;max-width:90vw;max-height:90vh}._1n94osfm{cursor:default;display:block;position:relative}._1n94osfn{border:2px dashed var(--rc-border);border-radius:var(--rc-radius-md);color:var(--rc-text-secondary);font-size:var(--rc-font-size-md);cursor:pointer;flex-direction:column;justify-content:center;align-items:center;gap:8px;padding:2rem;transition:border-color .2s,color .2s,background-color .2s;display:flex}._1n94osfn:hover{border-color:var(--rc-accent);color:var(--rc-text);background-color:color-mix(in srgb, var(--rc-accent) 8%, transparent)}._1n94osfo{z-index:20;border:1px solid var(--rc-border);background:color-mix(in srgb, var(--rc-bg) 96%, transparent);box-shadow:var(--rc-shadow-top-bar);opacity:0;pointer-events:none;border-radius:10px;align-items:center;gap:4px;padding:4px;transition:opacity .15s;display:inline-flex;position:absolute;top:-2.5rem;left:50%;transform:translate(-50%)}._1n94osfp{opacity:1;pointer-events:auto}._1n94osfq{width:30px;height:30px;color:var(--rc-text-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;transition:background-color .15s,color .15s;display:inline-flex}._1n94osfq:hover{color:var(--rc-text);background-color:var(--rc-fill-secondary)}._1n94osfr:hover{color:var(--rc-alert-caution);background-color:color-mix(in srgb, var(--rc-alert-caution) 12%, transparent)}._1n94osfs{width:360px;font-size:var(--rc-font-size-base);font-family:var(--rc-font-family);z-index:30;flex-direction:column;gap:8px;padding:12px;display:flex;position:relative}._1n94osft{background-color:var(--rc-bg-secondary);border-radius:6px;align-items:center;gap:8px;min-width:0;padding:6px 10px;display:flex}._1n94osfu{color:var(--rc-text-secondary);flex-shrink:0}._1n94osfv{-webkit-appearance:none;appearance:none;color:inherit;font-size:var(--rc-font-size-sm);background-color:#0000;border:none;outline:none;flex:1;min-width:0;padding:0}._1n94osfv::placeholder{color:var(--rc-text-secondary)}._1n94osfw{border-radius:var(--rc-radius-md);border:1.5px dashed var(--rc-border);min-height:138px;color:var(--rc-text-secondary);cursor:pointer;flex-direction:column;justify-content:center;align-items:center;gap:.45rem;transition:border-color .15s,background-color .15s;display:flex}._1n94osfw:hover{border-color:color-mix(in srgb, var(--rc-accent) 45%, transparent);color:var(--rc-text);background-color:color-mix(in srgb, var(--rc-accent) 7%, transparent)}._1n94osfx{border-radius:var(--rc-radius-md);border:1px solid var(--rc-border);background-color:color-mix(in srgb, var(--rc-text) 2%, transparent);overflow:hidden}._1n94osfx img{object-fit:cover;width:100%;max-height:188px;display:block}._1n94osfy{color:var(--rc-text-secondary);font-size:var(--rc-font-size-xs);align-items:center;gap:.4rem;display:inline-flex}._1n94osfz{cursor:ew-resize;touch-action:none;opacity:0;z-index:10;background:#ffffffeb;border:1px solid #0000002e;border-radius:9999px;width:6px;height:36px;max-height:50%;transition:opacity .15s;position:absolute;top:50%;transform:translateY(-50%);box-shadow:0 1px 3px #00000040}._1n94osf10{left:6px}._1n94osf11{right:6px}._1n94osf12{opacity:1}._1n94osf13{font-family:var(--rc-font-family);z-index:30;align-items:center;gap:4px;padding:4px;display:flex}._1n94osf13._1n94osf13{width:-moz-fit-content;width:fit-content}._1n94osf14{align-items:center;width:132px;height:24px;display:flex;position:relative}._1n94osf15{-webkit-appearance:none;appearance:none;cursor:pointer;background:0 0;width:100%;height:24px;margin:0}._1n94osf15::-webkit-slider-runnable-track{background:linear-gradient(to right, var(--rc-text-secondary) var(--fill), var(--rc-fill) var(--fill));border-radius:9999px;height:3px}._1n94osf15::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;background-color:var(--rc-bg);border:1px solid var(--rc-border);cursor:ew-resize;border-radius:50%;width:12px;height:12px;margin-top:-4.5px;box-shadow:0 1px 3px #0003}._1n94osf15::-moz-range-track{background:linear-gradient(to right, var(--rc-text-secondary) var(--fill), var(--rc-fill) var(--fill));border-radius:9999px;height:3px}._1n94osf15::-moz-range-thumb{-webkit-appearance:none;appearance:none;background-color:var(--rc-bg);border:1px solid var(--rc-border);cursor:ew-resize;border-radius:50%;width:12px;height:12px;box-shadow:0 1px 3px #0003}._1n94osf16{background-color:var(--rc-bg);pointer-events:none;border-radius:50%;width:3px;height:3px;position:absolute;top:50%;transform:translate(-50%,-50%)}._1n94osf17{text-align:right;min-width:34px;font-size:var(--rc-font-size-xs);font-variant-numeric:tabular-nums;color:var(--rc-text-secondary)}._1n94osf18{height:26px;color:var(--rc-text-secondary);font-size:var(--rc-font-size-xs);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0 8px;font-weight:500;transition:background-color .15s,color .15s;display:inline-flex}._1n94osf18:hover,._1n94osf19{color:var(--rc-text);background-color:var(--rc-fill-secondary)}@media (max-width:640px){._1n94osf0[data-layout=float-left],._1n94osf0[data-layout=float-right]{float:none;width:auto;margin:1.25rem auto}}
1
+ :root{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#2563eb;--rc-quote-bg:#f5f5f5;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.7;--rc-line-height-tight:1.4;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}:root.dark,[data-theme=dark]{--rc-text:#fafafa;--rc-text-secondary:#a3a3a3;--rc-text-tertiary:#737373;--rc-text-quaternary:#525252;--rc-bg:#0a0a0a;--rc-bg-secondary:#171717;--rc-bg-tertiary:#262626;--rc-fill:#2a2a2a;--rc-fill-secondary:#222;--rc-fill-tertiary:#1a1a1a;--rc-fill-quaternary:#141414;--rc-border:#262626;--rc-accent:#60a5fa;--rc-accent-light:#60a5fa20;--rc-link:#60a5fa;--rc-code-text:#d4d4d4;--rc-code-bg:#262626;--rc-hr-border:#262626;--rc-quote-border:#60a5fa;--rc-quote-bg:#262626;--rc-alert-info:#7db9e5;--rc-alert-warning:#da864a;--rc-alert-tip:#54da48;--rc-alert-caution:#e16973;--rc-alert-important:#9966e0;--rc-shadow-top-bar:0 8px 30px #00000073, 0 2px 8px #0000004d;--rc-shadow-modal:0 10px 15px -3px #0006, 0 4px 6px -4px #00000059;--rc-shadow-menu:0 1px 4px #00000040, 0 4px 16px #0006}._7eow1s0{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#2563eb;--rc-quote-bg:#f5f5f5;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.7;--rc-line-height-tight:1.4;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}._7eow1s1{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#2563eb;--rc-quote-bg:#f5f5f5;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:700px;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:4px;--rc-space-sm:8px;--rc-space-md:16px;--rc-space-lg:24px;--rc-space-xl:32px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:16px;--rc-font-size-small:14px;--rc-line-height:1.8;--rc-line-height-tight:1.4;--rc-font-family:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-radius-sm:4px;--rc-radius-md:8px;--rc-radius-lg:12px}._7eow1s2{--rc-text:#000;--rc-text-secondary:#262626;--rc-text-tertiary:#737373;--rc-text-quaternary:#a3a3a3;--rc-bg:#fff;--rc-bg-secondary:#fafafa;--rc-bg-tertiary:#f5f5f5;--rc-fill:#e8e8e8;--rc-fill-secondary:#eee;--rc-fill-tertiary:#f5f5f5;--rc-fill-quaternary:#fafafa;--rc-border:#f5f5f5;--rc-accent:#2563eb;--rc-accent-light:#2563eb20;--rc-link:#2563eb;--rc-code-text:#404040;--rc-code-bg:#f5f5f5;--rc-hr-border:#e5e5e5;--rc-quote-border:#a3a3a3;--rc-quote-bg:#fafafa;--rc-alert-info:#006bb7;--rc-alert-warning:#c50;--rc-alert-tip:#1c0;--rc-alert-caution:#c01;--rc-alert-important:#50c;--rc-max-width:none;--rc-shadow-top-bar:0 8px 30px #0000001f, 0 2px 8px #0000000f;--rc-shadow-modal:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--rc-shadow-menu:0 1px 4px #0000000a, 0 4px 16px #00000014;--rc-space-xs:2px;--rc-space-sm:4px;--rc-space-md:10px;--rc-space-lg:16px;--rc-space-xl:20px;--rc-font-family-sans:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-font-family-serif:"Noto Serif CJK SC", "Source Han Serif SC", "Source Han Serif", "source-han-serif-sc", "Songti SC", STSong, "华文宋体", serif;--rc-font-family-kai:"楷体", KaiTi, STKaiti, "Kaiti SC", "LXGW WenKai", "霞鹜文楷", "Noto Serif CJK SC", serif;--rc-font-mono:"SF Mono", SFMono-Regular, ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;--rc-font-size-2xs:.625em;--rc-font-size-xs:.75em;--rc-font-size-sm:.8125em;--rc-font-size-md:.875em;--rc-font-size-lg:1.25em;--rc-font-size-base:14px;--rc-font-size-small:12px;--rc-line-height:1.5;--rc-line-height-tight:1.3;--rc-font-family:"PingFang SC", "Microsoft YaHei", "Segoe UI", Roboto, Helvetica, "noto sans sc", "hiragino sans gb", -apple-system, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Not Color Emoji;--rc-radius-sm:3px;--rc-radius-md:6px;--rc-radius-lg:12px}.dark ._7eow1s0,[data-theme=dark] ._7eow1s0,.dark._7eow1s0,[data-theme=dark]._7eow1s0,.dark ._7eow1s1,[data-theme=dark] ._7eow1s1,.dark._7eow1s1,[data-theme=dark]._7eow1s1,.dark ._7eow1s2,[data-theme=dark] ._7eow1s2,.dark._7eow1s2,[data-theme=dark]._7eow1s2{--rc-text:#fafafa;--rc-text-secondary:#a3a3a3;--rc-text-tertiary:#737373;--rc-text-quaternary:#525252;--rc-bg:#0a0a0a;--rc-bg-secondary:#171717;--rc-bg-tertiary:#262626;--rc-fill:#2a2a2a;--rc-fill-secondary:#222;--rc-fill-tertiary:#1a1a1a;--rc-fill-quaternary:#141414;--rc-border:#262626;--rc-accent:#60a5fa;--rc-accent-light:#60a5fa20;--rc-link:#60a5fa;--rc-code-text:#d4d4d4;--rc-code-bg:#262626;--rc-hr-border:#262626;--rc-quote-border:#60a5fa;--rc-quote-bg:#262626;--rc-alert-info:#7db9e5;--rc-alert-warning:#da864a;--rc-alert-tip:#54da48;--rc-alert-caution:#e16973;--rc-alert-important:#9966e0;--rc-shadow-top-bar:0 8px 30px #00000073, 0 2px 8px #0000004d;--rc-shadow-modal:0 10px 15px -3px #0006, 0 4px 6px -4px #00000059;--rc-shadow-menu:0 1px 4px #00000040, 0 4px 16px #0006}@keyframes _1n94osf2{0%{opacity:.16;-webkit-mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 150% 0/400% no-repeat;mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 150% 0/400% no-repeat}to{opacity:1;-webkit-mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 0/400% no-repeat;mask:linear-gradient(90deg,#000 25%,#000000e6 50%,#0000) 0/400% no-repeat}}@keyframes _1n94osfb{to{transform:rotate(360deg)}}@keyframes _1n94osfj{0%{opacity:0}to{opacity:1}}._1n94osf0{text-align:center;width:var(--rich-image-display-width,auto);max-width:100%;max-height:var(--rich-image-display-height,none);margin:1.25rem auto}._1n94osf0[data-layout=align-left]{text-align:left;margin-left:0;margin-right:auto}._1n94osf0[data-layout=align-right]{text-align:right;margin-left:auto;margin-right:0}._1n94osf0[data-layout=float-left]{float:left;width:var(--rich-image-display-width,42%);margin:.125rem 1.25rem .875rem 0}._1n94osf0[data-layout=float-right]{float:right;width:var(--rich-image-display-width,42%);margin:.125rem 0 .875rem 1.25rem}._1n94osf0[data-display=fixed-height],._1n94osf0[data-layout=float-left][data-display=fixed-height],._1n94osf0[data-layout=float-right][data-display=fixed-height]{width:auto}.rich-image-wrapper[data-layout^=float] ._1n94osf0{float:none;width:auto;margin:0}._1n94osf1{opacity:.4}.rich-editor__content[data-rich-image-drop-side]{cursor:move}.rich-editor__content[data-rich-image-drop-side=left]{box-shadow:inset 3px 0 0 color-mix(in srgb, var(--rc-accent) 70%, transparent)}.rich-editor__content[data-rich-image-drop-side=right]{box-shadow:inset -3px 0 0 color-mix(in srgb, var(--rc-accent) 70%, transparent)}._1n94osf3{opacity:0;width:100%;max-width:100%;height:auto;display:block;border-radius:0!important}._1n94osf7{opacity:1;animation:.42s _1n94osf2}._1n94osf8{border-radius:var(--rc-radius-md);cursor:zoom-in;justify-content:center;align-items:center;min-height:3rem;transition:transform .25s;display:inline-flex;position:relative;overflow:hidden}._1n94osf8:hover{transform:translateY(-1px)}._1n94osf9{cursor:default}._1n94osf9:hover{transform:none}._1n94osfa{cursor:default}._1n94osfa:hover{transform:none}._1n94osf0[data-display=fixed-height] ._1n94osf3{width:auto;max-width:100%;height:auto;max-height:var(--rich-image-display-height)}._1n94osf0[data-display=fixed-height] ._1n94osf8{width:auto;max-width:100%;max-height:var(--rich-image-display-height)}._1n94osfc{border:2px solid color-mix(in srgb, var(--rc-text-secondary) 45%, transparent);border-top-color:color-mix(in srgb, var(--rc-text) 65%, transparent);border-radius:50%;width:1.65rem;height:1.65rem;animation:.75s linear infinite _1n94osfb;position:absolute}._1n94osfd{white-space:nowrap;font-size:var(--rc-font-size-sm);letter-spacing:-.005em;color:#fff;-webkit-backdrop-filter:blur(20px)saturate(180%);background:#ff3b30a6;border-radius:6px;padding:.3rem .7rem;font-weight:500;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}._1n94osfe{color:var(--rc-text-tertiary);font-size:var(--rc-font-size-md);margin-top:.5rem;line-height:1.5}._1n94osff{margin:var(--rc-space-md) 0;text-align:center}._1n94osfg{border-radius:var(--rc-radius-md);transition:background-color .3s;display:inline-block;position:relative;overflow:hidden}._1n94osfh{opacity:0;transition:opacity .3s}._1n94osfi{opacity:1;transition:opacity .3s}._1n94osfk{z-index:9999;cursor:zoom-out;background-color:#000000d9;justify-content:center;align-items:center;animation:.2s _1n94osfj;display:flex;position:fixed;top:0;bottom:0;left:0;right:0}._1n94osfl{object-fit:contain;border-radius:0;max-width:90vw;max-height:90vh}.rich-image{margin:var(--rc-space-md) 0;text-align:center}.rich-image figcaption{font-size:var(--rc-font-size-md);color:var(--rc-text-secondary);margin-top:var(--rc-space-sm);line-height:var(--rc-line-height-tight)}.rich-image-container{border-radius:var(--rc-radius-md);transition:background-color .3s;display:inline-block;position:relative;overflow:hidden}.rich-image-hidden{opacity:0;transition:opacity .3s}.rich-image-visible{opacity:1;transition:opacity .3s}.rich-image-zoom-overlay{z-index:9999;cursor:zoom-out;background-color:#000000d9;justify-content:center;align-items:center;animation:.2s _1n94osfj;display:flex;position:fixed;top:0;bottom:0;left:0;right:0}.rich-image-zoom-img{object-fit:contain;border-radius:0;max-width:90vw;max-height:90vh}._1n94osfm{cursor:default;display:block;position:relative}._1n94osfn{border:2px dashed var(--rc-border);border-radius:var(--rc-radius-md);color:var(--rc-text-secondary);font-size:var(--rc-font-size-md);cursor:pointer;flex-direction:column;justify-content:center;align-items:center;gap:8px;padding:2rem;transition:border-color .2s,color .2s,background-color .2s;display:flex}._1n94osfn:hover{border-color:var(--rc-accent);color:var(--rc-text);background-color:color-mix(in srgb, var(--rc-accent) 8%, transparent)}._1n94osfo{z-index:20;border:1px solid var(--rc-border);background:color-mix(in srgb, var(--rc-bg) 96%, transparent);box-shadow:var(--rc-shadow-top-bar);opacity:0;pointer-events:none;border-radius:10px;align-items:center;gap:4px;padding:4px;transition:opacity .15s;display:inline-flex;position:absolute;top:-2.5rem;left:50%;transform:translate(-50%)}._1n94osfp{opacity:1;pointer-events:auto}._1n94osfq{width:30px;height:30px;color:var(--rc-text-secondary);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;transition:background-color .15s,color .15s;display:inline-flex}._1n94osfq:hover{color:var(--rc-text);background-color:var(--rc-fill-secondary)}._1n94osfr:hover{color:var(--rc-alert-caution);background-color:color-mix(in srgb, var(--rc-alert-caution) 12%, transparent)}._1n94osfs{width:360px;font-size:var(--rc-font-size-base);font-family:var(--rc-font-family);z-index:30;flex-direction:column;gap:8px;padding:12px;display:flex;position:relative}._1n94osft{background-color:var(--rc-bg-secondary);border-radius:6px;align-items:center;gap:8px;min-width:0;padding:6px 10px;display:flex}._1n94osfu{color:var(--rc-text-secondary);flex-shrink:0}._1n94osfv{-webkit-appearance:none;appearance:none;color:inherit;font-size:var(--rc-font-size-sm);background-color:#0000;border:none;outline:none;flex:1;min-width:0;padding:0}._1n94osfv::placeholder{color:var(--rc-text-secondary)}._1n94osfw{border-radius:var(--rc-radius-md);border:1.5px dashed var(--rc-border);min-height:138px;color:var(--rc-text-secondary);cursor:pointer;flex-direction:column;justify-content:center;align-items:center;gap:.45rem;transition:border-color .15s,background-color .15s;display:flex}._1n94osfw:hover{border-color:color-mix(in srgb, var(--rc-accent) 45%, transparent);color:var(--rc-text);background-color:color-mix(in srgb, var(--rc-accent) 7%, transparent)}._1n94osfx{border-radius:var(--rc-radius-md);border:1px solid var(--rc-border);background-color:color-mix(in srgb, var(--rc-text) 2%, transparent);overflow:hidden}._1n94osfx img{object-fit:cover;width:100%;max-height:188px;display:block}._1n94osfy{color:var(--rc-text-secondary);font-size:var(--rc-font-size-xs);align-items:center;gap:.4rem;display:inline-flex}._1n94osfz{cursor:ew-resize;touch-action:none;opacity:0;z-index:10;background:#ffffffeb;border:1px solid #0000002e;border-radius:9999px;width:6px;height:36px;max-height:50%;transition:opacity .15s;position:absolute;top:50%;transform:translateY(-50%);box-shadow:0 1px 3px #00000040}._1n94osf10{left:6px}._1n94osf11{right:6px}._1n94osf12{opacity:1}._1n94osf13{font-family:var(--rc-font-family);z-index:30;align-items:center;gap:4px;padding:4px;display:flex}._1n94osf13._1n94osf13{width:-moz-fit-content;width:fit-content}._1n94osf14{font-family:var(--rc-font-family);z-index:30;flex-direction:column;align-items:stretch;gap:2px;padding:4px;display:flex}._1n94osf14._1n94osf14{width:272px}._1n94osf15{align-items:center;gap:4px;min-height:26px;display:flex}._1n94osf16{flex-shrink:0;align-items:center;gap:6px;margin-left:auto;padding-left:8px;display:flex}._1n94osf17{flex:1;align-items:center;height:24px;display:flex;position:relative}._1n94osf18{-webkit-appearance:none;appearance:none;cursor:pointer;background:0 0;width:100%;height:24px;margin:0}._1n94osf18::-webkit-slider-runnable-track{background:linear-gradient(to right, var(--rc-text-secondary) var(--fill), var(--rc-fill) var(--fill));border-radius:9999px;height:3px}._1n94osf18::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;background-color:var(--rc-bg);border:1px solid var(--rc-border);cursor:ew-resize;border-radius:50%;width:12px;height:12px;margin-top:-4.5px;box-shadow:0 1px 3px #0003}._1n94osf18::-moz-range-track{background:linear-gradient(to right, var(--rc-text-secondary) var(--fill), var(--rc-fill) var(--fill));border-radius:9999px;height:3px}._1n94osf18::-moz-range-thumb{-webkit-appearance:none;appearance:none;background-color:var(--rc-bg);border:1px solid var(--rc-border);cursor:ew-resize;border-radius:50%;width:12px;height:12px;box-shadow:0 1px 3px #0003}._1n94osf19{background-color:var(--rc-bg);pointer-events:none;border-radius:50%;width:3px;height:3px;position:absolute;top:50%;transform:translate(-50%,-50%)}._1n94osf1a{box-sizing:border-box;border:1px solid var(--rc-border);background-color:var(--rc-bg-secondary);width:52px;height:26px;color:var(--rc-text);font-size:var(--rc-font-size-xs);font-variant-numeric:tabular-nums;text-align:center;border-radius:6px;justify-content:center;align-items:center;line-height:24px;display:inline-flex;overflow:hidden}._1n94osf1b{-webkit-appearance:none;appearance:none;-moz-appearance:textfield;outline:none;min-width:0;padding:0}._1n94osf1b:focus{border-color:var(--rc-text-secondary)}._1n94osf1c{box-sizing:border-box;text-align:right;width:48px;font-size:var(--rc-font-size-xs);font-variant-numeric:tabular-nums;color:var(--rc-text-tertiary)}._1n94osf1d{height:26px;color:var(--rc-text-secondary);font-size:var(--rc-font-size-xs);cursor:pointer;background:0 0;border:none;border-radius:6px;justify-content:center;align-items:center;padding:0 8px;font-weight:500;transition:background-color .15s,color .15s;display:inline-flex}._1n94osf1d:hover,._1n94osf1e{color:var(--rc-text);background-color:var(--rc-fill-secondary)}@media (max-width:640px){._1n94osf0[data-layout=float-left],._1n94osf0[data-layout=float-right]{float:none;width:auto;margin:1.25rem auto}}
2
2
  /*$vite$:1*/
package/dist/static.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as ImageRenderer } from "./ImageRenderer-BUWdit_Y.js";
1
+ import { t as ImageRenderer } from "./ImageRenderer-B3TgOJEU.js";
2
2
  export { ImageRenderer };
@@ -66,10 +66,15 @@ export declare const resizeHandleLeft: string;
66
66
  export declare const resizeHandleRight: string;
67
67
  export declare const resizeHandleVisible: string;
68
68
  export declare const controlPanel: string;
69
+ export declare const sizePanel: string;
70
+ export declare const sizePanelRow: string;
71
+ export declare const sizeReadout: string;
69
72
  export declare const sizeSliderWrap: string;
70
73
  export declare const sizeSlider: string;
71
74
  export declare const sizeSliderTick: string;
72
- export declare const sizeValue: string;
75
+ export declare const sizeValueField: string;
76
+ export declare const sizePxInput: string;
77
+ export declare const sizeEquiv: string;
73
78
  export declare const sizeOption: string;
74
79
  export declare const editToolbarButtonActive: string;
75
80
  //# sourceMappingURL=styles.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;CAwBrB,CAAC;AAIX,eAAO,MAAM,IAAI,QAsCf,CAAC;AAWH,eAAO,MAAM,aAAa,QAExB,CAAC;AAyBH,eAAO,MAAM,KAAK,QAOhB,CAAC;AAEH,eAAO,MAAM,UAAU,gDAIrB,CAAC;AAEH,eAAO,MAAM,YAAY,QAGvB,CAAC;AAEH,eAAO,MAAM,KAAK,QAehB,CAAC;AAEH,eAAO,MAAM,aAAa,QAOxB,CAAC;AAEH,eAAO,MAAM,WAAW,QAOtB,CAAC;AAIH,eAAO,MAAM,MAAM,QAQjB,CAAC;AAEH,eAAO,MAAM,UAAU,QAerB,CAAC;AAEH,eAAO,MAAM,OAAO,QAKlB,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;CAO5B,CAAC;AAgCX,eAAO,MAAM,WAAW,QAA2B,CAAC;AACpD,eAAO,MAAM,gBAAgB,QAAgC,CAAC;AAC9D,eAAO,MAAM,aAAa,QAA6B,CAAC;AACxD,eAAO,MAAM,cAAc,QAA8B,CAAC;AA0B1D,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAClE,eAAO,MAAM,gBAAgB,QAAgC,CAAC;AAU9D,eAAO,MAAM,WAAW,QAItB,CAAC;AAEH,eAAO,MAAM,eAAe,QAoB1B,CAAC;AAEH,eAAO,MAAM,WAAW,QAiBtB,CAAC;AAEH,eAAO,MAAM,kBAAkB,QAG7B,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAkB5B,CAAC;AAEH,eAAO,MAAM,uBAAuB,QAOlC,CAAC;AAEH,eAAO,MAAM,SAAS,QAUpB,CAAC;AAEH,eAAO,MAAM,SAAS,QAQpB,CAAC;AAEH,eAAO,MAAM,aAAa,QAGxB,CAAC;AAEH,eAAO,MAAM,SAAS,QAepB,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAmB5B,CAAC;AAEH,eAAO,MAAM,cAAc,QAKzB,CAAC;AASH,eAAO,MAAM,SAAS,QAMpB,CAAC;AAEH,eAAO,MAAM,YAAY,QAgBvB,CAAC;AAEH,eAAO,MAAM,gBAAgB,QAAqB,CAAC;AACnD,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AAErD,eAAO,MAAM,mBAAmB,QAE9B,CAAC;AAEH,eAAO,MAAM,YAAY,QAOvB,CAAC;AAQH,eAAO,MAAM,cAAc,QAMzB,CAAC;AAaH,eAAO,MAAM,UAAU,QAwBrB,CAAC;AAEH,eAAO,MAAM,cAAc,QASzB,CAAC;AAEH,eAAO,MAAM,SAAS,QAMpB,CAAC;AAEH,eAAO,MAAM,UAAU,QAoBrB,CAAC;AAEH,eAAO,MAAM,uBAAuB,QAGlC,CAAC"}
1
+ {"version":3,"file":"styles.css.d.ts","sourceRoot":"","sources":["../src/styles.css.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;CAwBrB,CAAC;AAIX,eAAO,MAAM,IAAI,QA8Cf,CAAC;AAWH,eAAO,MAAM,aAAa,QAExB,CAAC;AAyBH,eAAO,MAAM,KAAK,QAOhB,CAAC;AAEH,eAAO,MAAM,UAAU,gDAIrB,CAAC;AAEH,eAAO,MAAM,YAAY,QAGvB,CAAC;AAEH,eAAO,MAAM,KAAK,QAehB,CAAC;AAEH,eAAO,MAAM,aAAa,QAOxB,CAAC;AAEH,eAAO,MAAM,WAAW,QAOtB,CAAC;AAiBH,eAAO,MAAM,MAAM,QAQjB,CAAC;AAEH,eAAO,MAAM,UAAU,QAerB,CAAC;AAEH,eAAO,MAAM,OAAO,QAKlB,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;CAO5B,CAAC;AAgCX,eAAO,MAAM,WAAW,QAA2B,CAAC;AACpD,eAAO,MAAM,gBAAgB,QAAgC,CAAC;AAC9D,eAAO,MAAM,aAAa,QAA6B,CAAC;AACxD,eAAO,MAAM,cAAc,QAA8B,CAAC;AA0B1D,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AAClE,eAAO,MAAM,gBAAgB,QAAgC,CAAC;AAU9D,eAAO,MAAM,WAAW,QAItB,CAAC;AAEH,eAAO,MAAM,eAAe,QAoB1B,CAAC;AAEH,eAAO,MAAM,WAAW,QAiBtB,CAAC;AAEH,eAAO,MAAM,kBAAkB,QAG7B,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAkB5B,CAAC;AAEH,eAAO,MAAM,uBAAuB,QAOlC,CAAC;AAEH,eAAO,MAAM,SAAS,QAUpB,CAAC;AAEH,eAAO,MAAM,SAAS,QAQpB,CAAC;AAEH,eAAO,MAAM,aAAa,QAGxB,CAAC;AAEH,eAAO,MAAM,SAAS,QAepB,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAmB5B,CAAC;AAEH,eAAO,MAAM,cAAc,QAKzB,CAAC;AASH,eAAO,MAAM,SAAS,QAMpB,CAAC;AAEH,eAAO,MAAM,YAAY,QAgBvB,CAAC;AAEH,eAAO,MAAM,gBAAgB,QAAqB,CAAC;AACnD,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AAErD,eAAO,MAAM,mBAAmB,QAE9B,CAAC;AAEH,eAAO,MAAM,YAAY,QAOvB,CAAC;AAQH,eAAO,MAAM,SAAS,QAQpB,CAAC;AAMH,eAAO,MAAM,YAAY,QAKvB,CAAC;AAEH,eAAO,MAAM,WAAW,QAOtB,CAAC;AAEH,eAAO,MAAM,cAAc,QAMzB,CAAC;AAaH,eAAO,MAAM,UAAU,QAwBrB,CAAC;AAEH,eAAO,MAAM,cAAc,QASzB,CAAC;AAEH,eAAO,MAAM,cAAc,QAgBzB,CAAC;AAEH,eAAO,MAAM,WAAW,QAWtB,CAAC;AAEH,eAAO,MAAM,SAAS,QAOpB,CAAC;AAEH,eAAO,MAAM,UAAU,QAoBrB,CAAC;AAEH,eAAO,MAAM,uBAAuB,QAGlC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { ImageLayout } from '@haklex/rich-editor/nodes';
1
+ import { ImageDisplaySize, ImageLayout } from '@haklex/rich-editor/nodes';
2
2
  export declare function useImageActions(): {
3
3
  commitMeta: () => void;
4
4
  closeReplacePanel: () => void;
@@ -10,7 +10,7 @@ export declare function useImageActions(): {
10
10
  handleDuplicate: () => void;
11
11
  handleDownload: () => void;
12
12
  handleDelete: () => void;
13
- handleSetDisplayWidth: (width?: number) => void;
13
+ handleSetDisplaySize: (size: ImageDisplaySize) => void;
14
14
  handleSetLayout: (layout?: ImageLayout) => void;
15
15
  };
16
16
  //# sourceMappingURL=useImageActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useImageActions.d.ts","sourceRoot":"","sources":["../src/useImageActions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAqD7D,wBAAgB,eAAe;;;8BAwGd,IAAI,GAAG,IAAI;;;wCA6Db,OAAO;;;;;oCA/ET,MAAM;+BASL,WAAW;EA2FxB"}
1
+ {"version":3,"file":"useImageActions.d.ts","sourceRoot":"","sources":["../src/useImageActions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAqD/E,wBAAgB,eAAe;;;8BA0Gd,IAAI,GAAG,IAAI;;;wCA6Db,OAAO;;;;;iCA/EX,gBAAgB;+BASb,WAAW;EA2FxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useImageResize.d.ts","sourceRoot":"","sources":["../src/useImageResize.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAI1C,wBAAgB,cAAc;+BAQlB,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,QAAQ,UAAU;EAqF5D"}
1
+ {"version":3,"file":"useImageResize.d.ts","sourceRoot":"","sources":["../src/useImageResize.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAI1C,wBAAgB,cAAc;+BAQlB,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,QAAQ,UAAU;EA+G5D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haklex/rich-renderer-image",
3
- "version": "0.35.0",
3
+ "version": "0.36.0",
4
4
  "description": "Image renderer with blurhash",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  "react-dom": "^19.2.8",
37
37
  "typescript": "^7.0.2",
38
38
  "unplugin-dts": "^1.0.3",
39
- "vite": "^8.2.0"
39
+ "vite": "^8.2.1"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@lexical/react": "^0.49.0",
@@ -45,9 +45,9 @@
45
45
  "lucide-react": "^1.0.0",
46
46
  "react": ">=19",
47
47
  "react-dom": ">=19",
48
- "@haklex/rich-editor": "^0.35.0",
49
- "@haklex/rich-editor-ui": "^0.35.0",
50
- "@haklex/rich-style-token": "^0.35.0"
48
+ "@haklex/rich-editor": "^0.36.0",
49
+ "@haklex/rich-style-token": "^0.36.0",
50
+ "@haklex/rich-editor-ui": "^0.36.0"
51
51
  },
52
52
  "publishConfig": {
53
53
  "access": "public"