ritext 1.0.19 → 1.0.25

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.
Files changed (133) hide show
  1. package/LICENSE +57 -0
  2. package/README.md +15 -41
  3. package/dist/chunk-EN4BPI7N.mjs +1213 -0
  4. package/dist/chunk-FJQ4FZRS.mjs +98 -0
  5. package/dist/chunk-M4ORWR74.mjs +31 -0
  6. package/dist/chunk-SJFT7WNM.mjs +38 -0
  7. package/dist/chunk-W367UGID.mjs +55 -0
  8. package/dist/{chunk-FWCSY2DS.mjs → chunk-WNQUEZJF.mjs} +22 -1
  9. package/dist/{chunk-D47XME55.mjs → chunk-WXCHG3NQ.mjs} +8 -2
  10. package/dist/chunk-WZKGD3DD.mjs +231 -0
  11. package/dist/extension/backgroundcolor.d.mts +16 -0
  12. package/dist/extension/backgroundcolor.d.ts +16 -0
  13. package/dist/extension/backgroundcolor.js +668 -0
  14. package/dist/extension/backgroundcolor.mjs +107 -0
  15. package/dist/extension/base.d.mts +6 -0
  16. package/dist/extension/base.d.ts +6 -0
  17. package/dist/extension/base.js +65 -0
  18. package/dist/extension/base.mjs +22 -0
  19. package/dist/extension/blockquote.d.mts +10 -0
  20. package/dist/extension/blockquote.d.ts +10 -0
  21. package/dist/extension/blockquote.js +239 -0
  22. package/dist/extension/blockquote.mjs +46 -0
  23. package/dist/extension/bold.d.mts +7 -2
  24. package/dist/extension/bold.d.ts +7 -2
  25. package/dist/extension/bold.js +22 -45
  26. package/dist/extension/bold.mjs +13 -35
  27. package/dist/extension/bulletlist.d.mts +10 -0
  28. package/dist/extension/bulletlist.d.ts +10 -0
  29. package/dist/extension/bulletlist.js +241 -0
  30. package/dist/extension/bulletlist.mjs +46 -0
  31. package/dist/extension/clearformat.d.mts +1 -1
  32. package/dist/extension/clearformat.d.ts +1 -1
  33. package/dist/extension/clearformat.js +11 -6
  34. package/dist/extension/clearformat.mjs +8 -35
  35. package/dist/extension/color.d.mts +17 -0
  36. package/dist/extension/color.d.ts +17 -0
  37. package/dist/extension/color.js +580 -0
  38. package/dist/extension/color.mjs +12 -0
  39. package/dist/extension/emoji.d.mts +15 -0
  40. package/dist/extension/emoji.d.ts +15 -0
  41. package/dist/extension/emoji.js +421 -0
  42. package/dist/extension/emoji.mjs +158 -0
  43. package/dist/extension/font-family.d.mts +8 -11
  44. package/dist/extension/font-family.d.ts +8 -11
  45. package/dist/extension/font-family.js +11 -12
  46. package/dist/extension/font-family.mjs +10 -11
  47. package/dist/extension/font-size.d.mts +8 -11
  48. package/dist/extension/font-size.d.ts +8 -11
  49. package/dist/extension/font-size.js +10 -12
  50. package/dist/extension/font-size.mjs +9 -11
  51. package/dist/extension/heading.d.mts +10 -10
  52. package/dist/extension/heading.d.ts +10 -10
  53. package/dist/extension/heading.js +68 -80
  54. package/dist/extension/heading.mjs +40 -206
  55. package/dist/extension/history.d.mts +4 -2
  56. package/dist/extension/history.d.ts +4 -2
  57. package/dist/extension/history.js +16 -9
  58. package/dist/extension/history.mjs +16 -64
  59. package/dist/extension/horizontalrule.d.mts +10 -0
  60. package/dist/extension/horizontalrule.d.ts +10 -0
  61. package/dist/extension/horizontalrule.js +242 -0
  62. package/dist/extension/horizontalrule.mjs +42 -0
  63. package/dist/extension/image.d.mts +26 -0
  64. package/dist/extension/image.d.ts +26 -0
  65. package/dist/extension/image.js +1611 -0
  66. package/dist/extension/image.mjs +1153 -0
  67. package/dist/extension/indentoutdent.d.mts +21 -0
  68. package/dist/extension/indentoutdent.d.ts +21 -0
  69. package/dist/extension/indentoutdent.js +380 -0
  70. package/dist/extension/indentoutdent.mjs +164 -0
  71. package/dist/extension/italic.d.mts +7 -2
  72. package/dist/extension/italic.d.ts +7 -2
  73. package/dist/extension/italic.js +22 -45
  74. package/dist/extension/italic.mjs +14 -40
  75. package/dist/extension/lineheight.d.mts +15 -0
  76. package/dist/extension/lineheight.d.ts +15 -0
  77. package/dist/extension/lineheight.js +342 -0
  78. package/dist/extension/lineheight.mjs +87 -0
  79. package/dist/extension/link.d.mts +15 -0
  80. package/dist/extension/link.d.ts +15 -0
  81. package/dist/extension/link.js +588 -0
  82. package/dist/extension/link.mjs +278 -0
  83. package/dist/extension/orderedlist.d.mts +10 -0
  84. package/dist/extension/orderedlist.d.ts +10 -0
  85. package/dist/extension/orderedlist.js +241 -0
  86. package/dist/extension/orderedlist.mjs +46 -0
  87. package/dist/extension/strike.d.mts +8 -3
  88. package/dist/extension/strike.d.ts +8 -3
  89. package/dist/extension/strike.js +22 -45
  90. package/dist/extension/strike.mjs +14 -40
  91. package/dist/extension/subandsuperscript.d.mts +7 -10
  92. package/dist/extension/subandsuperscript.d.ts +7 -10
  93. package/dist/extension/subandsuperscript.js +66 -77
  94. package/dist/extension/subandsuperscript.mjs +18 -48
  95. package/dist/extension/subscript.d.mts +4 -2
  96. package/dist/extension/subscript.d.ts +4 -2
  97. package/dist/extension/subscript.js +20 -45
  98. package/dist/extension/subscript.mjs +10 -10
  99. package/dist/extension/superscript.d.mts +4 -2
  100. package/dist/extension/superscript.d.ts +4 -2
  101. package/dist/extension/superscript.js +20 -45
  102. package/dist/extension/superscript.mjs +10 -10
  103. package/dist/extension/table.d.mts +12 -0
  104. package/dist/extension/table.d.ts +12 -0
  105. package/dist/extension/table.js +585 -0
  106. package/dist/extension/table.mjs +324 -0
  107. package/dist/extension/tasklist.d.mts +10 -0
  108. package/dist/extension/tasklist.d.ts +10 -0
  109. package/dist/extension/tasklist.js +246 -0
  110. package/dist/extension/tasklist.mjs +52 -0
  111. package/dist/extension/textalign.d.mts +15 -0
  112. package/dist/extension/textalign.d.ts +15 -0
  113. package/dist/extension/textalign.js +404 -0
  114. package/dist/extension/textalign.mjs +79 -0
  115. package/dist/extension/underline.d.mts +7 -2
  116. package/dist/extension/underline.d.ts +7 -2
  117. package/dist/extension/underline.js +22 -45
  118. package/dist/extension/underline.mjs +14 -39
  119. package/dist/index.css +683 -31
  120. package/dist/index.d.mts +3 -1
  121. package/dist/index.d.ts +3 -1
  122. package/dist/index.js +306 -24
  123. package/dist/index.mjs +141 -13
  124. package/dist/tiptap-ext.type-B3TtT7-J.d.mts +43 -0
  125. package/dist/tiptap-ext.type-B3TtT7-J.d.ts +43 -0
  126. package/package.json +41 -28
  127. package/dist/chunk-2YUUKIIT.mjs +0 -149
  128. package/dist/chunk-7QOFJIQ3.mjs +0 -90
  129. package/dist/chunk-GSHINFPO.mjs +0 -53
  130. package/dist/chunk-LMOTHRGQ.mjs +0 -37
  131. package/dist/chunk-MBF77NHS.mjs +0 -37
  132. package/dist/tiptap-ext.type-DrneAC5G.d.mts +0 -39
  133. package/dist/tiptap-ext.type-DrneAC5G.d.ts +0 -39
@@ -0,0 +1,1153 @@
1
+ import {
2
+ CheckboxComponent_default,
3
+ InputComponent_default
4
+ } from "../chunk-W367UGID.mjs";
5
+ import {
6
+ ButtonWithoutActive_default
7
+ } from "../chunk-WXCHG3NQ.mjs";
8
+ import {
9
+ AlignCenter,
10
+ AlignLeft,
11
+ AlignRight,
12
+ CloseIcon,
13
+ FlipHorizontal,
14
+ FlipVertical,
15
+ ImageIcon,
16
+ ImageUpIcon,
17
+ Tooltip_default,
18
+ TrashIcon
19
+ } from "../chunk-EN4BPI7N.mjs";
20
+ import {
21
+ __async,
22
+ __objRest,
23
+ __spreadProps,
24
+ __spreadValues
25
+ } from "../chunk-WNQUEZJF.mjs";
26
+
27
+ // src/extension/Image.tsx
28
+ import { Fragment as Fragment5, useState as useState6 } from "react";
29
+ import { Image as TiptapImage } from "@tiptap/extension-image";
30
+ import { mergeAttributes } from "@tiptap/react";
31
+ import { ReactNodeViewRenderer } from "@tiptap/react";
32
+
33
+ // src/lib/components/ImageDialog.tsx
34
+ import { useState as useState4 } from "react";
35
+
36
+ // src/lib/components/_com/Dialog.tsx
37
+ import { m, AnimatePresence, domAnimation, LazyMotion } from "motion/react";
38
+ import { createPortal } from "react-dom";
39
+ import { twMerge } from "tailwind-merge";
40
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
41
+ var Dialog = ({ open, onClose, children, backdropClassName, className }) => {
42
+ const animation = {
43
+ unmount: {
44
+ opacity: 0,
45
+ y: -8,
46
+ transition: {
47
+ duration: 0.15
48
+ }
49
+ },
50
+ mount: {
51
+ opacity: 1,
52
+ y: 0,
53
+ transition: {
54
+ duration: 0.15
55
+ }
56
+ }
57
+ };
58
+ const backdropAnimation = {
59
+ unmount: {
60
+ opacity: 0,
61
+ transition: {
62
+ delay: 0.1
63
+ }
64
+ },
65
+ mount: {
66
+ opacity: 1
67
+ }
68
+ };
69
+ if (typeof window === "undefined") return null;
70
+ return createPortal(
71
+ /* @__PURE__ */ jsx(LazyMotion, { features: domAnimation, children: /* @__PURE__ */ jsx(AnimatePresence, { children: open && /* @__PURE__ */ jsxs(Fragment, { children: [
72
+ /* @__PURE__ */ jsx(
73
+ m.div,
74
+ {
75
+ className: `bg-black/55 fixed top-0 left-0 w-full h-full flex justify-center items-center z-9999999 ${backdropClassName}`,
76
+ initial: "unmount",
77
+ exit: "unmount",
78
+ animate: open ? "mount" : "unmount",
79
+ variants: backdropAnimation,
80
+ transition: { duration: 0.2 },
81
+ onClick: onClose
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsx(
85
+ m.div,
86
+ {
87
+ className: twMerge("fixed inset-0 h-max m-auto z-9999999 overflow-auto bg-white rounded-2xl", className),
88
+ initial: "unmount",
89
+ exit: "unmount",
90
+ animate: open ? "mount" : "unmount",
91
+ variants: animation,
92
+ children
93
+ }
94
+ )
95
+ ] }) }) }),
96
+ document.body
97
+ );
98
+ };
99
+ var Header = ({ title, className, titleClassName, buttonClassName, onClose }) => {
100
+ return /* @__PURE__ */ jsxs("div", { className: twMerge("flex items-center gap-4 text-strong", className), children: [
101
+ /* @__PURE__ */ jsx("h4", { className: twMerge("text-xl flex-1 font-semibold", titleClassName), children: title }),
102
+ onClose && /* @__PURE__ */ jsx("button", { className: twMerge("hover:bg-light p-1 rounded-md transition-all", buttonClassName), onClick: onClose, children: /* @__PURE__ */ jsx(CloseIcon, { size: 20 }) })
103
+ ] });
104
+ };
105
+ var Body = ({ className, children, id }) => {
106
+ return /* @__PURE__ */ jsx("div", { className: twMerge("max-h-[80vh] overflow-auto [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-gray-200 [&::-webkit-scrollbar-thumb]:rounded-full", className), id, children });
107
+ };
108
+ Dialog.Header = Header;
109
+ Dialog.Body = Body;
110
+ var Dialog_default = Dialog;
111
+
112
+ // src/lib/components/_com/ImageUploader.tsx
113
+ import { Fragment as Fragment3, useState as useState3 } from "react";
114
+
115
+ // src/lib/components/_root/image/ImageUpload.tsx
116
+ import { useRef, useState, useCallback } from "react";
117
+
118
+ // src/lib/components/_root/image/typings.ts
119
+ var ImageAcceptType = /* @__PURE__ */ ((ImageAcceptType2) => {
120
+ ImageAcceptType2["ALL"] = "all";
121
+ ImageAcceptType2["SVG"] = "image/svg+xml";
122
+ ImageAcceptType2["PNG"] = "image/png";
123
+ ImageAcceptType2["JPEG"] = "image/jpeg";
124
+ ImageAcceptType2["GIF"] = "image/gif";
125
+ ImageAcceptType2["BMP"] = "image/bmp";
126
+ ImageAcceptType2["WEBP"] = "image/webp";
127
+ ImageAcceptType2["TIFF"] = "image/tiff";
128
+ ImageAcceptType2["ICO"] = "image/x-icon";
129
+ ImageAcceptType2["HEIC"] = "image/heic";
130
+ ImageAcceptType2["HEIF"] = "image/heif";
131
+ return ImageAcceptType2;
132
+ })(ImageAcceptType || {});
133
+
134
+ // src/lib/components/_root/image/utils.ts
135
+ var openFileDialog = (inputRef) => {
136
+ if (inputRef.current) inputRef.current.click();
137
+ };
138
+ var getImageAccepts = (acceptType) => {
139
+ if (!acceptType || acceptType === "all" /* ALL */) {
140
+ const defaultArray = Object.values(ImageAcceptType).filter((value) => value !== "all" /* ALL */);
141
+ return defaultArray;
142
+ } else {
143
+ return acceptType;
144
+ }
145
+ };
146
+ var getImage = (file) => {
147
+ const image = new Image();
148
+ return new Promise((resolve) => {
149
+ image.addEventListener("load", () => resolve(image));
150
+ image.src = URL.createObjectURL(file);
151
+ });
152
+ };
153
+ var getBase64 = (file) => {
154
+ const reader = new FileReader();
155
+ return new Promise((resolve) => {
156
+ reader.addEventListener("load", () => resolve(String(reader.result)));
157
+ reader.readAsDataURL(file);
158
+ });
159
+ };
160
+ var isResolutionValid = (image, resolutionType, resolutionWidth, resolutionHeight) => {
161
+ if (!resolutionWidth || !resolutionHeight || !image.width || !image.height)
162
+ return true;
163
+ switch (resolutionType) {
164
+ case "absolute": {
165
+ if (image.width === resolutionWidth && image.height === resolutionHeight)
166
+ return true;
167
+ break;
168
+ }
169
+ case "ratio": {
170
+ const ratio = resolutionWidth / resolutionHeight;
171
+ if (image.width / image.height === ratio) return true;
172
+ break;
173
+ }
174
+ case "less": {
175
+ if (image.width <= resolutionWidth && image.height <= resolutionHeight)
176
+ return true;
177
+ break;
178
+ }
179
+ case "more": {
180
+ if (image.width >= resolutionWidth && image.height >= resolutionHeight)
181
+ return true;
182
+ break;
183
+ }
184
+ default:
185
+ break;
186
+ }
187
+ return false;
188
+ };
189
+ var createImage = (url) => new Promise((resolve, reject) => {
190
+ const image = new Image();
191
+ image.addEventListener("load", () => resolve(image));
192
+ image.addEventListener("error", (error) => reject(error));
193
+ image.src = url;
194
+ });
195
+ function getRadianAngle(degreeValue) {
196
+ return degreeValue * Math.PI / 180;
197
+ }
198
+ function rotateSize(width, height, rotation) {
199
+ const rotRad = getRadianAngle(rotation);
200
+ return {
201
+ width: Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
202
+ height: Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height)
203
+ };
204
+ }
205
+ function getCroppedImg(_0, _1) {
206
+ return __async(this, arguments, function* (imageSrc, pixelCrop, rotation = 0, flip2 = { horizontal: false, vertical: false }) {
207
+ const image = yield createImage(imageSrc);
208
+ const canvas = document.createElement("canvas");
209
+ const ctx = canvas.getContext("2d");
210
+ if (!ctx || !pixelCrop) {
211
+ return null;
212
+ }
213
+ const rotRad = getRadianAngle(rotation);
214
+ const { width: bBoxWidth, height: bBoxHeight } = rotateSize(
215
+ image.width,
216
+ image.height,
217
+ rotation
218
+ );
219
+ canvas.width = bBoxWidth;
220
+ canvas.height = bBoxHeight;
221
+ ctx.translate(bBoxWidth / 2, bBoxHeight / 2);
222
+ ctx.rotate(rotRad);
223
+ ctx.scale(flip2.horizontal ? -1 : 1, flip2.vertical ? -1 : 1);
224
+ ctx.translate(-image.width / 2, -image.height / 2);
225
+ ctx.drawImage(image, 0, 0);
226
+ const croppedCanvas = document.createElement("canvas");
227
+ const croppedCtx = croppedCanvas.getContext("2d");
228
+ if (!croppedCtx) {
229
+ return null;
230
+ }
231
+ croppedCanvas.width = pixelCrop.width;
232
+ croppedCanvas.height = pixelCrop.height;
233
+ croppedCtx.drawImage(
234
+ canvas,
235
+ pixelCrop.x,
236
+ pixelCrop.y,
237
+ pixelCrop.width,
238
+ pixelCrop.height,
239
+ 0,
240
+ 0,
241
+ pixelCrop.width,
242
+ pixelCrop.height
243
+ );
244
+ const blob = yield new Promise(
245
+ (resolve) => croppedCanvas.toBlob((b) => resolve(b), "image/png")
246
+ );
247
+ return {
248
+ file: new File([blob], `${crypto.randomUUID()}.png`, { type: "image/png" }),
249
+ dataURL: croppedCanvas.toDataURL("image/png")
250
+ };
251
+ });
252
+ }
253
+
254
+ // src/lib/components/_root/image/ImageUpload.tsx
255
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
256
+ var ImageUpload = ({ inputProps, acceptType, maxFileSize, resolutionWidth, resolutionHeight, resolutionType = "absolute", children, onChange, value, onError }) => {
257
+ if (Array.isArray(acceptType) && acceptType.includes("all" /* ALL */)) {
258
+ throw new Error("Invalid accept type: ImageAcceptType.All cannot be included in an array.");
259
+ }
260
+ const inputRef = useRef(null);
261
+ const [isDragging, setIsDragging] = useState(false);
262
+ const [errors, setErrors] = useState({ acceptType: false, maxFileSize: false, resolution: false });
263
+ const handleChange = (files) => __async(null, null, function* () {
264
+ if (!files) return null;
265
+ const file = files[0];
266
+ if (!file) return null;
267
+ const newErrors = {
268
+ acceptType: false,
269
+ maxFileSize: false,
270
+ resolution: false
271
+ };
272
+ const uploadSize = Number((file.size / 1024).toFixed(2));
273
+ if (maxFileSize && uploadSize > maxFileSize) newErrors.maxFileSize = true;
274
+ if (!getImageAccepts(acceptType).includes(file.type)) newErrors.acceptType = true;
275
+ const image = yield getImage(file);
276
+ const checkRes = isResolutionValid(image, resolutionType, resolutionWidth, resolutionHeight);
277
+ if (!checkRes) newErrors.resolution = true;
278
+ setErrors(newErrors);
279
+ if (newErrors.acceptType || newErrors.maxFileSize || newErrors.resolution) {
280
+ onError == null ? void 0 : onError(newErrors);
281
+ return;
282
+ }
283
+ const dataUrl = yield getBase64(file);
284
+ onChange({
285
+ file,
286
+ dataURL: dataUrl
287
+ });
288
+ });
289
+ const onImageRemove = () => {
290
+ onChange(null);
291
+ };
292
+ const onInputChange = (e) => __async(null, null, function* () {
293
+ yield handleChange(e.target.files);
294
+ if (inputRef.current) inputRef.current.value = "";
295
+ });
296
+ const handleClickInput = useCallback(() => openFileDialog(inputRef), [
297
+ inputRef
298
+ ]);
299
+ const onImageUpload = useCallback(() => {
300
+ handleClickInput();
301
+ }, [handleClickInput]);
302
+ const handleDrag = (e) => {
303
+ e.preventDefault();
304
+ e.stopPropagation();
305
+ };
306
+ const handleDragIn = (e) => {
307
+ e.preventDefault();
308
+ e.stopPropagation();
309
+ if (e.dataTransfer.items && e.dataTransfer.items.length > 0) {
310
+ setIsDragging(true);
311
+ }
312
+ };
313
+ const handleDragOut = (e) => {
314
+ e.preventDefault();
315
+ e.stopPropagation();
316
+ setIsDragging(false);
317
+ };
318
+ const handleDrop = (e) => {
319
+ e.preventDefault();
320
+ e.stopPropagation();
321
+ setIsDragging(false);
322
+ if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
323
+ handleChange(e.dataTransfer.files);
324
+ }
325
+ };
326
+ const handleDragStart = (e) => {
327
+ e.preventDefault();
328
+ e.stopPropagation();
329
+ e.dataTransfer.clearData();
330
+ };
331
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
332
+ /* @__PURE__ */ jsx2(
333
+ "input",
334
+ __spreadValues({
335
+ type: "file",
336
+ style: { display: "none" },
337
+ accept: getImageAccepts(acceptType).join(","),
338
+ multiple: false,
339
+ onChange: onInputChange,
340
+ ref: inputRef
341
+ }, inputProps)
342
+ ),
343
+ children == null ? void 0 : children({
344
+ onImageUpload,
345
+ onImageRemove,
346
+ dragProps: {
347
+ onDrop: handleDrop,
348
+ onDragEnter: handleDragIn,
349
+ onDragLeave: handleDragOut,
350
+ onDragOver: handleDrag,
351
+ onDragStart: handleDragStart
352
+ },
353
+ isDragging,
354
+ errors,
355
+ imageInfo: value
356
+ })
357
+ ] });
358
+ };
359
+ var ImageUpload_default = ImageUpload;
360
+
361
+ // src/lib/components/_com/ImageUploader.tsx
362
+ import Cropper from "react-easy-crop";
363
+ import { AnimatePresence as AnimatePresence2, motion } from "motion/react";
364
+
365
+ // src/lib/components/_com/Loading.tsx
366
+ import { twMerge as twMerge2 } from "tailwind-merge";
367
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
368
+ var Loading = ({ size = 25, className }) => {
369
+ return /* @__PURE__ */ jsxs3("svg", { className: `animate-spin ${twMerge2("stroke-primary", className)}`, viewBox: "0 0 256 256", style: { width: size + "px", height: size + "px" }, children: [
370
+ /* @__PURE__ */ jsx3("line", { x1: "128", y1: "32", x2: "128", y2: "64", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" }),
371
+ /* @__PURE__ */ jsx3("line", { x1: "195.9", y1: "60.1", x2: "173.3", y2: "82.7", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" }),
372
+ /* @__PURE__ */ jsx3("line", { x1: "224", y1: "128", x2: "192", y2: "128", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" }),
373
+ /* @__PURE__ */ jsx3("line", { x1: "195.9", y1: "195.9", x2: "173.3", y2: "173.3", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" }),
374
+ /* @__PURE__ */ jsx3("line", { x1: "128", y1: "224", x2: "128", y2: "192", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" }),
375
+ /* @__PURE__ */ jsx3("line", { x1: "60.1", y1: "195.9", x2: "82.7", y2: "173.3", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" }),
376
+ /* @__PURE__ */ jsx3("line", { x1: "32", y1: "128", x2: "64", y2: "128", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" }),
377
+ /* @__PURE__ */ jsx3("line", { x1: "60.1", y1: "60.1", x2: "82.7", y2: "82.7", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "24" })
378
+ ] });
379
+ };
380
+ var Loading_default = Loading;
381
+
382
+ // src/lib/components/_com/RangeSlider.tsx
383
+ import { useRef as useRef2, useLayoutEffect, useState as useState2 } from "react";
384
+ import { twMerge as twMerge3 } from "tailwind-merge";
385
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
386
+ var RangeSlider = ({ min = 0, max = 100, step = 1, onChange, value, className = "", height = 4, trackColor = "#d7dbe0", showInput = true }) => {
387
+ const trackRef = useRef2(null);
388
+ const [trackWidth, setTrackWidth] = useState2(null);
389
+ const [inputValue, setInputValue] = useState2({ isFocus: false, value: value == null ? void 0 : value.toString() });
390
+ const percentage = (value - min) / (max - min) * 100;
391
+ const getThumbPosition = () => {
392
+ if (!trackWidth) return `${percentage}%`;
393
+ const thumbWidth = 18;
394
+ const pixelPosition = percentage / 100 * (trackWidth - thumbWidth) + thumbWidth / 2;
395
+ return `${pixelPosition / trackWidth * 100}%`;
396
+ };
397
+ const validateAndUpdateValue = () => {
398
+ let newValue = Number(inputValue.value);
399
+ if (isNaN(newValue)) {
400
+ setInputValue({ isFocus: false, value: value.toString() });
401
+ return;
402
+ }
403
+ if (newValue < min) newValue = min;
404
+ if (newValue > max) newValue = max;
405
+ if (step !== 1) {
406
+ const steps = Math.round((newValue - min) / step);
407
+ newValue = min + steps * step;
408
+ }
409
+ onChange(newValue);
410
+ setInputValue({ isFocus: false, value: newValue.toString() });
411
+ };
412
+ const handleKeyDown = (e) => {
413
+ if (e.key === "Enter") {
414
+ validateAndUpdateValue();
415
+ }
416
+ };
417
+ const handleBlur = () => {
418
+ validateAndUpdateValue();
419
+ };
420
+ const handleFocus = () => {
421
+ setInputValue({ isFocus: true, value: value.toString() });
422
+ };
423
+ useLayoutEffect(() => {
424
+ if (trackRef.current) {
425
+ setTrackWidth(trackRef.current.clientWidth);
426
+ }
427
+ }, []);
428
+ return /* @__PURE__ */ jsxs4("div", { className: twMerge3("relative flex items-center", className), children: [
429
+ /* @__PURE__ */ jsxs4("div", { ref: trackRef, className: "relative w-full rounded-full", style: { height: `${height}px`, background: trackColor }, children: [
430
+ /* @__PURE__ */ jsx4(
431
+ "input",
432
+ {
433
+ type: "range",
434
+ min,
435
+ max,
436
+ step,
437
+ value,
438
+ onChange: (e) => onChange(Number(e.target.value)),
439
+ className: "absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10 peer"
440
+ }
441
+ ),
442
+ /* @__PURE__ */ jsx4("div", { className: "absolute h-full rounded-full bg-gray-700", style: { width: `${percentage}%` } }),
443
+ /* @__PURE__ */ jsxs4("div", { className: "w-4.5 h-4.5 absolute top-1/2 -translate-1/2 group", style: { left: getThumbPosition() }, children: [
444
+ /* @__PURE__ */ jsx4("div", { className: "absolute w-full h-full bg-white rounded-full border border-solid border-gray-300 shadow-3xl z-99" }),
445
+ /* @__PURE__ */ jsx4("div", { className: "absolute bg-gray-200 -top-2.5 -left-2.5 -right-2.5 -bottom-2.5 rounded-full opacity-0 group-peer-active:opacity-100 z-9" })
446
+ ] })
447
+ ] }),
448
+ showInput && /* @__PURE__ */ jsx4(
449
+ "input",
450
+ {
451
+ type: "text",
452
+ value: inputValue.isFocus ? inputValue.value : value,
453
+ onChange: (e) => setInputValue((p) => __spreadProps(__spreadValues({}, p), { value: e.target.value })),
454
+ onKeyDown: handleKeyDown,
455
+ onBlur: handleBlur,
456
+ onFocus: handleFocus,
457
+ className: "ml-4 w-12 text-center border border-gray-200 rounded px-1 py-1.5 text-sm focus:outline-none focus:border-gray-400",
458
+ "aria-label": "Range value"
459
+ }
460
+ )
461
+ ] });
462
+ };
463
+ var RangeSlider_default = RangeSlider;
464
+
465
+ // src/lib/components/_com/ImageUploader.tsx
466
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
467
+ var ImageUploader = ({ editor, options, onClose }) => {
468
+ const [image, setImage] = useState3(null);
469
+ const [open, setOpen] = useState3(false);
470
+ const [crop, setCrop] = useState3({ x: 0, y: 0 });
471
+ const [zoom, setZoom] = useState3(1);
472
+ const [rotation, setRotation] = useState3(0);
473
+ const [alt, setAlt] = useState3("");
474
+ const [inline, setInline] = useState3(false);
475
+ const [flip2, setFlip] = useState3({ horizontal: false, vertical: false });
476
+ const [croppedAreaPixels, setCroppedAreaPixels] = useState3(null);
477
+ const [isUploading, setUploading] = useState3(false);
478
+ const onImageChange = (image2) => __async(null, null, function* () {
479
+ setImage(image2);
480
+ });
481
+ const reset = () => {
482
+ setOpen(false);
483
+ setCrop({ x: 0, y: 0 });
484
+ setZoom(1);
485
+ setRotation(0);
486
+ setFlip({ horizontal: false, vertical: false });
487
+ setCroppedAreaPixels(null);
488
+ };
489
+ const createCroppedImage = () => __async(null, null, function* () {
490
+ if (image == null ? void 0 : image.dataURL) {
491
+ const croppedImage = yield getCroppedImg(
492
+ image == null ? void 0 : image.dataURL,
493
+ croppedAreaPixels,
494
+ rotation
495
+ );
496
+ onImageChange(croppedImage);
497
+ reset();
498
+ }
499
+ });
500
+ const onInsertImage = () => __async(null, null, function* () {
501
+ if (!(image == null ? void 0 : image.dataURL)) return;
502
+ if (options == null ? void 0 : options.upload) {
503
+ if (!image.file) return;
504
+ setUploading(true);
505
+ const url = yield options.upload(image.file);
506
+ editor == null ? void 0 : editor.chain().focus().setImageWithInline({ src: url, alt, width: 300, inline }).run();
507
+ setUploading(false);
508
+ onClose == null ? void 0 : onClose();
509
+ } else {
510
+ editor == null ? void 0 : editor.chain().focus().setImageWithInline({ src: image.dataURL, alt, width: 300, inline }).run();
511
+ onClose == null ? void 0 : onClose();
512
+ }
513
+ });
514
+ return /* @__PURE__ */ jsxs5("div", { className: "mt-3", children: [
515
+ /* @__PURE__ */ jsx5(
516
+ ImageUpload_default,
517
+ {
518
+ onChange: onImageChange,
519
+ value: image,
520
+ maxFileSize: options == null ? void 0 : options.maxSize,
521
+ children: ({
522
+ isDragging,
523
+ dragProps,
524
+ onImageUpload,
525
+ errors
526
+ }) => /* @__PURE__ */ jsxs5("div", { className: "relative", children: [
527
+ /* @__PURE__ */ jsx5("div", { className: `border bg-gray-50 overflow-hidden border-dashed w-full pt-7 pb-4 rounded-xl relative ${isDragging ? "border-green-600" : "border-gray-300"}`, children: /* @__PURE__ */ jsx5("div", { className: "text-center px-4 flex items-center justify-center w-full h-full", children: /* @__PURE__ */ jsxs5("div", { children: [
528
+ /* @__PURE__ */ jsx5("div", __spreadProps(__spreadValues({}, dragProps), { children: !(image == null ? void 0 : image.dataURL) ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
529
+ /* @__PURE__ */ jsx5(ImageIcon, { className: "mx-auto", size: 60, strokeWidth: 1.5 }),
530
+ /* @__PURE__ */ jsx5("p", { className: "mt-4 text-base", children: "Drag and drop an image here, or click following button to upload image." })
531
+ ] }) : /* @__PURE__ */ jsx5("div", { className: "relative w-full h-full", children: /* @__PURE__ */ jsx5("img", { src: image.dataURL, alt: "Selected Image", className: "object-contain w-full h-full" }) }) })),
532
+ /* @__PURE__ */ jsxs5("div", { className: "flex gap-x-2 mt-4", children: [
533
+ /* @__PURE__ */ jsx5("button", { className: "flex-1 cursor-pointer text-center bg-white border rounded-lg border-gray-200 py-2 text-sm px-2", onClick: onImageUpload, children: "Upload Image" }),
534
+ /* @__PURE__ */ jsx5("button", { className: "flex-1 rounded-lg text-center bg-white cursor-pointer border border-gray-200 py-2 text-sm px-2", onClick: () => (onImageUpload(), setOpen(true)), children: "Upload & Crop" })
535
+ ] })
536
+ ] }) }) }),
537
+ errors && /* @__PURE__ */ jsxs5("ul", { className: "mt-2 ml-1", children: [
538
+ errors.maxFileSize && /* @__PURE__ */ jsxs5("li", { className: "flex text-sm text-error items-center gap-1", children: [
539
+ /* @__PURE__ */ jsx5("div", { className: "w-1 h-1 rounded-full bg-error" }),
540
+ " ",
541
+ /* @__PURE__ */ jsx5("p", { children: "File size exceeds 5MB limit." })
542
+ ] }),
543
+ errors.acceptType && /* @__PURE__ */ jsxs5("li", { className: "flex text-sm text-error items-center gap-1", children: [
544
+ /* @__PURE__ */ jsx5("div", { className: "w-1 h-1 rounded-full bg-error" }),
545
+ " ",
546
+ /* @__PURE__ */ jsx5("p", { children: "Only image files are allowed." })
547
+ ] })
548
+ ] }),
549
+ /* @__PURE__ */ jsx5(
550
+ CheckboxComponent_default,
551
+ {
552
+ id: "isInline",
553
+ checked: inline,
554
+ onChange: setInline,
555
+ label: "Insert as inline",
556
+ bottomSpace: false
557
+ }
558
+ ),
559
+ /* @__PURE__ */ jsx5(
560
+ InputComponent_default,
561
+ {
562
+ id: "Alt",
563
+ placeholder: "Alt text",
564
+ containerClassName: "mt-3",
565
+ value: alt,
566
+ onChange: (e) => setAlt(e.target.value)
567
+ }
568
+ ),
569
+ /* @__PURE__ */ jsxs5("button", { className: "bg-gray-700 py-2 px-6 rounded-lg text-white w-full mt-3 relative", onClick: onInsertImage, children: [
570
+ /* @__PURE__ */ jsx5("span", { className: `${isUploading && "opacity-15"}`, children: "Insert Image" }),
571
+ isUploading && /* @__PURE__ */ jsx5(Loading_default, { className: "absolute top-1/2 left-1/2 -translate-1/2 stroke-white", size: 24 })
572
+ ] })
573
+ ] })
574
+ }
575
+ ),
576
+ /* @__PURE__ */ jsx5(AnimatePresence2, { children: open && (image == null ? void 0 : image.dataURL) && /* @__PURE__ */ jsxs5(
577
+ motion.div,
578
+ {
579
+ className: "fixed top-0 left-0 w-full h-full bg-black z-99999 flex flex-col",
580
+ initial: { opacity: 0 },
581
+ animate: { opacity: 1 },
582
+ exit: { opacity: 0 },
583
+ transition: { duration: 0.2 },
584
+ children: [
585
+ /* @__PURE__ */ jsx5("button", { className: "absolute top-8 right-8 bg-white z-9999999 text-black rounded-md p-1", onClick: () => (reset(), setImage(null)), children: /* @__PURE__ */ jsx5(CloseIcon, {}) }),
586
+ /* @__PURE__ */ jsx5("div", { className: "w-full flex-1 relative h-full", children: /* @__PURE__ */ jsx5(
587
+ Cropper,
588
+ {
589
+ image: image == null ? void 0 : image.dataURL,
590
+ aspect: (options == null ? void 0 : options.aspect) || 1,
591
+ crop,
592
+ zoom,
593
+ rotation,
594
+ onCropChange: setCrop,
595
+ onZoomChange: setZoom,
596
+ onRotationChange: setRotation,
597
+ onCropComplete: (_, e) => setCroppedAreaPixels(e),
598
+ transform: [
599
+ `translate(${crop.x}px, ${crop.y}px)`,
600
+ `rotateY(${flip2.horizontal ? 180 : 0}deg)`,
601
+ `rotateX(${flip2.vertical ? 180 : 0}deg)`,
602
+ `rotateZ(${rotation}deg)`,
603
+ `scale(${zoom})`
604
+ ].join(" ")
605
+ }
606
+ ) }),
607
+ /* @__PURE__ */ jsxs5(
608
+ motion.div,
609
+ {
610
+ className: "z-999 relative bg-white py-9 px-20",
611
+ initial: { y: 16, opacity: 0 },
612
+ animate: { y: 0, opacity: 1 },
613
+ exit: { y: 16, opacity: 0 },
614
+ transition: { duration: 0.22, ease: [0.22, 1, 0.36, 1] },
615
+ children: [
616
+ /* @__PURE__ */ jsxs5("div", { className: "flex gap-x-10", children: [
617
+ /* @__PURE__ */ jsxs5("div", { className: "flex gap-x-6 flex-1 items-center", children: [
618
+ /* @__PURE__ */ jsx5("h4", { children: "ZOOM" }),
619
+ /* @__PURE__ */ jsx5("div", { className: "flex-1", children: /* @__PURE__ */ jsx5(RangeSlider_default, { value: zoom, min: 1, max: 3, onChange: setZoom, step: 1e-10 }) })
620
+ ] }),
621
+ /* @__PURE__ */ jsxs5("div", { className: "flex gap-x-6 flex-1 items-center", children: [
622
+ /* @__PURE__ */ jsx5("h4", { children: "ROTATION" }),
623
+ /* @__PURE__ */ jsx5("div", { className: "flex-1", children: /* @__PURE__ */ jsx5(RangeSlider_default, { value: rotation, min: 0, max: 360, step: 1, onChange: setRotation }) })
624
+ ] }),
625
+ /* @__PURE__ */ jsx5("button", { className: "hover:bg-light px-2 py-2 rounded-lg transition-all", type: "button", onClick: () => setFlip((prev) => ({ horizontal: prev.horizontal, vertical: !prev.vertical })), children: /* @__PURE__ */ jsx5(FlipVertical, {}) }),
626
+ /* @__PURE__ */ jsx5("button", { className: "hover:bg-light px-2 py-2 rounded-lg transition-all", type: "button", onClick: () => setFlip((prev) => ({ horizontal: !prev.horizontal, vertical: prev.vertical })), children: /* @__PURE__ */ jsx5(FlipHorizontal, {}) })
627
+ ] }),
628
+ /* @__PURE__ */ jsx5("div", { className: "mt-8 text-center", children: /* @__PURE__ */ jsx5("button", { type: "button", onClick: createCroppedImage, className: "bg-gray-700 py-2 px-6 rounded-lg text-white", children: "Crop & Done" }) })
629
+ ]
630
+ }
631
+ )
632
+ ]
633
+ }
634
+ ) })
635
+ ] });
636
+ };
637
+ var ImageUploader_default = ImageUploader;
638
+
639
+ // src/lib/components/ImageDialog.tsx
640
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
641
+ var ImageDialog = ({ open, onClose, editor, options }) => {
642
+ const [type, setType] = useState4("upload");
643
+ return /* @__PURE__ */ jsxs6(
644
+ Dialog_default,
645
+ {
646
+ open,
647
+ onClose,
648
+ className: "max-w-100 w-[calc(100vw-4px)]",
649
+ children: [
650
+ /* @__PURE__ */ jsx6(
651
+ Dialog_default.Header,
652
+ {
653
+ title: "Add an image",
654
+ onClose,
655
+ className: "px-3 py-3.5",
656
+ titleClassName: "text-lg font-medium"
657
+ }
658
+ ),
659
+ /* @__PURE__ */ jsxs6(Dialog_default.Body, { className: "px-3 pb-3 pl-3 pr-3", children: [
660
+ /* @__PURE__ */ jsxs6("div", { className: "flex bg-gray-100 p-1 rounded-lg", children: [
661
+ /* @__PURE__ */ jsx6("button", { className: `flex-1 py-2 text-sm text-center px-2 rounded-lg ${type === "upload" ? "bg-white" : ""}`, onClick: () => setType("upload"), children: "Upload" }),
662
+ /* @__PURE__ */ jsx6("button", { className: `flex-1 py-2 text-sm text-center px-2 rounded-lg ${type === "url" ? "bg-white" : ""}`, onClick: () => setType("url"), children: "URL" })
663
+ ] }),
664
+ /* @__PURE__ */ jsx6(
665
+ ImageUploader_default,
666
+ {
667
+ editor,
668
+ options,
669
+ onClose
670
+ }
671
+ )
672
+ ] })
673
+ ]
674
+ }
675
+ );
676
+ };
677
+ var ImageDialog_default = ImageDialog;
678
+
679
+ // src/lib/components/ImageView.tsx
680
+ import { useEffect, useLayoutEffect as useLayoutEffect2, useMemo, useRef as useRef3, useState as useState5 } from "react";
681
+ import { NodeViewWrapper } from "@tiptap/react";
682
+ import { useFloating, autoUpdate, offset, flip, shift, useDismiss, useInteractions } from "@floating-ui/react";
683
+ import { AnimatePresence as AnimatePresence3, motion as motion2 } from "motion/react";
684
+ import { NodeSelection, TextSelection } from "@tiptap/pm/state";
685
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
686
+ var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
687
+ var ImageView = (props) => {
688
+ var _a, _b;
689
+ const { node, selected, updateAttributes, deleteNode, editor, getPos } = props;
690
+ const rootRef = useRef3(null);
691
+ const boxRef = useRef3(null);
692
+ const imgRef = useRef3(null);
693
+ const floatingWrapRef = useRef3(null);
694
+ const [resizing, setResizing] = useState5(false);
695
+ const [dragging, setDragging] = useState5(false);
696
+ const draggingRef = useRef3(false);
697
+ const isInline = !!node.attrs.inline;
698
+ const align = node.attrs.align || "left";
699
+ const width = (_a = node.attrs.width) != null ? _a : null;
700
+ const height = (_b = node.attrs.height) != null ? _b : null;
701
+ const flipX = !!node.attrs.flipX;
702
+ const flipY = !!node.attrs.flipY;
703
+ const transform = useMemo(() => {
704
+ const sx = flipX ? -1 : 1;
705
+ const sy = flipY ? -1 : 1;
706
+ return `scale(${sx}, ${sy})`;
707
+ }, [flipX, flipY]);
708
+ const keepSelected = () => {
709
+ try {
710
+ const pos = typeof getPos === "function" ? getPos() : null;
711
+ if (typeof pos === "number") editor.commands.setNodeSelection(pos);
712
+ } catch (e) {
713
+ void e;
714
+ }
715
+ };
716
+ const setAlign = (a) => {
717
+ keepSelected();
718
+ updateAttributes({ align: a, inline: false });
719
+ queueMicrotask(keepSelected);
720
+ };
721
+ const toggleFlipX = () => {
722
+ keepSelected();
723
+ updateAttributes({ flipX: !flipX });
724
+ queueMicrotask(keepSelected);
725
+ };
726
+ const toggleFlipY = () => {
727
+ keepSelected();
728
+ updateAttributes({ flipY: !flipY });
729
+ queueMicrotask(keepSelected);
730
+ };
731
+ const isMenuVisible = selected && !resizing && !draggingRef.current;
732
+ const { refs, floatingStyles, context, update } = useFloating({
733
+ open: isMenuVisible,
734
+ placement: "bottom",
735
+ whileElementsMounted: autoUpdate,
736
+ strategy: "fixed",
737
+ middleware: [offset(14), flip({ padding: 10 }), shift({ padding: 8 })],
738
+ onOpenChange: (open, ev) => {
739
+ var _a2;
740
+ if (open) return;
741
+ const target = (_a2 = ev == null ? void 0 : ev.target) != null ? _a2 : null;
742
+ if (!target) return;
743
+ const view = editor.view;
744
+ if (view.dom.contains(target)) return;
745
+ try {
746
+ const pos = typeof getPos === "function" ? getPos() : null;
747
+ if (typeof pos !== "number") return;
748
+ const docSize = view.state.doc.content.size;
749
+ const next = Math.min(pos + 1, docSize);
750
+ const tr = view.state.tr.setSelection(TextSelection.create(view.state.doc, next));
751
+ view.dispatch(tr);
752
+ } catch (e) {
753
+ void e;
754
+ }
755
+ }
756
+ });
757
+ const setReferenceEl = (el) => {
758
+ boxRef.current = el;
759
+ if (el) refs.setReference(el);
760
+ };
761
+ const setFloatingWrapEl = (el) => {
762
+ floatingWrapRef.current = el;
763
+ if (el) refs.setFloating(el);
764
+ };
765
+ useLayoutEffect2(() => {
766
+ if (!isMenuVisible) return;
767
+ requestAnimationFrame(() => requestAnimationFrame(() => update()));
768
+ }, [isMenuVisible, update]);
769
+ const dismiss = useDismiss(context, {
770
+ outsidePressEvent: "pointerdown",
771
+ outsidePress: (event) => {
772
+ var _a2, _b2;
773
+ const t = event.target;
774
+ if (!t) return true;
775
+ if ((_a2 = rootRef.current) == null ? void 0 : _a2.contains(t)) return false;
776
+ if ((_b2 = floatingWrapRef.current) == null ? void 0 : _b2.contains(t)) return false;
777
+ return true;
778
+ }
779
+ });
780
+ const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
781
+ const startResize = (corner, e) => {
782
+ var _a2, _b2;
783
+ e.preventDefault();
784
+ e.stopPropagation();
785
+ const box = boxRef.current;
786
+ const img = imgRef.current;
787
+ if (!box || !img) return;
788
+ keepSelected();
789
+ e.currentTarget.setPointerCapture(e.pointerId);
790
+ const rect = box.getBoundingClientRect();
791
+ const imgRect = img.getBoundingClientRect();
792
+ const startW = (_a2 = width != null ? width : imgRect.width) != null ? _a2 : 200;
793
+ const startH = (_b2 = height != null ? height : imgRect.height) != null ? _b2 : 200;
794
+ const ratio = startW > 0 ? startH / startW : 1;
795
+ const anchor = (() => {
796
+ if (corner === "tl") return { x: rect.right, y: rect.bottom };
797
+ if (corner === "tr") return { x: rect.left, y: rect.bottom };
798
+ if (corner === "bl") return { x: rect.right, y: rect.top };
799
+ return { x: rect.left, y: rect.top };
800
+ })();
801
+ setResizing(true);
802
+ let raf = 0;
803
+ let lastW = startW;
804
+ let lastH = startH;
805
+ const apply = (w, h) => {
806
+ if (Math.abs(w - lastW) < 1 && Math.abs(h - lastH) < 1) return;
807
+ lastW = w;
808
+ lastH = h;
809
+ updateAttributes({ width: w, height: h });
810
+ };
811
+ const onMove = (ev) => {
812
+ const rawW = Math.abs(ev.clientX - anchor.x);
813
+ const nextW = clamp(Math.round(rawW), 50, 4e3);
814
+ const nextH = clamp(Math.round(nextW * ratio), 50, 4e3);
815
+ if (raf) cancelAnimationFrame(raf);
816
+ raf = requestAnimationFrame(() => apply(nextW, nextH));
817
+ };
818
+ const finish = () => {
819
+ if (raf) cancelAnimationFrame(raf);
820
+ setResizing(false);
821
+ window.removeEventListener("pointermove", onMove);
822
+ window.removeEventListener("pointerup", finish);
823
+ setTimeout(keepSelected, 0);
824
+ };
825
+ window.addEventListener("pointermove", onMove);
826
+ window.addEventListener("pointerup", finish, { once: true });
827
+ };
828
+ const applyInlineByDropContext = (dropEvent) => {
829
+ requestAnimationFrame(() => {
830
+ var _a2, _b2;
831
+ const view = editor.view;
832
+ const { state, dispatch } = view;
833
+ const sel = state.selection;
834
+ let imagePos = null;
835
+ let imageNode = null;
836
+ if (sel instanceof NodeSelection && sel.node.type.name === node.type.name) {
837
+ imagePos = sel.from;
838
+ imageNode = sel.node;
839
+ } else {
840
+ const coords = view.posAtCoords({ left: dropEvent.clientX, top: dropEvent.clientY });
841
+ const pos = (_a2 = coords == null ? void 0 : coords.pos) != null ? _a2 : null;
842
+ if (typeof pos === "number") {
843
+ const maybe = state.doc.nodeAt(pos);
844
+ if (((_b2 = maybe == null ? void 0 : maybe.type) == null ? void 0 : _b2.name) === node.type.name) {
845
+ imagePos = pos;
846
+ imageNode = maybe;
847
+ }
848
+ }
849
+ }
850
+ if (!imageNode || typeof imagePos !== "number") return;
851
+ const $pos = state.doc.resolve(imagePos);
852
+ const shouldInline = $pos.parent.isTextblock;
853
+ const tr = state.tr.setNodeMarkup(imagePos, void 0, __spreadProps(__spreadValues({}, imageNode.attrs), {
854
+ inline: shouldInline
855
+ }));
856
+ if (tr.docChanged) dispatch(tr);
857
+ });
858
+ };
859
+ useEffect(() => {
860
+ if (!dragging) return;
861
+ const stop = () => {
862
+ draggingRef.current = false;
863
+ setDragging(false);
864
+ };
865
+ window.addEventListener("dragend", stop, true);
866
+ window.addEventListener("drop", stop, true);
867
+ window.addEventListener("mouseup", stop, true);
868
+ return () => {
869
+ window.removeEventListener("dragend", stop, true);
870
+ window.removeEventListener("drop", stop, true);
871
+ window.removeEventListener("mouseup", stop, true);
872
+ };
873
+ }, [dragging]);
874
+ const onDragStartCapture = (e) => {
875
+ draggingRef.current = true;
876
+ setDragging(true);
877
+ try {
878
+ const pos = typeof getPos === "function" ? getPos() : null;
879
+ if (typeof pos !== "number") return;
880
+ const { state, dispatch } = editor.view;
881
+ dispatch(state.tr.setSelection(NodeSelection.create(state.doc, pos)));
882
+ const slice = editor.view.state.selection.content();
883
+ editor.view.dragging = { slice, move: true };
884
+ try {
885
+ e.dataTransfer.effectAllowed = "move";
886
+ e.dataTransfer.dropEffect = "move";
887
+ e.dataTransfer.setData("text/plain", " ");
888
+ } catch (e2) {
889
+ }
890
+ } catch (e2) {
891
+ void e2;
892
+ }
893
+ };
894
+ const onDropCapture = (e) => {
895
+ if (!draggingRef.current) return;
896
+ applyInlineByDropContext(e.nativeEvent);
897
+ };
898
+ const onDragEnd = () => {
899
+ draggingRef.current = false;
900
+ setDragging(false);
901
+ setTimeout(keepSelected, 0);
902
+ };
903
+ const wrapperStyle = isInline ? { display: "inline-block", verticalAlign: "baseline", maxWidth: "100%" } : { display: "block", width: "100%", textAlign: align };
904
+ const boxStyle = {
905
+ display: "inline-block",
906
+ position: "relative",
907
+ maxWidth: "100%",
908
+ outline: selected ? "2px solid #111827" : void 0,
909
+ outlineOffset: selected ? 2 : void 0
910
+ };
911
+ const imgStyle = {
912
+ display: "block",
913
+ maxWidth: "100%",
914
+ width: width ? `${width}px` : void 0,
915
+ height: height ? `${height}px` : "auto",
916
+ transform,
917
+ userSelect: "none",
918
+ opacity: dragging ? 0.7 : 1
919
+ };
920
+ return /* @__PURE__ */ jsxs7(
921
+ NodeViewWrapper,
922
+ {
923
+ ref: rootRef,
924
+ as: "span",
925
+ contentEditable: false,
926
+ draggable: true,
927
+ onDragStartCapture,
928
+ onDropCapture,
929
+ onDragEnd,
930
+ "data-node": "image",
931
+ "data-inline": isInline ? "true" : "false",
932
+ style: wrapperStyle,
933
+ children: [
934
+ /* @__PURE__ */ jsxs7(
935
+ "span",
936
+ __spreadProps(__spreadValues({
937
+ ref: setReferenceEl
938
+ }, getReferenceProps({
939
+ onPointerDownCapture: () => {
940
+ keepSelected();
941
+ if (!dragging) draggingRef.current = false;
942
+ }
943
+ })), {
944
+ style: boxStyle,
945
+ children: [
946
+ /* @__PURE__ */ jsx7(
947
+ "img",
948
+ {
949
+ ref: imgRef,
950
+ src: node.attrs.src,
951
+ alt: node.attrs.alt || "",
952
+ title: node.attrs.title || "",
953
+ draggable: false,
954
+ style: imgStyle
955
+ }
956
+ ),
957
+ dragging && /* @__PURE__ */ jsx7(
958
+ "span",
959
+ {
960
+ style: {
961
+ pointerEvents: "none",
962
+ position: "absolute",
963
+ inset: 0,
964
+ borderRadius: 6,
965
+ boxShadow: "0 0 0 2px rgba(17,24,39,0.4) inset"
966
+ }
967
+ }
968
+ ),
969
+ selected && /* @__PURE__ */ jsxs7(Fragment4, { children: [
970
+ /* @__PURE__ */ jsx7("span", { onPointerDown: (e) => startResize("tl", e), style: { position: "absolute", top: -8, left: -8, height: 12, width: 12, background: "#111827", cursor: "nwse-resize" } }),
971
+ /* @__PURE__ */ jsx7("span", { onPointerDown: (e) => startResize("tr", e), style: { position: "absolute", top: -8, right: -8, height: 12, width: 12, background: "#111827", cursor: "nesw-resize" } }),
972
+ /* @__PURE__ */ jsx7("span", { onPointerDown: (e) => startResize("bl", e), style: { position: "absolute", bottom: -8, left: -8, height: 12, width: 12, background: "#111827", cursor: "nesw-resize" } }),
973
+ /* @__PURE__ */ jsx7("span", { onPointerDown: (e) => startResize("br", e), style: { position: "absolute", bottom: -8, right: -8, height: 12, width: 12, background: "#111827", cursor: "nwse-resize" } })
974
+ ] })
975
+ ]
976
+ })
977
+ ),
978
+ /* @__PURE__ */ jsx7(AnimatePresence3, { children: isMenuVisible && /* @__PURE__ */ jsx7(
979
+ "div",
980
+ __spreadProps(__spreadValues({
981
+ ref: setFloatingWrapEl,
982
+ style: floatingStyles
983
+ }, getFloatingProps()), {
984
+ children: /* @__PURE__ */ jsxs7(
985
+ motion2.div,
986
+ {
987
+ initial: { opacity: 0, scale: 0.98, y: -4 },
988
+ animate: { opacity: 1, scale: 1, y: 0 },
989
+ exit: { opacity: 0, scale: 0.98, y: -4 },
990
+ transition: { type: "tween", duration: 0.12 },
991
+ style: {
992
+ display: "inline-flex",
993
+ alignItems: "center",
994
+ gap: 6,
995
+ borderRadius: 12,
996
+ border: "1px solid #e5e7eb",
997
+ background: "#ffffff",
998
+ padding: 6,
999
+ boxShadow: "0 10px 20px rgba(0,0,0,0.06)"
1000
+ },
1001
+ children: [
1002
+ /* @__PURE__ */ jsx7(Tooltip_default, { content: "Flip Horizontal", children: /* @__PURE__ */ jsx7("button", { type: "button", onClick: toggleFlipX, style: { borderRadius: 10, border: "1px solid #e5e7eb", background: "#fff", padding: "6px 8px", fontSize: 12, cursor: "pointer" }, children: /* @__PURE__ */ jsx7(FlipVertical, {}) }) }),
1003
+ /* @__PURE__ */ jsx7(Tooltip_default, { content: "Flip Vertical", children: /* @__PURE__ */ jsx7("button", { type: "button", onClick: toggleFlipY, style: { borderRadius: 10, border: "1px solid #e5e7eb", background: "#fff", padding: "6px 8px", fontSize: 12, cursor: "pointer" }, children: /* @__PURE__ */ jsx7(FlipHorizontal, {}) }) }),
1004
+ /* @__PURE__ */ jsx7("span", { style: { width: 1, height: 16, background: "#e5e7eb", margin: "0 6px" } }),
1005
+ /* @__PURE__ */ jsx7(Tooltip_default, { content: "Left", children: /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => setAlign("left"), style: { borderRadius: 10, border: "1px solid #e5e7eb", background: "#fff", padding: "6px 8px", fontSize: 12, cursor: "pointer" }, children: /* @__PURE__ */ jsx7(AlignLeft, {}) }) }),
1006
+ /* @__PURE__ */ jsx7(Tooltip_default, { content: "Center", children: /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => setAlign("center"), style: { borderRadius: 10, border: "1px solid #e5e7eb", background: "#fff", padding: "6px 8px", fontSize: 12, cursor: "pointer" }, children: /* @__PURE__ */ jsx7(AlignCenter, {}) }) }),
1007
+ /* @__PURE__ */ jsx7(Tooltip_default, { content: "Right", children: /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => setAlign("right"), style: { borderRadius: 10, border: "1px solid #e5e7eb", background: "#fff", padding: "6px 8px", fontSize: 12, cursor: "pointer" }, children: /* @__PURE__ */ jsx7(AlignRight, {}) }) }),
1008
+ /* @__PURE__ */ jsx7("span", { style: { width: 1, height: 16, background: "#e5e7eb", margin: "0 6px" } }),
1009
+ /* @__PURE__ */ jsx7(Tooltip_default, { content: "Remove", children: /* @__PURE__ */ jsx7(
1010
+ "button",
1011
+ {
1012
+ type: "button",
1013
+ onClick: deleteNode,
1014
+ style: {
1015
+ borderRadius: 10,
1016
+ border: "1px solid #fecaca",
1017
+ background: "#fff",
1018
+ padding: "6px 8px",
1019
+ fontSize: 12,
1020
+ cursor: "pointer",
1021
+ color: "#dc2626"
1022
+ },
1023
+ children: /* @__PURE__ */ jsx7(TrashIcon, {})
1024
+ }
1025
+ ) })
1026
+ ]
1027
+ }
1028
+ )
1029
+ })
1030
+ ) })
1031
+ ]
1032
+ }
1033
+ );
1034
+ };
1035
+
1036
+ // src/extension/Image.tsx
1037
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1038
+ var Image2 = TiptapImage.extend({
1039
+ group: "inline",
1040
+ inline: true,
1041
+ draggable: true,
1042
+ selectable: true,
1043
+ defining: true,
1044
+ addOptions() {
1045
+ var _a;
1046
+ const parent = (_a = this.parent) == null ? void 0 : _a.call(this);
1047
+ return __spreadProps(__spreadValues({}, parent != null ? parent : {}), {
1048
+ defaultInline: false,
1049
+ component: ({ editor, options, buttonClassName }) => {
1050
+ const Wrapper = () => {
1051
+ const [open, setOpen] = useState6(false);
1052
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
1053
+ /* @__PURE__ */ jsx8(
1054
+ ButtonWithoutActive_default,
1055
+ {
1056
+ className: options.className,
1057
+ icon: options.icon,
1058
+ style: options.style,
1059
+ tooltip: options.tooltip,
1060
+ tooltipClassName: options.tooltipClassName,
1061
+ tooltipPlacement: options.tooltipPlacement,
1062
+ _internalIcon: /* @__PURE__ */ jsx8(ImageUpIcon, {}),
1063
+ _onToggle: () => setOpen((p) => !p),
1064
+ _tooltipContent: "Image",
1065
+ _buttonClassName: buttonClassName
1066
+ }
1067
+ ),
1068
+ /* @__PURE__ */ jsx8(
1069
+ ImageDialog_default,
1070
+ {
1071
+ open,
1072
+ onClose: () => setOpen(false),
1073
+ editor,
1074
+ options: {
1075
+ upload: options.upload,
1076
+ maxSize: options.maxSize,
1077
+ defaultInline: options.defaultInline,
1078
+ enableAlt: options.enableAlt,
1079
+ aspect: options.aspect
1080
+ }
1081
+ }
1082
+ )
1083
+ ] });
1084
+ };
1085
+ return /* @__PURE__ */ jsx8(Wrapper, {});
1086
+ }
1087
+ });
1088
+ },
1089
+ addAttributes() {
1090
+ var _a;
1091
+ return __spreadProps(__spreadValues({}, (_a = this.parent) == null ? void 0 : _a.call(this)), {
1092
+ inline: {
1093
+ default: false,
1094
+ parseHTML: (el) => el.getAttribute("data-inline") === "true",
1095
+ renderHTML: (attrs) => ({ "data-inline": attrs.inline ? "true" : "false" })
1096
+ },
1097
+ width: {
1098
+ default: null,
1099
+ parseHTML: (el) => {
1100
+ const v = el.getAttribute("data-width");
1101
+ return v ? Number(v) : null;
1102
+ },
1103
+ renderHTML: (attrs) => attrs.width ? { "data-width": String(attrs.width) } : {}
1104
+ },
1105
+ height: {
1106
+ default: null,
1107
+ parseHTML: (el) => {
1108
+ const v = el.getAttribute("data-height");
1109
+ return v ? Number(v) : null;
1110
+ },
1111
+ renderHTML: (attrs) => attrs.height ? { "data-height": String(attrs.height) } : {}
1112
+ },
1113
+ flipX: {
1114
+ default: false,
1115
+ parseHTML: (el) => el.getAttribute("data-flip-x") === "true",
1116
+ renderHTML: (attrs) => ({ "data-flip-x": attrs.flipX ? "true" : "false" })
1117
+ },
1118
+ flipY: {
1119
+ default: false,
1120
+ parseHTML: (el) => el.getAttribute("data-flip-y") === "true",
1121
+ renderHTML: (attrs) => ({ "data-flip-y": attrs.flipY ? "true" : "false" })
1122
+ },
1123
+ align: {
1124
+ default: "left",
1125
+ parseHTML: (el) => el.getAttribute("data-align") || "left",
1126
+ renderHTML: (attrs) => ({ "data-align": attrs.align || "left" })
1127
+ }
1128
+ });
1129
+ },
1130
+ addCommands() {
1131
+ var _a;
1132
+ return __spreadProps(__spreadValues({}, (_a = this.parent) == null ? void 0 : _a.call(this)), {
1133
+ setImageWithInline: (options) => ({ commands }) => {
1134
+ var _a2, _b;
1135
+ const inlineAttr = (_b = (_a2 = options.inline) != null ? _a2 : this.options.defaultInline) != null ? _b : false;
1136
+ const _c = options, { inline: _inline } = _c, attrs = __objRest(_c, ["inline"]);
1137
+ return commands.insertContent({
1138
+ type: this.name,
1139
+ attrs: __spreadProps(__spreadValues({}, attrs), { inline: inlineAttr })
1140
+ });
1141
+ }
1142
+ });
1143
+ },
1144
+ addNodeView() {
1145
+ return ReactNodeViewRenderer(ImageView);
1146
+ },
1147
+ renderHTML({ HTMLAttributes }) {
1148
+ return ["img", mergeAttributes(HTMLAttributes)];
1149
+ }
1150
+ });
1151
+ export {
1152
+ Image2 as Image
1153
+ };