@cyberyozh/text-editor 0.1.17 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editor.css +1 -0
- package/dist/editor.css.map +1 -1
- package/dist/editor.d.mts +9 -1
- package/dist/editor.d.ts +9 -1
- package/dist/editor.js +204 -66
- package/dist/editor.js.map +1 -1
- package/dist/editor.mjs +216 -78
- package/dist/editor.mjs.map +1 -1
- package/dist/render.js +29 -38
- package/dist/render.js.map +1 -1
- package/dist/render.mjs +34 -43
- package/dist/render.mjs.map +1 -1
- package/dist/styles/index.css +1 -0
- package/package.json +40 -39
- package/src/editor/controls/bubble-menu-content.tsx +12 -3
- package/src/editor/controls/character-count.tsx +7 -4
- package/src/editor/dialogs/image-dialog/image-dialog.css +1 -0
- package/src/editor/dialogs/image-dialog/image-dialog.tsx +9 -1
- package/src/editor/editor.tsx +52 -9
- package/src/editor/handlers/index.ts +4 -0
- package/src/editor/handlers/insertion-handlers.ts +113 -0
- package/src/editor/index.ts +1 -1
- package/src/types/image-handler.ts +5 -0
- package/src/utils/prosemirror-iterator.ts +38 -0
package/dist/editor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/editor/editor.tsx
|
|
2
|
-
import { useState as
|
|
2
|
+
import { useState as useState13, useEffect as useEffect11 } from "react";
|
|
3
3
|
import { NodeSelection } from "@tiptap/pm/state";
|
|
4
|
-
import { Slice } from "@tiptap/pm/model";
|
|
4
|
+
import { Slice as Slice2 } from "@tiptap/pm/model";
|
|
5
5
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
6
6
|
import { BubbleMenu } from "@tiptap/react/menus";
|
|
7
7
|
import "highlight.js/styles/github-dark.css";
|
|
@@ -749,10 +749,16 @@ var BubbleMenuContent = ({ editor, onLinkClick }) => {
|
|
|
749
749
|
} = useEditorState({
|
|
750
750
|
editor,
|
|
751
751
|
selector: (ctx) => {
|
|
752
|
+
let canAvalable = false;
|
|
753
|
+
try {
|
|
754
|
+
ctx.editor.can().undo();
|
|
755
|
+
canAvalable = true;
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
752
758
|
return {
|
|
753
|
-
canUndo: ctx.editor.can().undo(),
|
|
754
|
-
canRedo: ctx.editor.can().redo(),
|
|
755
|
-
canClearMarks: ctx.editor.can().unsetAllMarks(),
|
|
759
|
+
canUndo: canAvalable ? ctx.editor.can().undo() : false,
|
|
760
|
+
canRedo: canAvalable ? ctx.editor.can().redo() : false,
|
|
761
|
+
canClearMarks: canAvalable ? ctx.editor.can().unsetAllMarks() : false,
|
|
756
762
|
isHeading2: ctx.editor.isActive("heading", { level: 2 }),
|
|
757
763
|
isHeading3: ctx.editor.isActive("heading", { level: 3 }),
|
|
758
764
|
isHeading4: ctx.editor.isActive("heading", { level: 4 }),
|
|
@@ -1446,10 +1452,12 @@ var CharacterCount = ({ editor }) => {
|
|
|
1446
1452
|
const translations2 = useTiptapTranslations();
|
|
1447
1453
|
const { charactersCount, wordsCount } = useEditorState2({
|
|
1448
1454
|
editor,
|
|
1449
|
-
selector: (context) =>
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1455
|
+
selector: (context) => {
|
|
1456
|
+
return {
|
|
1457
|
+
charactersCount: context.editor.storage.characterCount?.characters() || 0,
|
|
1458
|
+
wordsCount: context.editor.storage.characterCount?.words() || 0
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1453
1461
|
});
|
|
1454
1462
|
return /* @__PURE__ */ jsxs14("div", { className: "tiptap__character-count", children: [
|
|
1455
1463
|
/* @__PURE__ */ jsxs14("span", { className: "tiptap__character-count-item", children: [
|
|
@@ -1764,7 +1772,7 @@ var TableMenu = ({ editor }) => {
|
|
|
1764
1772
|
};
|
|
1765
1773
|
|
|
1766
1774
|
// src/editor/dialogs/image-dialog/image-dialog.tsx
|
|
1767
|
-
import { useRef as useRef6, useState as useState6 } from "react";
|
|
1775
|
+
import { useEffect as useEffect5, useRef as useRef6, useState as useState6 } from "react";
|
|
1768
1776
|
|
|
1769
1777
|
// src/editor/components/button/button.tsx
|
|
1770
1778
|
import { jsx as jsx64 } from "react/jsx-runtime";
|
|
@@ -1880,6 +1888,7 @@ import { Fragment as Fragment2, jsx as jsx67, jsxs as jsxs18 } from "react/jsx-r
|
|
|
1880
1888
|
var ImageDialog = ({
|
|
1881
1889
|
open,
|
|
1882
1890
|
onClose,
|
|
1891
|
+
defaultFile,
|
|
1883
1892
|
onImageAdd,
|
|
1884
1893
|
onImageEdit,
|
|
1885
1894
|
uploadHandler,
|
|
@@ -1963,6 +1972,11 @@ var ImageDialog = ({
|
|
|
1963
1972
|
}));
|
|
1964
1973
|
};
|
|
1965
1974
|
};
|
|
1975
|
+
useEffect5(() => {
|
|
1976
|
+
if (defaultFile) {
|
|
1977
|
+
handleSelectFile(defaultFile);
|
|
1978
|
+
}
|
|
1979
|
+
}, []);
|
|
1966
1980
|
const handleAreaClick = () => {
|
|
1967
1981
|
inputRef.current?.click();
|
|
1968
1982
|
};
|
|
@@ -2154,7 +2168,7 @@ var ImageDialog = ({
|
|
|
2154
2168
|
};
|
|
2155
2169
|
|
|
2156
2170
|
// src/editor/dialogs/add-katex-dialog/add-katex-dialog.tsx
|
|
2157
|
-
import { useEffect as
|
|
2171
|
+
import { useEffect as useEffect6, useRef as useRef7, useState as useState7 } from "react";
|
|
2158
2172
|
import { jsx as jsx68, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2159
2173
|
var AddKatexDialog = ({
|
|
2160
2174
|
open,
|
|
@@ -2165,7 +2179,7 @@ var AddKatexDialog = ({
|
|
|
2165
2179
|
const translations2 = useTiptapTranslations();
|
|
2166
2180
|
const [content, setContent] = useState7(initialContent);
|
|
2167
2181
|
const contentInputRef = useRef7(null);
|
|
2168
|
-
|
|
2182
|
+
useEffect6(() => {
|
|
2169
2183
|
if (open) {
|
|
2170
2184
|
setContent(initialContent);
|
|
2171
2185
|
setTimeout(() => {
|
|
@@ -2264,7 +2278,7 @@ var AddKatexDialog = ({
|
|
|
2264
2278
|
};
|
|
2265
2279
|
|
|
2266
2280
|
// src/editor/dialogs/add-link-dialog/add-link-dialog.tsx
|
|
2267
|
-
import { useEffect as
|
|
2281
|
+
import { useEffect as useEffect7, useRef as useRef8, useState as useState8 } from "react";
|
|
2268
2282
|
import { jsx as jsx69, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2269
2283
|
var AddLinkDialog = ({
|
|
2270
2284
|
open,
|
|
@@ -2288,7 +2302,7 @@ var AddLinkDialog = ({
|
|
|
2288
2302
|
setAddNoopener(initialLinkRel);
|
|
2289
2303
|
setError("");
|
|
2290
2304
|
};
|
|
2291
|
-
|
|
2305
|
+
useEffect7(() => {
|
|
2292
2306
|
if (open) {
|
|
2293
2307
|
clearDialogState();
|
|
2294
2308
|
setTimeout(() => {
|
|
@@ -2455,7 +2469,7 @@ var AddLinkDialog = ({
|
|
|
2455
2469
|
};
|
|
2456
2470
|
|
|
2457
2471
|
// src/editor/dialogs/add-video-dialog/add-video-dialog.tsx
|
|
2458
|
-
import { useEffect as
|
|
2472
|
+
import { useEffect as useEffect8, useRef as useRef9, useState as useState9 } from "react";
|
|
2459
2473
|
|
|
2460
2474
|
// src/utils/detect-video-hosting.ts
|
|
2461
2475
|
var VIDEO_PATTERNS = {
|
|
@@ -2542,7 +2556,7 @@ var AddVideoDialog = ({ open, onClose, onVideoAdd }) => {
|
|
|
2542
2556
|
const [error, setError] = useState9("");
|
|
2543
2557
|
const [isValidating, setIsValidating] = useState9(false);
|
|
2544
2558
|
const urlInputRef = useRef9(null);
|
|
2545
|
-
|
|
2559
|
+
useEffect8(() => {
|
|
2546
2560
|
if (open) {
|
|
2547
2561
|
setUrl("");
|
|
2548
2562
|
setError("");
|
|
@@ -3089,7 +3103,7 @@ import Link, { isAllowedUri } from "@tiptap/extension-link";
|
|
|
3089
3103
|
import { markPasteRule } from "@tiptap/core";
|
|
3090
3104
|
|
|
3091
3105
|
// node_modules/linkifyjs/dist/linkify.mjs
|
|
3092
|
-
var encodedTlds = "
|
|
3106
|
+
var encodedTlds = "aaa1rp3bb0ott3vie4c1le2ogado5udhabi7c0ademy5centure6ountant0s9o1tor4d0s1ult4e0g1ro2tna4f0l1rica5g0akhan5ency5i0g1rbus3force5tel5kdn3l0ibaba4pay4lfinanz6state5y2sace3tom5m0azon4ericanexpress7family11x2fam3ica3sterdam8nalytics7droid5quan4z2o0l2partments8p0le4q0uarelle8r0ab1mco4chi3my2pa2t0e3s0da2ia2sociates9t0hleta5torney7u0ction5di0ble3o3spost5thor3o0s4w0s2x0a2z0ure5ba0by2idu3namex4d1k2r0celona5laycard4s5efoot5gains6seball5ketball8uhaus5yern5b0c1t1va3cg1n2d1e0ats2uty4er2rlin4st0buy5t2f1g1h0arti5i0ble3d1ke2ng0o3o1z2j1lack0friday9ockbuster8g1omberg7ue3m0s1w2n0pparibas9o0ats3ehringer8fa2m1nd2o0k0ing5sch2tik2on4t1utique6x2r0adesco6idgestone9oadway5ker3ther5ussels7s1t1uild0ers6siness6y1zz3v1w1y1z0h3ca0b1fe2l0l1vinklein9m0era3p2non3petown5ital0one8r0avan4ds2e0er0s4s2sa1e1h1ino4t0ering5holic7ba1n1re3c1d1enter4o1rn3f0a1d2g1h0anel2nel4rity4se2t2eap3intai5ristmas6ome4urch5i0priani6rcle4sco3tadel4i0c2y3k1l0aims4eaning6ick2nic1que6othing5ud3ub0med6m1n1o0ach3des3ffee4llege4ogne5m0mbank4unity6pany2re3uter5sec4ndos3struction8ulting7tact3ractors9oking4l1p2rsica5untry4pon0s4rses6pa2r0edit0card4union9icket5own3s1uise0s6u0isinella9v1w1x1y0mru3ou3z2dad1nce3ta1e1ing3sun4y2clk3ds2e0al0er2s3gree4livery5l1oitte5ta3mocrat6ntal2ist5si0gn4v2hl2iamonds6et2gital5rect0ory7scount3ver5h2y2j1k1m1np2o0cs1tor4g1mains5t1wnload7rive4tv2ubai3pont4rban5vag2r2z2earth3t2c0o2deka3u0cation8e1g1mail3erck5nergy4gineer0ing9terprises10pson4quipment8r0icsson6ni3s0q1tate5t1u0rovision8s2vents5xchange6pert3osed4ress5traspace10fage2il1rwinds6th3mily4n0s2rm0ers5shion4t3edex3edback6rrari3ero6i0delity5o2lm2nal1nce1ial7re0stone6mdale6sh0ing5t0ness6j1k1lickr3ghts4r2orist4wers5y2m1o0o0d1tball6rd1ex2sale4um3undation8x2r0ee1senius7l1ogans4ntier7tr2ujitsu5n0d2rniture7tbol5yi3ga0l0lery3o1up4me0s3p1rden4y2b0iz3d0n2e0a1nt0ing5orge5f1g0ee3h1i0ft0s3ves2ing5l0ass3e1obal2o4m0ail3bh2o1x2n1odaddy5ld0point6f2odyear5g0le4p1t1v2p1q1r0ainger5phics5tis4een3ipe3ocery4up4s1t1u0cci3ge2ide2tars5ru3w1y2hair2mburg5ngout5us3bo2dfc0bank7ealth0care8lp1sinki6re1mes5iphop4samitsu7tachi5v2k0t2m1n1ockey4ldings5iday5medepot5goods5s0ense7nda3rse3spital5t0ing5t0els3mail5use3w2r1sbc3t1u0ghes5yatt3undai7ibm2cbc2e1u2d1e0ee3fm2kano4l1m0amat4db2mo0bilien9n0c1dustries8finiti5o2g1k1stitute6urance4e4t0ernational10uit4vestments10o1piranga7q1r0ish4s0maili5t0anbul7t0au2v3jaguar4va3cb2e0ep2tzt3welry6io2ll2m0p2nj2o0bs1urg4t1y2p0morgan6rs3uegos4niper7kaufen5ddi3e0rryhotels6properties14fh2g1h1i0a1ds2m1ndle4tchen5wi3m1n1oeln3matsu5sher5p0mg2n2r0d1ed3uokgroup8w1y0oto4z2la0caixa5mborghini8er3nd0rover6xess5salle5t0ino3robe5w0yer5b1c1ds2ease3clerc5frak4gal2o2xus4gbt3i0dl2fe0insurance9style7ghting6ke2lly3mited4o2ncoln4k2ve1ing5k1lc1p2oan0s3cker3us3l1ndon4tte1o3ve3pl0financial11r1s1t0d0a3u0ndbeck6xe1ury5v1y2ma0drid4if1son4keup4n0agement7go3p1rket0ing3s4riott5shalls7ttel5ba2c0kinsey7d1e0d0ia3et2lbourne7me1orial6n0u2rck0msd7g1h1iami3crosoft7l1ni1t2t0subishi9k1l0b1s2m0a2n1o0bi0le4da2e1i1m1nash3ey2ster5rmon3tgage6scow4to0rcycles9v0ie4p1q1r1s0d2t0n1r2u0seum3ic4v1w1x1y1z2na0b1goya4me2vy3ba2c1e0c1t0bank4flix4work5ustar5w0s2xt0direct7us4f0l2g0o2hk2i0co2ke1on3nja3ssan1y5l1o0kia3rton4w0ruz3tv4p1r0a1w2tt2u1yc2z2obi1server7ffice5kinawa6layan0group9lo3m0ega4ne1g1l0ine5oo2pen3racle3nge4g0anic5igins6saka4tsuka4t2vh3pa0ge2nasonic7ris2s1tners4s1y3y2ccw3e0t2f0izer5g1h0armacy6d1ilips5one2to0graphy6s4ysio5ics1tet2ures6d1n0g1k2oneer5zza4k1l0ace2y0station9umbing5s3m1n0c2ohl2ker3litie5rn2st3r0axi3ess3ime3o0d0uctions8f1gressive8mo2perties3y5tection8u0dential9s1t1ub2w0c2y2qa1pon3uebec3st5racing4dio4e0ad1lestate6tor2y4cipes5d0umbrella9hab3ise0n3t2liance6n0t0als5pair3ort3ublican8st0aurant8view0s5xroth6ich0ardli6oh3l1o1p2o0cks3deo3gers4om3s0vp3u0gby3hr2n2w0e2yukyu6sa0arland6fe0ty4kura4le1on3msclub4ung5ndvik0coromant12ofi4p1rl2s1ve2xo3b0i1s2c0b1haeffler7midt4olarships8ol3ule3warz5ience5ot3d1e0arch3t2cure1ity6ek2lect4ner3rvices6ven3w1x0y3fr2g1h0angrila6rp3ell3ia1ksha5oes2p0ping5uji3w3i0lk2na1gles5te3j1k0i0n2y0pe4l0ing4m0art3ile4n0cf3o0ccer3ial4ftbank4ware6hu2lar2utions7ng1y2y2pa0ce3ort2t3r0l2s1t0ada2ples4r1tebank4farm7c0group6ockholm6rage3e3ream4udio2y3yle4u0cks3pplies3y2ort5rf1gery5zuki5v1watch4iss4x1y0dney4stems6z2tab1ipei4lk2obao4rget4tamotors6r2too4x0i3c0i2d0k2eam2ch0nology8l1masek5nnis4va3f1g1h0d1eater2re6iaa2ckets5enda4ps2res2ol4j0maxx4x2k0maxx5l1m0all4n1o0day3kyo3ols3p1ray3shiba5tal3urs3wn2yota3s3r0ade1ing4ining5vel0ers0insurance16ust3v2t1ube2i1nes3shu4v0s2w1z2ua1bank3s2g1k1nicom3versity8o2ol2ps2s1y1z2va0cations7na1guard7c1e0gas3ntures6risign5m\xF6gensberater2ung14sicherung10t2g1i0ajes4deo3g1king4llas4n1p1rgin4sa1ion4va1o3laanderen9n1odka3lvo3te1ing3o2yage5u2wales2mart4ter4ng0gou5tch0es6eather0channel12bcam3er2site5d0ding5ibo2r3f1hoswho6ien2ki2lliamhill9n0dows4e1ners6me2oodside6rk0s2ld3w2s1tc1f3xbox3erox4ihuan4n2xx2yz3yachts4hoo3maxun5ndex5e1odobashi7ga2kohama6u0tube6t1un3za0ppos4ra3ero3ip2m1one3uerich6w2";
|
|
3093
3107
|
var encodedUtlds = "\u03B5\u03BB1\u03C52\u0431\u04331\u0435\u043B3\u0434\u0435\u0442\u04384\u0435\u044E2\u043A\u0430\u0442\u043E\u043B\u0438\u043A6\u043E\u043C3\u043C\u043A\u04342\u043E\u043D1\u0441\u043A\u0432\u04306\u043E\u043D\u043B\u0430\u0439\u043D5\u0440\u04333\u0440\u0443\u04412\u04442\u0441\u0430\u0439\u04423\u0440\u04313\u0443\u043A\u04403\u049B\u0430\u04373\u0570\u0561\u05753\u05D9\u05E9\u05E8\u05D0\u05DC5\u05E7\u05D5\u05DD3\u0627\u0628\u0648\u0638\u0628\u064A5\u0631\u0627\u0645\u0643\u06485\u0644\u0627\u0631\u062F\u06464\u0628\u062D\u0631\u064A\u06465\u062C\u0632\u0627\u0626\u06315\u0633\u0639\u0648\u062F\u064A\u06296\u0639\u0644\u064A\u0627\u06465\u0645\u063A\u0631\u06285\u0645\u0627\u0631\u0627\u062A5\u06CC\u0631\u0627\u06465\u0628\u0627\u0631\u062A2\u0632\u0627\u06314\u064A\u062A\u06433\u06BE\u0627\u0631\u062A5\u062A\u0648\u0646\u06334\u0633\u0648\u062F\u0627\u06463\u0631\u064A\u06295\u0634\u0628\u0643\u06294\u0639\u0631\u0627\u06422\u06282\u0645\u0627\u06464\u0641\u0644\u0633\u0637\u064A\u06466\u0642\u0637\u06313\u0643\u0627\u062B\u0648\u0644\u064A\u06436\u0648\u06453\u0645\u0635\u06312\u0644\u064A\u0633\u064A\u06275\u0648\u0631\u064A\u062A\u0627\u0646\u064A\u06277\u0642\u06394\u0647\u0645\u0631\u0627\u06475\u067E\u0627\u06A9\u0633\u062A\u0627\u06467\u0680\u0627\u0631\u062A4\u0915\u0949\u092E3\u0928\u0947\u091F3\u092D\u093E\u0930\u09240\u092E\u094D3\u094B\u09245\u0938\u0902\u0917\u0920\u09285\u09AC\u09BE\u0982\u09B2\u09BE5\u09AD\u09BE\u09B0\u09A42\u09F0\u09A44\u0A2D\u0A3E\u0A30\u0A244\u0AAD\u0ABE\u0AB0\u0AA44\u0B2D\u0B3E\u0B30\u0B244\u0B87\u0BA8\u0BCD\u0BA4\u0BBF\u0BAF\u0BBE6\u0BB2\u0B99\u0BCD\u0B95\u0BC86\u0B9A\u0BBF\u0B99\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0BC2\u0BB0\u0BCD11\u0C2D\u0C3E\u0C30\u0C24\u0C4D5\u0CAD\u0CBE\u0CB0\u0CA44\u0D2D\u0D3E\u0D30\u0D24\u0D025\u0DBD\u0D82\u0D9A\u0DCF4\u0E04\u0E2D\u0E213\u0E44\u0E17\u0E223\u0EA5\u0EB2\u0EA73\u10D2\u10D42\u307F\u3093\u306A3\u30A2\u30DE\u30BE\u30F34\u30AF\u30E9\u30A6\u30C94\u30B0\u30FC\u30B0\u30EB4\u30B3\u30E02\u30B9\u30C8\u30A23\u30BB\u30FC\u30EB3\u30D5\u30A1\u30C3\u30B7\u30E7\u30F36\u30DD\u30A4\u30F3\u30C84\u4E16\u754C2\u4E2D\u4FE11\u56FD1\u570B1\u6587\u7F513\u4E9A\u9A6C\u900A3\u4F01\u4E1A2\u4F5B\u5C712\u4FE1\u606F2\u5065\u5EB72\u516B\u53662\u516C\u53F81\u76CA2\u53F0\u6E7E1\u70632\u5546\u57CE1\u5E971\u68072\u5609\u91CC0\u5927\u9152\u5E975\u5728\u7EBF2\u5927\u62FF2\u5929\u4E3B\u65593\u5A31\u4E502\u5BB6\u96FB2\u5E7F\u4E1C2\u5FAE\u535A2\u6148\u55842\u6211\u7231\u4F603\u624B\u673A2\u62DB\u80582\u653F\u52A11\u5E9C2\u65B0\u52A0\u57612\u95FB2\u65F6\u5C1A2\u66F8\u7C4D2\u673A\u67842\u6DE1\u9A6C\u95213\u6E38\u620F2\u6FB3\u95802\u70B9\u770B2\u79FB\u52A82\u7EC4\u7EC7\u673A\u67844\u7F51\u57401\u5E971\u7AD91\u7EDC2\u8054\u901A2\u8C37\u6B4C2\u8D2D\u72692\u901A\u8CA92\u96C6\u56E22\u96FB\u8A0A\u76C8\u79D14\u98DE\u5229\u6D663\u98DF\u54C12\u9910\u53852\u9999\u683C\u91CC\u62C93\u6E2F2\uB2F7\uB1371\uCEF42\uC0BC\uC1312\uD55C\uAD6D2";
|
|
3094
3108
|
var numeric = "numeric";
|
|
3095
3109
|
var ascii = "ascii";
|
|
@@ -4020,8 +4034,6 @@ function init$1({
|
|
|
4020
4034
|
ta(Email$1, groups.domain, EmailDomain);
|
|
4021
4035
|
tt(Email$1, DOT, EmailDomainDot);
|
|
4022
4036
|
tt(Email$1, HYPHEN, EmailDomainHyphen);
|
|
4023
|
-
const EmailColon = tt(Email$1, COLON);
|
|
4024
|
-
ta(EmailColon, groups.numeric, Email);
|
|
4025
4037
|
const DomainHyphen = tt(Domain, HYPHEN);
|
|
4026
4038
|
const DomainDot = tt(Domain, DOT);
|
|
4027
4039
|
tt(DomainHyphen, HYPHEN, DomainHyphen);
|
|
@@ -4085,11 +4097,11 @@ function init$1({
|
|
|
4085
4097
|
const [OPEN, CLOSE] = bracketPairs[i];
|
|
4086
4098
|
const UrlOpen = tt(Url$1, OPEN);
|
|
4087
4099
|
tt(UrlNonaccept, OPEN, UrlOpen);
|
|
4088
|
-
tt(UrlOpen, CLOSE, Url$1);
|
|
4089
4100
|
const UrlOpenQ = makeState(Url);
|
|
4090
4101
|
ta(UrlOpen, qsAccepting, UrlOpenQ);
|
|
4091
4102
|
const UrlOpenSyms = makeState();
|
|
4092
|
-
ta(UrlOpen, qsNonAccepting);
|
|
4103
|
+
ta(UrlOpen, qsNonAccepting, UrlOpenSyms);
|
|
4104
|
+
tt(UrlOpen, CLOSE, Url$1);
|
|
4093
4105
|
ta(UrlOpenQ, qsAccepting, UrlOpenQ);
|
|
4094
4106
|
ta(UrlOpenQ, qsNonAccepting, UrlOpenSyms);
|
|
4095
4107
|
ta(UrlOpenSyms, qsAccepting, UrlOpenQ);
|
|
@@ -5636,13 +5648,13 @@ var Video = ({ src, children }) => {
|
|
|
5636
5648
|
};
|
|
5637
5649
|
|
|
5638
5650
|
// src/editor/controls/emoji-picker.tsx
|
|
5639
|
-
import { useEffect as
|
|
5651
|
+
import { useEffect as useEffect9, useRef as useRef10 } from "react";
|
|
5640
5652
|
import EmojiPicker from "emoji-picker-react";
|
|
5641
5653
|
import { jsx as jsx83 } from "react/jsx-runtime";
|
|
5642
5654
|
var EmojiPickerComponent = ({ isOpen, onClose, onEmojiSelect }) => {
|
|
5643
5655
|
const pickerRef = useRef10(null);
|
|
5644
5656
|
const translations2 = useTiptapTranslations();
|
|
5645
|
-
|
|
5657
|
+
useEffect9(() => {
|
|
5646
5658
|
const handleClickOutside = (event) => {
|
|
5647
5659
|
if (pickerRef.current && !pickerRef.current.contains(event.target)) {
|
|
5648
5660
|
onClose();
|
|
@@ -5984,11 +5996,11 @@ import TextAlign from "@tiptap/extension-text-align";
|
|
|
5984
5996
|
|
|
5985
5997
|
// node_modules/@tiptap/extension-text-style/dist/index.js
|
|
5986
5998
|
import { Mark as Mark2, mergeAttributes as mergeAttributes8 } from "@tiptap/core";
|
|
5987
|
-
import { Extension as Extension2 } from "@tiptap/core";
|
|
5988
|
-
import { Extension as Extension22 } from "@tiptap/core";
|
|
5989
|
-
import { Extension as Extension3 } from "@tiptap/core";
|
|
5990
|
-
import { Extension as Extension4 } from "@tiptap/core";
|
|
5991
|
-
import { Extension as Extension5 } from "@tiptap/core";
|
|
5999
|
+
import { Extension as Extension2, getStyleProperty } from "@tiptap/core";
|
|
6000
|
+
import { Extension as Extension22, getStyleProperty as getStyleProperty2 } from "@tiptap/core";
|
|
6001
|
+
import { Extension as Extension3, getStyleProperty as getStyleProperty3 } from "@tiptap/core";
|
|
6002
|
+
import { Extension as Extension4, getStyleProperty as getStyleProperty4 } from "@tiptap/core";
|
|
6003
|
+
import { Extension as Extension5, getStyleProperty as getStyleProperty5 } from "@tiptap/core";
|
|
5992
6004
|
import { Extension as Extension6 } from "@tiptap/core";
|
|
5993
6005
|
var MAX_FIND_CHILD_SPAN_DEPTH = 20;
|
|
5994
6006
|
var findChildSpans = (element, depth = 0) => {
|
|
@@ -6086,21 +6098,8 @@ var BackgroundColor = Extension2.create({
|
|
|
6086
6098
|
default: null,
|
|
6087
6099
|
parseHTML: (element) => {
|
|
6088
6100
|
var _a;
|
|
6089
|
-
const
|
|
6090
|
-
|
|
6091
|
-
const decls = styleAttr.split(";").map((s) => s.trim()).filter(Boolean);
|
|
6092
|
-
for (let i = decls.length - 1; i >= 0; i -= 1) {
|
|
6093
|
-
const parts = decls[i].split(":");
|
|
6094
|
-
if (parts.length >= 2) {
|
|
6095
|
-
const prop = parts[0].trim().toLowerCase();
|
|
6096
|
-
const val = parts.slice(1).join(":").trim();
|
|
6097
|
-
if (prop === "background-color") {
|
|
6098
|
-
return val.replace(/['"]+/g, "");
|
|
6099
|
-
}
|
|
6100
|
-
}
|
|
6101
|
-
}
|
|
6102
|
-
}
|
|
6103
|
-
return (_a = element.style.backgroundColor) == null ? void 0 : _a.replace(/['"]+/g, "");
|
|
6101
|
+
const value = (_a = getStyleProperty(element, "background-color")) != null ? _a : element.style.backgroundColor;
|
|
6102
|
+
return value == null ? void 0 : value.replace(/['"]+/g, "");
|
|
6104
6103
|
},
|
|
6105
6104
|
renderHTML: (attributes) => {
|
|
6106
6105
|
if (!attributes.backgroundColor) {
|
|
@@ -6142,21 +6141,8 @@ var Color = Extension22.create({
|
|
|
6142
6141
|
default: null,
|
|
6143
6142
|
parseHTML: (element) => {
|
|
6144
6143
|
var _a;
|
|
6145
|
-
const
|
|
6146
|
-
|
|
6147
|
-
const decls = styleAttr.split(";").map((s) => s.trim()).filter(Boolean);
|
|
6148
|
-
for (let i = decls.length - 1; i >= 0; i -= 1) {
|
|
6149
|
-
const parts = decls[i].split(":");
|
|
6150
|
-
if (parts.length >= 2) {
|
|
6151
|
-
const prop = parts[0].trim().toLowerCase();
|
|
6152
|
-
const val = parts.slice(1).join(":").trim();
|
|
6153
|
-
if (prop === "color") {
|
|
6154
|
-
return val.replace(/['"]+/g, "");
|
|
6155
|
-
}
|
|
6156
|
-
}
|
|
6157
|
-
}
|
|
6158
|
-
}
|
|
6159
|
-
return (_a = element.style.color) == null ? void 0 : _a.replace(/['"]+/g, "");
|
|
6144
|
+
const value = (_a = getStyleProperty2(element, "color")) != null ? _a : element.style.color;
|
|
6145
|
+
return value == null ? void 0 : value.replace(/['"]+/g, "");
|
|
6160
6146
|
},
|
|
6161
6147
|
renderHTML: (attributes) => {
|
|
6162
6148
|
if (!attributes.color) {
|
|
@@ -6196,7 +6182,14 @@ var FontFamily = Extension3.create({
|
|
|
6196
6182
|
attributes: {
|
|
6197
6183
|
fontFamily: {
|
|
6198
6184
|
default: null,
|
|
6199
|
-
|
|
6185
|
+
// Prefer the raw inline `style` attribute so unquoted or
|
|
6186
|
+
// single-quoted multi-word names are preserved instead of being
|
|
6187
|
+
// canonicalized by `element.style.fontFamily`, which forces double
|
|
6188
|
+
// quotes that then get HTML-encoded to `"` on serialization.
|
|
6189
|
+
parseHTML: (element) => {
|
|
6190
|
+
var _a;
|
|
6191
|
+
return (_a = getStyleProperty3(element, "font-family")) != null ? _a : element.style.fontFamily;
|
|
6192
|
+
},
|
|
6200
6193
|
renderHTML: (attributes) => {
|
|
6201
6194
|
if (!attributes.fontFamily) {
|
|
6202
6195
|
return {};
|
|
@@ -6235,7 +6228,13 @@ var FontSize = Extension4.create({
|
|
|
6235
6228
|
attributes: {
|
|
6236
6229
|
fontSize: {
|
|
6237
6230
|
default: null,
|
|
6238
|
-
|
|
6231
|
+
// Prefer the raw inline `style` attribute so the original format
|
|
6232
|
+
// is preserved instead of the canonicalized value returned by
|
|
6233
|
+
// `element.style.fontSize`.
|
|
6234
|
+
parseHTML: (element) => {
|
|
6235
|
+
var _a;
|
|
6236
|
+
return (_a = getStyleProperty4(element, "font-size")) != null ? _a : element.style.fontSize;
|
|
6237
|
+
},
|
|
6239
6238
|
renderHTML: (attributes) => {
|
|
6240
6239
|
if (!attributes.fontSize) {
|
|
6241
6240
|
return {};
|
|
@@ -6274,7 +6273,13 @@ var LineHeight = Extension5.create({
|
|
|
6274
6273
|
attributes: {
|
|
6275
6274
|
lineHeight: {
|
|
6276
6275
|
default: null,
|
|
6277
|
-
|
|
6276
|
+
// Prefer the raw inline `style` attribute so the original format
|
|
6277
|
+
// is preserved instead of the canonicalized value returned by
|
|
6278
|
+
// `element.style.lineHeight`.
|
|
6279
|
+
parseHTML: (element) => {
|
|
6280
|
+
var _a;
|
|
6281
|
+
return (_a = getStyleProperty5(element, "line-height")) != null ? _a : element.style.lineHeight;
|
|
6282
|
+
},
|
|
6278
6283
|
renderHTML: (attributes) => {
|
|
6279
6284
|
if (!attributes.lineHeight) {
|
|
6280
6285
|
return {};
|
|
@@ -6737,6 +6742,106 @@ var createHandleKatexAdd = (editor, closeDialogCommand, savedPos) => {
|
|
|
6737
6742
|
};
|
|
6738
6743
|
};
|
|
6739
6744
|
|
|
6745
|
+
// src/editor/handlers/insertion-handlers.ts
|
|
6746
|
+
import { useEffect as useEffect10, useState as useState12 } from "react";
|
|
6747
|
+
|
|
6748
|
+
// src/utils/prosemirror-iterator.ts
|
|
6749
|
+
import { Fragment as Fragment3, Slice } from "@tiptap/pm/model";
|
|
6750
|
+
function prosemirrorIterator(slice, cb) {
|
|
6751
|
+
const stack = [];
|
|
6752
|
+
if (slice instanceof Slice) {
|
|
6753
|
+
collectFragmentChildren(slice.content, stack);
|
|
6754
|
+
} else if (slice instanceof Fragment3) {
|
|
6755
|
+
collectFragmentChildren(slice, stack);
|
|
6756
|
+
} else {
|
|
6757
|
+
stack.push(slice);
|
|
6758
|
+
}
|
|
6759
|
+
while (stack.length > 0) {
|
|
6760
|
+
const node = stack.pop();
|
|
6761
|
+
const shouldStop = cb(node);
|
|
6762
|
+
if (shouldStop === true) break;
|
|
6763
|
+
if (node.content.size > 0) {
|
|
6764
|
+
collectFragmentChildren(node.content, stack);
|
|
6765
|
+
}
|
|
6766
|
+
}
|
|
6767
|
+
}
|
|
6768
|
+
function collectFragmentChildren(fragment, stack) {
|
|
6769
|
+
for (let i = fragment.childCount - 1; i >= 0; i--) {
|
|
6770
|
+
stack.push(fragment.child(i));
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
|
|
6774
|
+
// src/editor/handlers/insertion-handlers.ts
|
|
6775
|
+
function useInsertionHandlers() {
|
|
6776
|
+
const [insertedImage, setInsertedImage] = useState12(null);
|
|
6777
|
+
const { activeDialog, openDialogCommand } = useDialogCommand();
|
|
6778
|
+
useEffect10(() => {
|
|
6779
|
+
if (insertedImage && !activeDialog) {
|
|
6780
|
+
setInsertedImage(null);
|
|
6781
|
+
}
|
|
6782
|
+
}, [insertedImage, activeDialog]);
|
|
6783
|
+
function genericInsertionHandler(file, event) {
|
|
6784
|
+
if (!file || !file.type.startsWith("image/")) return;
|
|
6785
|
+
event.preventDefault();
|
|
6786
|
+
event.stopPropagation();
|
|
6787
|
+
setInsertedImage(file);
|
|
6788
|
+
openDialogCommand("image");
|
|
6789
|
+
}
|
|
6790
|
+
function onPaste(_, event) {
|
|
6791
|
+
const file = event.clipboardData?.files?.[0];
|
|
6792
|
+
genericInsertionHandler(file, event);
|
|
6793
|
+
}
|
|
6794
|
+
function onDrop(_, event) {
|
|
6795
|
+
const file = event.dataTransfer?.files?.[0];
|
|
6796
|
+
genericInsertionHandler(file, event);
|
|
6797
|
+
}
|
|
6798
|
+
function onMarkupInsertion(view, event, slice, setDisabled, uploadMultipleByURL) {
|
|
6799
|
+
if (!uploadMultipleByURL) return;
|
|
6800
|
+
let hasMarkupInsertion = false;
|
|
6801
|
+
if ("clipboardData" in event && Array.from(event.clipboardData?.items ?? []).some(
|
|
6802
|
+
(item) => item.type === "text/html"
|
|
6803
|
+
)) {
|
|
6804
|
+
hasMarkupInsertion = true;
|
|
6805
|
+
}
|
|
6806
|
+
if ("dataTransfer" in event && Array.from(event.dataTransfer?.items ?? []).some(
|
|
6807
|
+
(item) => item.type === "text/html"
|
|
6808
|
+
)) {
|
|
6809
|
+
hasMarkupInsertion = true;
|
|
6810
|
+
}
|
|
6811
|
+
if (hasMarkupInsertion) {
|
|
6812
|
+
const imagesToNodes = [];
|
|
6813
|
+
setDisabled(true);
|
|
6814
|
+
prosemirrorIterator(slice, (node) => {
|
|
6815
|
+
if (node.type.name === "image" && !node.attrs.fileId) {
|
|
6816
|
+
try {
|
|
6817
|
+
const url = new URL(node.attrs.src).toString();
|
|
6818
|
+
imagesToNodes.push([url, node]);
|
|
6819
|
+
} catch {
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
});
|
|
6823
|
+
uploadMultipleByURL(imagesToNodes.map(([url]) => url)).then((results) => {
|
|
6824
|
+
results.forEach((result, index) => {
|
|
6825
|
+
const node = imagesToNodes[index][1];
|
|
6826
|
+
node.attrs.src = null;
|
|
6827
|
+
node.attrs.fileId = result?.fileId;
|
|
6828
|
+
node.attrs.storageKey = result?.storageKey;
|
|
6829
|
+
});
|
|
6830
|
+
view.dispatch(view.state.tr.replaceSelection(slice));
|
|
6831
|
+
}).finally(() => {
|
|
6832
|
+
setDisabled(false);
|
|
6833
|
+
});
|
|
6834
|
+
return true;
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
return {
|
|
6838
|
+
insertedImage,
|
|
6839
|
+
onPaste,
|
|
6840
|
+
onDrop,
|
|
6841
|
+
onMarkupInsertion
|
|
6842
|
+
};
|
|
6843
|
+
}
|
|
6844
|
+
|
|
6740
6845
|
// src/editor/commands/suggestion.tsx
|
|
6741
6846
|
import { computePosition, flip, shift } from "@floating-ui/dom";
|
|
6742
6847
|
import { posToDOMRect } from "@tiptap/core";
|
|
@@ -7486,7 +7591,7 @@ function getTranslations(lang) {
|
|
|
7486
7591
|
}
|
|
7487
7592
|
|
|
7488
7593
|
// src/editor/editor.tsx
|
|
7489
|
-
import { Fragment as
|
|
7594
|
+
import { Fragment as Fragment4, jsx as jsx92, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
7490
7595
|
function TiptapContent({
|
|
7491
7596
|
content,
|
|
7492
7597
|
handleChange,
|
|
@@ -7496,27 +7601,35 @@ function TiptapContent({
|
|
|
7496
7601
|
lang,
|
|
7497
7602
|
error
|
|
7498
7603
|
}) {
|
|
7499
|
-
const [linkDialogContext, setLinkDialogContext] =
|
|
7500
|
-
const [katexDialogContext, setKatexDialogContext] =
|
|
7604
|
+
const [linkDialogContext, setLinkDialogContext] = useState13(null);
|
|
7605
|
+
const [katexDialogContext, setKatexDialogContext] = useState13(
|
|
7501
7606
|
null
|
|
7502
7607
|
);
|
|
7503
|
-
const [editFigureContext, setEditFigureContext] =
|
|
7504
|
-
const [savedDialogCursorPos, setSavedDialogCursorPos] =
|
|
7608
|
+
const [editFigureContext, setEditFigureContext] = useState13(null);
|
|
7609
|
+
const [savedDialogCursorPos, setSavedDialogCursorPos] = useState13(void 0);
|
|
7505
7610
|
const { activeDialog, closeDialogCommand, openDialogCommand } = useDialogCommand();
|
|
7506
7611
|
const translations2 = useTiptapTranslations();
|
|
7612
|
+
const [isDisabledInternal, setIsDisabledInternal] = useState13(isDisabled);
|
|
7613
|
+
const disabled = isDisabled || isDisabledInternal;
|
|
7507
7614
|
let safeContent;
|
|
7508
7615
|
try {
|
|
7509
7616
|
safeContent = JSON.parse(content);
|
|
7510
7617
|
} catch {
|
|
7511
7618
|
safeContent = { type: "doc", content: [{ type: "paragraph" }] };
|
|
7512
7619
|
}
|
|
7620
|
+
const {
|
|
7621
|
+
insertedImage,
|
|
7622
|
+
onPaste,
|
|
7623
|
+
onDrop,
|
|
7624
|
+
onMarkupInsertion
|
|
7625
|
+
} = useInsertionHandlers();
|
|
7513
7626
|
const editor = useEditor(
|
|
7514
7627
|
{
|
|
7515
|
-
editable: isEditable && !
|
|
7628
|
+
editable: isEditable && !disabled,
|
|
7516
7629
|
extensions: getEditorExtensions({
|
|
7517
7630
|
translations: translations2,
|
|
7518
7631
|
suggestion: createSuggestions(translations2, !!imageHandlers),
|
|
7519
|
-
isEditable: isEditable && !
|
|
7632
|
+
isEditable: isEditable && !disabled,
|
|
7520
7633
|
getImageById: imageHandlers?.getImage,
|
|
7521
7634
|
onKatexClick: (latex, pos) => {
|
|
7522
7635
|
setKatexDialogContext({ latex, pos });
|
|
@@ -7530,14 +7643,38 @@ function TiptapContent({
|
|
|
7530
7643
|
content: safeContent,
|
|
7531
7644
|
immediatelyRender: false,
|
|
7532
7645
|
editorProps: {
|
|
7533
|
-
|
|
7646
|
+
handleDOMEvents: {
|
|
7647
|
+
paste: onPaste,
|
|
7648
|
+
drop: onDrop
|
|
7649
|
+
},
|
|
7650
|
+
handleDrop: (view, event, slice) => {
|
|
7651
|
+
if (view.dragging) return false;
|
|
7652
|
+
const toReturn = onMarkupInsertion(
|
|
7653
|
+
view,
|
|
7654
|
+
event,
|
|
7655
|
+
slice,
|
|
7656
|
+
setIsDisabledInternal,
|
|
7657
|
+
imageHandlers?.uploadMultipleByURL
|
|
7658
|
+
);
|
|
7659
|
+
if (typeof toReturn === "boolean") return toReturn;
|
|
7660
|
+
return false;
|
|
7661
|
+
},
|
|
7662
|
+
handlePaste: (view, event, slice) => {
|
|
7663
|
+
const toReturn = onMarkupInsertion(
|
|
7664
|
+
view,
|
|
7665
|
+
event,
|
|
7666
|
+
slice,
|
|
7667
|
+
setIsDisabledInternal,
|
|
7668
|
+
imageHandlers?.uploadMultipleByURL
|
|
7669
|
+
);
|
|
7670
|
+
if (typeof toReturn === "boolean") return toReturn;
|
|
7534
7671
|
try {
|
|
7535
7672
|
const text = event.clipboardData?.getData("text/plain");
|
|
7536
7673
|
if (!text) return false;
|
|
7537
7674
|
const json = JSON.parse(text);
|
|
7538
7675
|
if (json.type === "doc" && json.content) {
|
|
7539
7676
|
const node = view.state.schema.nodeFromJSON(json);
|
|
7540
|
-
const newSlice = new
|
|
7677
|
+
const newSlice = new Slice2(node.content, 0, 0);
|
|
7541
7678
|
view.dispatch(view.state.tr.replaceSelection(newSlice));
|
|
7542
7679
|
return true;
|
|
7543
7680
|
}
|
|
@@ -7552,7 +7689,7 @@ function TiptapContent({
|
|
|
7552
7689
|
},
|
|
7553
7690
|
[lang]
|
|
7554
7691
|
);
|
|
7555
|
-
|
|
7692
|
+
useEffect11(() => {
|
|
7556
7693
|
if (activeDialog && editor && !editor.isDestroyed) {
|
|
7557
7694
|
const pos = editor.state.selection.from;
|
|
7558
7695
|
setSavedDialogCursorPos(pos);
|
|
@@ -7579,7 +7716,7 @@ function TiptapContent({
|
|
|
7579
7716
|
const katexHandler = createHandleKatexAdd(editor, handleCloseDialog, savedDialogCursorPos);
|
|
7580
7717
|
katexHandler(latex, katexDialogContext?.pos);
|
|
7581
7718
|
};
|
|
7582
|
-
|
|
7719
|
+
useEffect11(() => {
|
|
7583
7720
|
if (!editor || editor.isDestroyed) return;
|
|
7584
7721
|
editor.commands.setContent(safeContent, {
|
|
7585
7722
|
emitUpdate: false
|
|
@@ -7598,10 +7735,10 @@ function TiptapContent({
|
|
|
7598
7735
|
return true;
|
|
7599
7736
|
};
|
|
7600
7737
|
if (!editor) return null;
|
|
7601
|
-
return /* @__PURE__ */ jsxs35(
|
|
7738
|
+
return /* @__PURE__ */ jsxs35(Fragment4, { children: [
|
|
7602
7739
|
/* @__PURE__ */ jsx92(DragHandle, { editor }),
|
|
7603
|
-
isEditable && !
|
|
7604
|
-
isEditable && !
|
|
7740
|
+
isEditable && !disabled && /* @__PURE__ */ jsx92(TableMenu, { editor }),
|
|
7741
|
+
isEditable && !disabled && /* @__PURE__ */ jsx92(
|
|
7605
7742
|
BubbleMenu,
|
|
7606
7743
|
{
|
|
7607
7744
|
shouldShow: shouldBubbleMenuShow,
|
|
@@ -7619,20 +7756,21 @@ function TiptapContent({
|
|
|
7619
7756
|
/* @__PURE__ */ jsxs35(
|
|
7620
7757
|
"div",
|
|
7621
7758
|
{
|
|
7622
|
-
className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${
|
|
7759
|
+
className: `tiptap-content-wrapper ${!!error ? "has-error" : ""} ${disabled ? "is-disabled" : ""}`,
|
|
7623
7760
|
children: [
|
|
7624
7761
|
/* @__PURE__ */ jsx92(EditorContent, { editor }),
|
|
7625
|
-
isEditable && !
|
|
7762
|
+
isEditable && !disabled && /* @__PURE__ */ jsx92(CharacterCount, { editor })
|
|
7626
7763
|
]
|
|
7627
7764
|
}
|
|
7628
7765
|
),
|
|
7629
7766
|
!!error && /* @__PURE__ */ jsx92("p", { className: "tiptap-error-message", children: error }),
|
|
7630
|
-
isEditable && /* @__PURE__ */ jsxs35(
|
|
7767
|
+
isEditable && /* @__PURE__ */ jsxs35(Fragment4, { children: [
|
|
7631
7768
|
activeDialog === "image" && /* @__PURE__ */ jsx92(
|
|
7632
7769
|
ImageDialog,
|
|
7633
7770
|
{
|
|
7634
7771
|
open: true,
|
|
7635
7772
|
onClose: handleCloseDialog,
|
|
7773
|
+
defaultFile: insertedImage,
|
|
7636
7774
|
onImageAdd: handleImageAdd,
|
|
7637
7775
|
onImageEdit: handleImageEdit,
|
|
7638
7776
|
uploadHandler: imageHandlers?.upload,
|