@pie-lib/editable-html-tip-tap 3.0.3-next.20261007075859 → 3.0.3-next.20261007181944

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.
@@ -5,42 +5,42 @@ import { ExtendedTableCell as r, ExtendedTableHeader as i } from "../extensions/
5
5
  import { DivNode as a } from "../extensions/div-node.js";
6
6
  import { EnsureEmptyRootIsDiv as ee } from "../extensions/ensure-empty-root-div.js";
7
7
  import { EnsureListItemContentIsDiv as te } from "../extensions/ensure-list-item-content-is-div.js";
8
- import { DragInTheBlankNode as ne, ExplicitConstructedResponseNode as re, InlineDropdownNode as ie, MathTemplatedNode as ae, ResponseAreaExtension as o } from "../extensions/responseArea.js";
9
- import { MathNode as s } from "../extensions/math.js";
10
- import { ImageUploadNode as c } from "../extensions/image.js";
11
- import { Media as l } from "../extensions/media.js";
8
+ import { DragInTheBlankNode as o, ExplicitConstructedResponseNode as s, InlineDropdownNode as c, MathTemplatedNode as l, ResponseAreaExtension as u } from "../extensions/responseArea.js";
9
+ import { MathNode as ne } from "../extensions/math.js";
10
+ import { ImageUploadNode as re, uploadPastedImage as ie } from "../extensions/image.js";
11
+ import { Media as ae } from "../extensions/media.js";
12
12
  import { CSSMark as oe } from "../extensions/css.js";
13
- import { ExtendedListItem as u } from "../extensions/extended-list-item.js";
14
- import { HeadingParagraph as se } from "../extensions/heading-paragraph.js";
13
+ import { ExtendedListItem as se } from "../extensions/extended-list-item.js";
14
+ import { HeadingParagraph as ce } from "../extensions/heading-paragraph.js";
15
15
  import { valueToSize as d } from "../utils/size.js";
16
- import ce from "./TiptapContainer.js";
17
- import { PLUGINS_MAP as le, buildExtensions as ue } from "../extensions/index.js";
18
- import { useCallback as f, useEffect as p, useMemo as m, useRef as de, useState as h } from "react";
19
- import { debounce as fe } from "@pie-element/shared-lodash";
20
- import { EditorContent as g, useEditor as _, useEditorState as v } from "@tiptap/react";
21
- import { styled as y } from "@mui/material/styles";
22
- import b from "@tiptap/starter-kit";
23
- import { TextStyleKit as x } from "@tiptap/extension-text-style";
24
- import { CharacterCount as S } from "@tiptap/extension-character-count";
25
- import C from "@tiptap/extension-superscript";
26
- import w from "@tiptap/extension-subscript";
27
- import T from "@tiptap/extension-text-align";
28
- import E from "@tiptap/extension-image";
29
- import D from "@tiptap/extension-placeholder";
30
- import { TableRow as O } from "@tiptap/extension-table-row";
31
- import { jsx as k } from "react/jsx-runtime";
16
+ import le from "./TiptapContainer.js";
17
+ import { PLUGINS_MAP as ue, buildExtensions as f } from "../extensions/index.js";
18
+ import { useCallback as p, useEffect as m, useMemo as h, useRef as g, useState as _ } from "react";
19
+ import { debounce as de } from "@pie-element/shared-lodash";
20
+ import { EditorContent as v, useEditor as y, useEditorState as b } from "@tiptap/react";
21
+ import { styled as x } from "@mui/material/styles";
22
+ import S from "@tiptap/starter-kit";
23
+ import { TextStyleKit as C } from "@tiptap/extension-text-style";
24
+ import { CharacterCount as w } from "@tiptap/extension-character-count";
25
+ import T from "@tiptap/extension-superscript";
26
+ import E from "@tiptap/extension-subscript";
27
+ import D from "@tiptap/extension-text-align";
28
+ import O from "@tiptap/extension-image";
29
+ import k from "@tiptap/extension-placeholder";
30
+ import { TableRow as A } from "@tiptap/extension-table-row";
31
+ import { jsx as j } from "react/jsx-runtime";
32
32
  //#region src/components/EditableHtml.tsx
33
- var A = {
33
+ var M = {
34
34
  position: "bottom",
35
35
  alignment: "left",
36
36
  alwaysVisible: !1,
37
37
  showDone: !0,
38
38
  doneOn: "blur"
39
- }, j = {
39
+ }, N = {
40
40
  options: {},
41
41
  respAreaToolbar: () => {},
42
42
  onHandleAreaChange: () => {}
43
- }, M = [
43
+ }, P = [
44
44
  "bold",
45
45
  "italic",
46
46
  "underline",
@@ -62,7 +62,7 @@ var A = {
62
62
  "h3",
63
63
  "undo",
64
64
  "redo"
65
- ], N = {
65
+ ], F = {
66
66
  bold: "bold",
67
67
  italic: "italic",
68
68
  underline: "underline",
@@ -71,8 +71,9 @@ var A = {
71
71
  subscript: "subscript",
72
72
  "bulleted-list": "bulletList",
73
73
  "numbered-list": "orderedList",
74
- table: "table"
75
- }, P = {
74
+ table: "table",
75
+ image: "image"
76
+ }, I = {
76
77
  "--white": "#fff",
77
78
  "--black": "#2e2b29",
78
79
  "--black-contrast": "#110f0e",
@@ -91,7 +92,7 @@ var A = {
91
92
  "--red": "#ff5c33",
92
93
  "--red-light": "#ffebe5",
93
94
  "--shadow": "0px 12px 33px 0px rgba(0, 0, 0, .06),\n 0px 3.618px 9.949px 0px rgba(0, 0, 0, .04)"
94
- }, F = (e, t, n) => ({
95
+ }, L = (e, t, n) => ({
95
96
  role: "textbox",
96
97
  "data-pie-editor": "true",
97
98
  class: "mathjax_ignore",
@@ -100,55 +101,55 @@ var A = {
100
101
  autocapitalize: e ? "on" : "off",
101
102
  ...t && { "aria-label": t },
102
103
  ...n && { "aria-labelledby": n }
103
- }), I = ["aria-label", "aria-labelledby"], L = (g) => {
104
- let { showParagraphs: y, separateParagraphs: L } = g.pluginProps || {}, [pe, z] = h([]), [B, V] = h(!1), { toolbarOpts: me } = g, H = f((e) => {
105
- z((t) => {
104
+ }), R = ["aria-label", "aria-labelledby"], z = (v) => {
105
+ let { showParagraphs: x, separateParagraphs: z } = v.pluginProps || {}, [fe, V] = _([]), [H, pe] = _(!1), { toolbarOpts: me } = v, U = p((e) => {
106
+ V((t) => {
106
107
  let n = t.filter((t) => t.pos !== e);
107
- return n.length === 0 && V(!1), n;
108
+ return n.length === 0 && pe(!1), n;
108
109
  });
109
- }, [z]), U = {
110
- ...A,
110
+ }, [V]), W = {
111
+ ...M,
111
112
  ...me
112
- }, W = f((e) => {
113
+ }, G = p((e) => {
113
114
  let t = e.getHTML();
114
- g.markup !== t && g.onChange?.(t), U.doneOn === "blur" && g.onDone?.(t);
115
+ v.markup !== t && v.onChange?.(t), W.doneOn === "blur" && v.onDone?.(t);
115
116
  }, [
116
- g.markup,
117
- g.onChange,
118
- g.onDone,
119
- U.doneOn
120
- ]), G = m(() => ({
121
- ...j,
122
- ...g.responseAreaProps,
123
- onInlineDropdownToolbarClose: W
124
- }), [g.responseAreaProps, W]), K = m(() => {
125
- let { customPlugins: e, ...t } = g.pluginProps || {};
117
+ v.markup,
118
+ v.onChange,
119
+ v.onDone,
120
+ W.doneOn
121
+ ]), K = h(() => ({
122
+ ...N,
123
+ ...v.responseAreaProps,
124
+ onInlineDropdownToolbarClose: G
125
+ }), [v.responseAreaProps, G]), q = h(() => {
126
+ let { customPlugins: e, ...t } = v.pluginProps || {};
126
127
  e ||= [];
127
- let n = (g.activePlugins || M)?.filter((e) => {
128
- let n = le[e] || e, r = t[n] || {};
128
+ let n = (v.activePlugins || P)?.filter((e) => {
129
+ let n = ue[e] || e, r = t[n] || {};
129
130
  return !r || !r.disabled;
130
131
  });
131
- return ue(n, e, {
132
+ return f(n, e, {
132
133
  math: {},
133
- textAlign: g.textAlign,
134
+ textAlign: v.textAlign,
134
135
  html: {},
135
- extraCSSRules: g.extraCSSRules || {},
136
- image: { ...g.imageSupport },
136
+ extraCSSRules: v.extraCSSRules || {},
137
+ image: { ...v.imageSupport },
137
138
  toolbar: {},
138
139
  table: {},
139
- responseArea: { type: G.type },
140
- languageCharacters: g.languageCharactersProps,
140
+ responseArea: { type: K.type },
141
+ languageCharacters: v.languageCharactersProps,
141
142
  keyPadCharacterRef: {},
142
143
  setKeypadInteraction: {},
143
144
  media: {}
144
145
  });
145
- }, [g, G.type]), q = de(null);
146
- q.current = {
147
- plainText: g.pluginProps?.pasteFormatting?.disabled === !0,
148
- formatting: K.map((e) => N[e]).filter(Boolean)
146
+ }, [v, K.type]), J = g(null);
147
+ J.current = {
148
+ plainText: v.pluginProps?.pasteFormatting?.disabled === !0,
149
+ formatting: q.map((e) => F[e]).filter(Boolean)
149
150
  };
150
- let J = [
151
- T.configure({
151
+ let he = [
152
+ D.configure({
152
153
  types: [
153
154
  "heading",
154
155
  "paragraph",
@@ -170,10 +171,13 @@ var A = {
170
171
  "justify"
171
172
  ]
172
173
  }),
173
- x,
174
- e.configure({ settings: () => q.current }),
175
- S.configure({ limit: g.charactersLimit || 1e6 }),
176
- b.configure({
174
+ C,
175
+ e.configure({
176
+ settings: () => J.current,
177
+ uploadImage: v.imageSupport && ie
178
+ }),
179
+ w.configure({ limit: v.charactersLimit || 1e6 }),
180
+ S.configure({
177
181
  trailingNode: {
178
182
  node: "paragraph",
179
183
  notAfter: ["paragraph", "div"]
@@ -183,95 +187,95 @@ var A = {
183
187
  linkOnPaste: !1
184
188
  }
185
189
  }),
186
- u,
187
- a,
188
190
  se,
191
+ a,
192
+ ce,
189
193
  ee,
190
194
  te,
191
- D.configure({
192
- placeholder: g.placeholder,
195
+ k.configure({
196
+ placeholder: v.placeholder,
193
197
  showOnlyWhenEditable: !0,
194
198
  showOnlyCurrent: !1,
195
199
  includeChildren: !0
196
200
  }),
197
201
  n,
198
- O,
202
+ A,
199
203
  i,
200
204
  r,
201
- o.configure(G),
202
- re.configure(G),
203
- ne.configure(G),
204
- ie.configure(G),
205
- ae.configure(G),
206
- s.configure({
207
- toolbarOpts: U,
208
- math: g.pluginProps?.math || {}
205
+ u.configure(K),
206
+ s.configure(K),
207
+ o.configure(K),
208
+ c.configure(K),
209
+ l.configure(K),
210
+ ne.configure({
211
+ toolbarOpts: W,
212
+ math: v.pluginProps?.math || {}
209
213
  }),
210
- w,
211
- C,
212
214
  E,
213
- c.configure({
214
- toolbarOpts: U,
215
+ T,
216
+ O,
217
+ re.configure({
218
+ toolbarOpts: W,
215
219
  imageHandling: {
216
- disableImageAlignmentButtons: g.disableImageAlignmentButtons,
217
- onDone: (e) => g.onDone?.(e.getHTML()),
218
- onDelete: g.imageSupport && g.imageSupport.delete && ((e) => {
220
+ disableImageAlignmentButtons: v.disableImageAlignmentButtons,
221
+ onDone: (e) => v.onDone?.(e.getHTML()),
222
+ onDelete: v.imageSupport && v.imageSupport.delete && ((e) => {
219
223
  let { src: t } = e.attrs;
220
- g.imageSupport.delete(t, (t) => {
221
- H(e.pos);
224
+ v.imageSupport.delete(t, (t) => {
225
+ U(e.pos);
222
226
  });
223
227
  }),
224
- insertImageRequested: g.imageSupport && ((e, t, n) => {
228
+ insertImageRequested: v.imageSupport && ((e, t, n) => {
225
229
  let [r, i] = t, a = (t) => {
226
230
  let n;
227
- B && t && (n = g.onChange), H(i), n?.(e.getHTML());
231
+ H && t && (n = v.onChange), U(i), n?.(e.getHTML());
228
232
  };
229
- e._insertingImage = !0, z((e) => [...e, r]), (() => {
230
- let t = n(a), r = fe(() => {
233
+ e._insertingImage = !0, V((e) => [...e, r]), (() => {
234
+ let t = n(a), r = de(() => {
231
235
  let t = () => window.removeEventListener("focus", r);
232
236
  if (!e._insertingImage) {
233
237
  t();
234
238
  return;
235
239
  }
236
- H(i), e._insertingImage = !1, t();
240
+ U(i), e._insertingImage = !1, t();
237
241
  }, 500);
238
- window.addEventListener("focus", r), g.imageSupport.add(t);
242
+ window.addEventListener("focus", r), v.imageSupport.add(t);
239
243
  })();
240
244
  }),
241
- maxImageWidth: g.maxImageWidth,
242
- maxImageHeight: g.maxImageHeight
245
+ maxImageWidth: v.maxImageWidth,
246
+ maxImageHeight: v.maxImageHeight
243
247
  },
244
248
  limit: 3
245
249
  }),
246
- l.configure({ uploadSoundSupport: g.uploadSoundSupport }),
247
- oe.configure({ extraCSSRules: g.extraCSSRules })
248
- ], Y = g.spellCheck !== !1, { ariaLabel: X, ariaLabelledBy: Z } = g, Q = _({
249
- extensions: J,
250
+ ae.configure({ uploadSoundSupport: v.uploadSoundSupport }),
251
+ oe.configure({ extraCSSRules: v.extraCSSRules })
252
+ ], Y = v.spellCheck !== !1, { ariaLabel: X, ariaLabelledBy: Z } = v, Q = y({
253
+ extensions: he,
250
254
  immediatelyRender: !1,
251
255
  editorProps: {
252
- attributes: F(Y, X, Z),
256
+ attributes: L(Y, X, Z),
253
257
  handleKeyDown(e, t) {
254
- return g.onKeyDown ? g.onKeyDown(t) : !1;
258
+ return v.onKeyDown ? v.onKeyDown(t) : !1;
255
259
  }
256
260
  },
257
- editable: !g.disabled,
258
- content: t(g.markup),
261
+ editable: !v.disabled,
262
+ content: t(v.markup),
259
263
  onUpdate: ({ editor: e, transaction: t }) => {
260
- t.isDone && g.onChange?.(e.getHTML());
264
+ t.isDone && v.onChange?.(e.getHTML());
261
265
  },
262
266
  onBlur: ({ editor: e }) => {
263
- e._insertingImage || e._toolbarOpened || e.isActive("inline_dropdown") || e.isActive("explicit_constructed_response") || !e.schema || W(e);
267
+ e._insertingImage || e._toolbarOpened || e.isActive("inline_dropdown") || e.isActive("explicit_constructed_response") || !e.schema || G(e);
264
268
  }
265
- }, [g.charactersLimit]);
266
- p(() => {
267
- g.editorRef && g.editorRef(Q);
268
- }, [g.editorRef, Q]), p(() => {
269
- Q?.setEditable(!g.disabled);
270
- }, [g.disabled, Q]), p(() => {
269
+ }, [v.charactersLimit]);
270
+ m(() => {
271
+ v.editorRef && v.editorRef(Q);
272
+ }, [v.editorRef, Q]), m(() => {
273
+ Q?.setEditable(!v.disabled);
274
+ }, [v.disabled, Q]), m(() => {
271
275
  if (!Q) return;
272
- let e = Q.options?.editorProps, t = e?.attributes || {}, n = F(Y, X, Z);
273
- if (["spellcheck", ...I].every((e) => t[e] === n[e])) return;
274
- let r = Object.fromEntries(Object.entries(t).filter(([e]) => !I.includes(e)));
276
+ let e = Q.options?.editorProps, t = e?.attributes || {}, n = L(Y, X, Z);
277
+ if (["spellcheck", ...R].every((e) => t[e] === n[e])) return;
278
+ let r = Object.fromEntries(Object.entries(t).filter(([e]) => !R.includes(e)));
275
279
  Q.setOptions({ editorProps: {
276
280
  ...e,
277
281
  attributes: {
@@ -284,20 +288,20 @@ var A = {
284
288
  X,
285
289
  Z,
286
290
  Q
287
- ]), p(() => {
291
+ ]), m(() => {
288
292
  if (!Q) return;
289
- let e = t(g.markup);
293
+ let e = t(v.markup);
290
294
  e !== Q.getHTML() && Q.commands.setContent(e, { emitUpdate: !1 });
291
- }, [g.markup, Q]), p(() => {
292
- Object.entries(P).forEach(([e, t]) => {
295
+ }, [v.markup, Q]), m(() => {
296
+ Object.entries(I).forEach(([e, t]) => {
293
297
  document.documentElement.style.setProperty(e, t);
294
298
  });
295
299
  }, []);
296
- let he = v({
300
+ let ge = b({
297
301
  editor: Q,
298
302
  selector: (e) => ({ isFocused: e.editor?.isFocused })
299
- }), $ = m(() => {
300
- let { minWidth: e, width: t, maxWidth: n, minHeight: r, height: i, maxHeight: a } = g;
303
+ }), $ = h(() => {
304
+ let { minWidth: e, width: t, maxWidth: n, minHeight: r, height: i, maxHeight: a } = v;
301
305
  return {
302
306
  width: d(t),
303
307
  minWidth: d(e),
@@ -306,25 +310,25 @@ var A = {
306
310
  minHeight: d(r),
307
311
  maxHeight: d(a)
308
312
  };
309
- }, [g]);
310
- return /* @__PURE__ */ k(ce, {
311
- ...g,
312
- activePlugins: K,
313
- toolbarOpts: U,
314
- editorState: he,
313
+ }, [v]);
314
+ return /* @__PURE__ */ j(le, {
315
+ ...v,
316
+ activePlugins: q,
317
+ toolbarOpts: W,
318
+ editorState: ge,
315
319
  editor: Q,
316
- children: Q && /* @__PURE__ */ k(R, {
320
+ children: Q && /* @__PURE__ */ j(B, {
317
321
  style: {
318
322
  minHeight: $.minHeight,
319
323
  height: $.height,
320
324
  maxHeight: $.maxHeight
321
325
  },
322
- showParagraph: y && !y.disabled,
323
- separateParagraph: L && !L.disabled,
326
+ showParagraph: x && !x.disabled,
327
+ separateParagraph: z && !z.disabled,
324
328
  editor: Q
325
329
  })
326
330
  });
327
- }, R = y(g, { shouldForwardProp: (e) => !["showParagraph", "separateParagraph"].includes(e) })(({ showParagraph: e, separateParagraph: t }) => ({
331
+ }, B = x(v, { shouldForwardProp: (e) => !["showParagraph", "separateParagraph"].includes(e) })(({ showParagraph: e, separateParagraph: t }) => ({
328
332
  display: "flex",
329
333
  outline: "none !important",
330
334
  "& .ProseMirror": {
@@ -354,4 +358,4 @@ var A = {
354
358
  }
355
359
  }));
356
360
  //#endregion
357
- export { L as EditableHtml, L as default };
361
+ export { z as EditableHtml, z as default };
@@ -1,2 +1,7 @@
1
1
  import { Node } from '@tiptap/core';
2
+ /**
3
+ * Uploads the image pasted with `nodeKey` and a data URL src, as a pasted image file uploads.
4
+ * Resolves once the upload succeeds or fails.
5
+ */
6
+ export declare function uploadPastedImage(editor: any, nodeKey: any): Promise<unknown>;
2
7
  export declare const ImageUploadNode: Node<any, any>;
@@ -7,7 +7,30 @@ import { Node as a, mergeAttributes as o } from "@tiptap/core";
7
7
  import { Plugin as s } from "@tiptap/pm/state";
8
8
  import { jsx as c } from "react/jsx-runtime";
9
9
  //#region src/extensions/image.tsx
10
- var l = a.create({
10
+ var l = (e) => {
11
+ let [t, n] = e.split(","), r = /^data:([^;,]+)/.exec(t)?.[1] ?? "image/png", i = Uint8Array.from(atob(n), (e) => e.charCodeAt(0));
12
+ return new File([i], `image.${r.split("/")[1]}`, { type: r });
13
+ };
14
+ function u(t, r, i, a, o = () => {}) {
15
+ let s = e(t, i);
16
+ if (!s) {
17
+ o();
18
+ return;
19
+ }
20
+ r(t, s, (e) => {
21
+ let r;
22
+ return r = new n(t, s, (t) => {
23
+ t || r?.updateNode({ loaded: !0 }), e(t), o();
24
+ }, !0), r.fileChosen(a), r;
25
+ });
26
+ }
27
+ function d(t, n) {
28
+ let r = t.extensionManager.extensions.find((e) => e.name === "imageUploadNode")?.options.imageHandling?.insertImageRequested, i = e(t, n)?.[0].attrs.src;
29
+ return new Promise((e) => {
30
+ r && i ? u(t, r, n, l(i), e) : e();
31
+ });
32
+ }
33
+ var f = a.create({
11
34
  name: "imageUploadNode",
12
35
  group: "block",
13
36
  atom: !0,
@@ -45,33 +68,26 @@ var l = a.create({
45
68
  }) };
46
69
  },
47
70
  addProseMirrorPlugins() {
48
- let r = this.editor, i = this.options;
49
- return [new s({ props: { handlePaste(a, o) {
50
- let s = Array.from(o.clipboardData?.items || []).find((e) => e.kind === "file" && e.type.startsWith("image/"));
51
- if (!s) return !1;
52
- let c = s.getAsFile();
53
- if (!c) return !1;
54
- let l = i?.imageHandling?.insertImageRequested, u = t(), d = new FileReader();
55
- return d.onload = () => {
56
- let t = d.result;
57
- if (typeof t != "string" || (r.commands.insertContent({
71
+ let e = this.editor, n = this.options;
72
+ return [new s({ props: { handlePaste(r, i) {
73
+ let a = Array.from(i.clipboardData?.items || []).find((e) => e.kind === "file" && e.type.startsWith("image/"));
74
+ if (!a) return !1;
75
+ let o = a.getAsFile();
76
+ if (!o) return !1;
77
+ let s = n?.imageHandling?.insertImageRequested, c = t(), l = new FileReader();
78
+ return l.onload = () => {
79
+ let t = l.result;
80
+ typeof t == "string" && (e.commands.insertContent({
58
81
  type: "imageUploadNode",
59
82
  attrs: {
60
83
  src: t,
61
- loaded: !l,
62
- nodeKey: u
84
+ loaded: !s,
85
+ nodeKey: c
63
86
  }
64
- }), !l)) return;
65
- let i = e(r, u);
66
- i && l(r, i, (e) => {
67
- let t;
68
- return t = new n(r, i, (n) => {
69
- n || t?.updateNode({ loaded: !0 }), e(n);
70
- }, !0), t.fileChosen(c), t;
71
- });
72
- }, d.readAsDataURL(c), !0;
87
+ }), s && u(e, s, c, o));
88
+ }, l.readAsDataURL(o), !0;
73
89
  } } })];
74
90
  }
75
91
  });
76
92
  //#endregion
77
- export { l as ImageUploadNode };
93
+ export { f as ImageUploadNode, d as uploadPastedImage };
@@ -1,16 +1,27 @@
1
- import { Extension } from '@tiptap/core';
1
+ import { type Editor, Extension } from '@tiptap/core';
2
2
  /** The formatting that content pasted from outside a PIE editor can keep, by mark or node name. */
3
- export declare const PASTED_FORMATTING: readonly ["bold", "italic", "underline", "strike", "superscript", "subscript", "bulletList", "orderedList", "table"];
3
+ export declare const PASTED_FORMATTING: readonly ["bold", "italic", "underline", "strike", "superscript", "subscript", "bulletList", "orderedList", "table", "image"];
4
4
  export type PastedFormatting = (typeof PASTED_FORMATTING)[number];
5
5
  export type ExternalPasteSettings = {
6
6
  /** Paste as plain text, as the Slate editor did. Math copied from rendered PIE content stays math. */
7
7
  plainText: boolean;
8
- /** The formatting a paste keeps. The rest becomes text: a list its items, a table its rows. */
8
+ /**
9
+ * The formatting a paste keeps. The rest becomes text: a list its items, a table its rows.
10
+ * `image` keeps the pictures Word puts in its HTML as data URLs, in an editor with image upload
11
+ * nodes. Other images go.
12
+ */
9
13
  formatting: readonly PastedFormatting[];
10
14
  };
11
15
  export type ExternalPasteOptions = {
12
16
  /** Read at each paste and drop, so a mounted editor follows a change of toolbar or setting. */
13
17
  settings: () => ExternalPasteSettings;
18
+ /**
19
+ * Uploads the picture pasted as the image upload node with `nodeKey`, which shows its data URL
20
+ * until the upload replaces it, and resolves once the upload finishes. Pictures upload one at a
21
+ * time, as a host on pie-player-components serves one upload at a time. Without it, the data
22
+ * URL is the picture's src.
23
+ */
24
+ uploadImage?: (editor: Editor, nodeKey: string) => Promise<void>;
14
25
  };
15
26
  /**
16
27
  * Pastes and drops content from outside a PIE editor, such as from Word or Google Docs, with its
@@ -18,8 +29,9 @@ export type ExternalPasteOptions = {
18
29
  * superscript, subscript, lists and tables in `formatting`. Its fonts, sizes, colours, alignment,
19
30
  * headings, links and class names go, which TextStyleKit, TextAlign and CSSMark would otherwise keep
20
31
  * (PIE-1145). Word for Windows and Mac copies lists as styled paragraphs, which are rebuilt as
21
- * lists first. With `plainText` set it pastes plain text. Copies from a PIE editor and pasted
22
- * images are left to ProseMirror and the image upload handler.
32
+ * lists first. Word's pictures paste as image upload nodes when `formatting` has `image`, and
33
+ * upload through `uploadImage`. With `plainText` set it pastes plain text. Copies from a PIE editor
34
+ * and pasted image files are left to ProseMirror and the image upload handler.
23
35
  *
24
36
  * Mirrored in `packages/lib-svelte/editable-html-tiptap-svelte/src/external-paste.ts`.
25
37
  */
@@ -11,14 +11,22 @@ var a = [
11
11
  "subscript",
12
12
  "bulletList",
13
13
  "orderedList",
14
- "table"
15
- ], o = (e) => Array.from(e.items ?? []).some((e) => e.kind === "file" && e.type.startsWith("image/")), s = (e, t) => new DOMParser().parseFromString(e, "text/html").querySelector(t) !== null, c = (e) => e.includes("data-pm-slice") && s(e, "[data-pm-slice]"), l = (e) => /data-latex|data-type="?mathml/.test(e) && s(e, "[data-latex], [data-type=\"mathml\"]"), u = null, d = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"), f = (e) => e.replace(/\r*\n|\r/g, "\n").replace(/\n+$/, ""), p = (e) => {
14
+ "table",
15
+ "image"
16
+ ], o = (e) => Array.from(e.items ?? []).some((e) => e.kind === "file" && e.type.startsWith("image/")), s = (e, t) => new DOMParser().parseFromString(e, "text/html").querySelector(t) !== null, c = (e) => e.includes("data-pm-slice") && s(e, "[data-pm-slice]"), l = (e) => /data-latex|data-type="?mathml/.test(e) && s(e, "[data-latex], [data-type=\"mathml\"]"), u = null, d = /^data:image\/(?:png|jpeg|gif|webp);base64,/i, f = () => `img-${Date.now()}-${Math.random().toString(36).slice(2)}`;
17
+ function p(e) {
18
+ if (!e.includes("data:image/")) return e;
19
+ let t = new DOMParser().parseFromString(e, "text/html");
20
+ for (let e of Array.from(t.body.querySelectorAll("img"))) d.test(e.getAttribute("src") ?? "") && e.setAttribute("data-type", "image-upload-node");
21
+ return t.body.innerHTML;
22
+ }
23
+ var m = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"), h = (e) => e.replace(/\r*\n|\r/g, "\n").replace(/\n+$/, ""), g = (e) => {
16
24
  let t = [];
17
25
  return e.forEach((e) => {
18
26
  t.push(e);
19
27
  }), t;
20
28
  };
21
- function m(e, t) {
29
+ function _(e, t) {
22
30
  let n = e.topNodeType.create(null, t), r = (e) => {
23
31
  let t = 0;
24
32
  for (let r = n[e]; r && !r.isLeaf && !r.type.spec.isolating; r = r[e]) t += 1;
@@ -26,7 +34,7 @@ function m(e, t) {
26
34
  };
27
35
  return t.length ? n.slice(r("firstChild"), n.content.size - r("lastChild"), !0) : n.slice(0, 0);
28
36
  }
29
- var h = [
37
+ var v = [
30
38
  [1e3, "m"],
31
39
  [900, "cm"],
32
40
  [500, "d"],
@@ -40,19 +48,19 @@ var h = [
40
48
  [5, "v"],
41
49
  [4, "iv"],
42
50
  [1, "i"]
43
- ], g = (e) => h.reduce(({ numeral: e, rest: t }, [n, r]) => ({
51
+ ], y = (e) => v.reduce(({ numeral: e, rest: t }, [n, r]) => ({
44
52
  numeral: e + r.repeat(Math.floor(t / n)),
45
53
  rest: t % n
46
54
  }), {
47
55
  numeral: "",
48
56
  rest: e
49
- }).numeral, _ = (e) => (e > 26 ? _(Math.floor((e - 1) / 26)) : "") + String.fromCharCode(97 + (e - 1) % 26);
50
- function v(e, t) {
57
+ }).numeral, b = (e) => (e > 26 ? b(Math.floor((e - 1) / 26)) : "") + String.fromCharCode(97 + (e - 1) % 26);
58
+ function x(e, t) {
51
59
  if (t < 1 || !e || !/^[aAiI]$/.test(e)) return String(t);
52
- let n = /^[aA]$/.test(e) ? _(t) : g(t);
60
+ let n = /^[aA]$/.test(e) ? b(t) : y(t);
53
61
  return e === e.toUpperCase() ? n.toUpperCase() : n;
54
62
  }
55
- function y(e) {
63
+ function S(e) {
56
64
  let t = e?.getData("text/html");
57
65
  if (!e || !t || c(t)) return null;
58
66
  let n = e.getData("text/plain"), r = Array.from(e.types).includes("text/rtf") && n.trim() !== "";
@@ -61,55 +69,67 @@ function y(e) {
61
69
  html: t
62
70
  };
63
71
  }
64
- var b = (e) => (t, n) => {
65
- let { schema: r } = n, { paragraph: i, hardBreak: a, math: o } = r.nodes, s = (e) => i.create(null, e), c = (t) => t.filter((t) => e.has(t.type.name)).map((e) => e.type.create()), l = (e) => p(e).flatMap((e) => {
72
+ var C = (e, t = null) => (n, r) => {
73
+ let { schema: i } = r, { paragraph: a, hardBreak: o, math: s, imageUploadNode: c } = i.nodes, l = (e) => a.create(null, e), u = (t) => t.filter((t) => e.has(t.type.name)).map((e) => e.type.create()), p = (e) => g(e).flatMap((e) => {
66
74
  if (e.isText) {
67
- let t = c(e.marks);
68
- return (e.text ?? "").split("\n").flatMap((e, n) => [...n && a ? [a.create(null, null, t)] : [], ...e ? [r.text(e, t)] : []]);
75
+ let t = u(e.marks);
76
+ return (e.text ?? "").split("\n").flatMap((e, n) => [...n && o ? [o.create(null, null, t)] : [], ...e ? [i.text(e, t)] : []]);
69
77
  }
70
- return e.type === a || o && e.type === o ? [e.mark(c(e.marks))] : e.isLeaf ? [] : l(e);
71
- }), u = (e) => {
78
+ return e.type === o || s && e.type === s ? [e.mark(u(e.marks))] : e.isLeaf ? [] : p(e);
79
+ }), m = (e) => {
80
+ let { src: n, width: r, height: i, alt: a } = e.attrs;
81
+ if (!t || !d.test(n ?? "")) return [];
82
+ let o = f();
83
+ return t.keys.push(o), [e.type.create({
84
+ src: n,
85
+ width: r,
86
+ height: i,
87
+ alt: a,
88
+ nodeKey: o,
89
+ loaded: !t.upload
90
+ })];
91
+ }, h = (e) => {
72
92
  let t = [];
73
- return e.descendants((e) => e.isTextblock ? (t.length && t.push(r.text(" ")), t.push(...l(e)), !1) : !0), t;
74
- }, d = (t) => {
93
+ return e.descendants((e) => e.isTextblock ? (t.length && t.push(i.text(" ")), t.push(...p(e)), !1) : !0), t;
94
+ }, v = (t) => {
75
95
  let n = t.type.name === "orderedList";
76
96
  if (e.has(t.type.name)) {
77
- let e = p(t).flatMap((e) => e.type.createAndFill(null, h(e)) ?? []), r = n ? {
97
+ let e = g(t).flatMap((e) => e.type.createAndFill(null, C(e)) ?? []), r = n ? {
78
98
  start: t.attrs.start,
79
99
  type: t.attrs.type
80
100
  } : null;
81
101
  return e.length ? [t.type.create(r, e)] : [];
82
102
  }
83
- let a = n ? t.attrs.start ?? 1 : 0;
84
- return p(t).flatMap((e) => {
85
- let o = r.text(n ? `${v(t.attrs.type, a++)}. ` : "• "), [c, ...l] = h(e);
86
- return c?.type === i ? [s([o, ...p(c)]), ...l] : [
87
- s([o]),
88
- ...c ? [c] : [],
89
- ...l
103
+ let r = n ? t.attrs.start ?? 1 : 0;
104
+ return g(t).flatMap((e) => {
105
+ let o = i.text(n ? `${x(t.attrs.type, r++)}. ` : "• "), [s, ...c] = C(e);
106
+ return s?.type === a ? [l([o, ...g(s)]), ...c] : [
107
+ l([o]),
108
+ ...s ? [s] : [],
109
+ ...c
90
110
  ];
91
111
  });
92
- }, f = (t) => {
112
+ }, y = (t) => {
93
113
  let n = t.attrs.caption ?? null;
94
114
  if (e.has("table")) {
95
- let e = p(t).flatMap((e) => {
96
- let t = p(e).flatMap((e) => {
115
+ let e = g(t).flatMap((e) => {
116
+ let t = g(e).flatMap((e) => {
97
117
  let { colspan: t, rowspan: n, scope: r } = e.attrs;
98
118
  return e.type.createAndFill({
99
119
  colspan: t,
100
120
  rowspan: n,
101
121
  scope: r
102
- }, h(e)) ?? [];
122
+ }, C(e)) ?? [];
103
123
  });
104
124
  return t.length ? [e.type.create(null, t)] : [];
105
125
  });
106
126
  return e.length ? [t.type.create({ caption: n }, e)] : [];
107
127
  }
108
- return [...n ? [s([r.text(n)])] : [], ...p(t).map((e) => s(p(e).flatMap((e, t) => t ? [r.text(" "), ...u(e)] : u(e))))];
109
- }, h = (e) => p(e).flatMap((e) => e.isTextblock ? [s(l(e))] : e.type.spec.tableRole === "table" ? f(e) : e.type.name === "bulletList" || e.type.name === "orderedList" ? d(e) : e.isLeaf ? [] : h(e));
110
- return m(r, t.content.firstChild?.isInline ? [s(l(t.content))] : h(t.content));
128
+ return [...n ? [l([i.text(n)])] : [], ...g(t).map((e) => l(g(e).flatMap((e, t) => t ? [i.text(" "), ...h(e)] : h(e))))];
129
+ }, b = (e) => /^\s+$/.test(e.textContent) && g(e).every((e) => e.isText), S = (e) => !!t && !!e && e.type === c, C = (e) => g(e).flatMap((e, t, n) => e.isTextblock && !e.content.size && (S(n[t - 1]) || S(n[t + 1])) ? [] : e.isTextblock && b(e) ? [l([i.text(e.textContent)])] : e.isTextblock ? [l(p(e))] : e.type.spec.tableRole === "table" ? y(e) : e.type.name === "bulletList" || e.type.name === "orderedList" ? v(e) : S(e) ? m(e) : e.isLeaf ? [] : C(e));
130
+ return _(i, n.content.firstChild?.isInline ? [l(p(n.content))] : C(n.content));
111
131
  };
112
- function x(e, t) {
132
+ function w(e, t) {
113
133
  let { schema: n } = t, r = t.selection.$from.marks(), i = (e) => {
114
134
  let t = [];
115
135
  return e.descendants((e) => (e.isTextblock && t.length && t.push(n.text(" ", r)), e.isText && e.text && t.push(n.text(e.text, r)), e.type === n.nodes.math && t.push(e.mark([])), !e.isInline)), t;
@@ -122,9 +142,9 @@ function x(e, t) {
122
142
  }), a.push(t.flatMap((e, t) => t ? [n.text(" ", r), ...e] : e)), !1;
123
143
  }
124
144
  return e.isTextblock ? (a.push(i(e)), !1) : !0;
125
- }), m(n, a.map((e) => n.nodes.paragraph.create(null, e)));
145
+ }), _(n, a.map((e) => n.nodes.paragraph.create(null, e)));
126
146
  }
127
- function S(e, t, n) {
147
+ function T(e, t, n) {
128
148
  u = n;
129
149
  try {
130
150
  e.pasteHTML(t);
@@ -132,34 +152,43 @@ function S(e, t, n) {
132
152
  u = null;
133
153
  }
134
154
  }
135
- function C(t, { text: n, html: r }, i) {
136
- let { selection: a, schema: o } = t.state, c = new Set(i.plainText ? [] : i.formatting);
137
- if ("$anchorCell" in a && !(c.has("table") && s(r, "table"))) {
138
- let e = f(n).split("\n").map((e) => `<tr>${e.split(" ").map((e) => `<td>${d(e)}</td>`).join("")}</tr>`);
155
+ function E(t, { text: n, html: r }, i, a) {
156
+ let { selection: o, schema: c } = t.state, u = new Set(i.plainText ? [] : i.formatting);
157
+ if ("$anchorCell" in o && !(u.has("table") && s(r, "table"))) {
158
+ let e = h(n).split("\n").map((e) => `<tr>${e.split(" ").map((e) => `<td>${m(e)}</td>`).join("")}</tr>`);
139
159
  t.pasteHTML(`<table><tbody>${e.join("")}</tbody></table>`);
140
160
  return;
141
161
  }
142
162
  if (!i.plainText) {
143
- S(t, e(r), b(c));
163
+ let n = u.has("image") && c.nodes.imageUploadNode ? {
164
+ keys: [],
165
+ upload: !!a
166
+ } : null;
167
+ T(t, e(n ? p(r) : r), C(u, n));
168
+ let i = Promise.resolve();
169
+ for (let e of n?.keys ?? []) i = i.then(() => a?.(e));
144
170
  return;
145
171
  }
146
- if (o.nodes.math && l(r)) {
147
- S(t, r, x);
172
+ if (c.nodes.math && l(r)) {
173
+ T(t, r, w);
148
174
  return;
149
175
  }
150
- t.pasteText(a.$from.parent.type.spec.code ? f(n) : f(n).replace(/^$/gm, "\xA0"));
176
+ t.pasteText(o.$from.parent.type.spec.code ? h(n) : h(n).replace(/^$/gm, "\xA0"));
151
177
  }
152
- var w = t.create({
178
+ var D = t.create({
153
179
  name: "externalPaste",
154
180
  priority: 1e3,
155
181
  addOptions() {
156
- return { settings: () => ({
157
- plainText: !1,
158
- formatting: a
159
- }) };
182
+ return {
183
+ settings: () => ({
184
+ plainText: !1,
185
+ formatting: a
186
+ }),
187
+ uploadImage: void 0
188
+ };
160
189
  },
161
190
  addProseMirrorPlugins() {
162
- let { settings: e } = this.options;
191
+ let { settings: e, uploadImage: t } = this.options, { editor: a } = this, o = t ? (e) => t(a, e) : null;
163
192
  return [new n({
164
193
  key: new r("externalPaste"),
165
194
  props: {
@@ -167,19 +196,19 @@ var w = t.create({
167
196
  return u ? u(e, t.state) : e;
168
197
  },
169
198
  handlePaste(t, n) {
170
- let r = y(n.clipboardData);
171
- return r === null ? !1 : (C(t, r, e()), !0);
199
+ let r = S(n.clipboardData);
200
+ return r === null ? !1 : (E(t, r, e(), o), !0);
172
201
  },
173
202
  handleDrop(t, n) {
174
- let r = t.dragging ? null : y(n.dataTransfer), a = r === null ? null : t.posAtCoords({
203
+ let r = t.dragging ? null : S(n.dataTransfer), a = r === null ? null : t.posAtCoords({
175
204
  left: n.clientX,
176
205
  top: n.clientY
177
206
  });
178
- return r === null || !a ? !1 : (t.dispatch(t.state.tr.setSelection(i.near(t.state.doc.resolve(a.pos)))), C(t, r, e()), t.focus(), !0);
207
+ return r === null || !a ? !1 : (t.dispatch(t.state.tr.setSelection(i.near(t.state.doc.resolve(a.pos)))), E(t, r, e(), o), t.focus(), !0);
179
208
  }
180
209
  }
181
210
  })];
182
211
  }
183
212
  });
184
213
  //#endregion
185
- export { w as ExternalPaste, a as PASTED_FORMATTING };
214
+ export { D as ExternalPaste, a as PASTED_FORMATTING };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-lib/editable-html-tip-tap",
3
- "version": "3.0.3-next.20261007075859",
3
+ "version": "3.0.3-next.20261007181944",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,10 +13,10 @@
13
13
  "@dnd-kit/utilities": "3.2.2",
14
14
  "@mui/icons-material": "^7.3.4",
15
15
  "@mui/material": "^7.3.4",
16
- "@pie-lib/drag": "5.0.3-next.20261007075859",
17
- "@pie-lib/math-input": "9.0.3-next.20261007075859",
18
- "@pie-lib/math-toolbar": "4.0.3-next.20261007075859",
19
- "@pie-lib/render-ui": "8.0.3-next.20261007075859",
16
+ "@pie-lib/drag": "5.0.3-next.20261007175429",
17
+ "@pie-lib/math-input": "9.0.3-next.20261007175429",
18
+ "@pie-lib/math-toolbar": "4.0.3-next.20261007175429",
19
+ "@pie-lib/render-ui": "8.0.3-next.20261007175429",
20
20
  "@pie-lib/translator": "5.0.2-next.20261006160832",
21
21
  "@tiptap/core": "3.31.4",
22
22
  "@tiptap/extension-character-count": "3.31.4",
@@ -47,7 +47,7 @@
47
47
  "@pie-element/shared-lodash": "0.1.3-next.20261006160832",
48
48
  "clsx": "^2.1.1",
49
49
  "prosemirror-state": "^1.4.4",
50
- "@pie-element/shared-math-rendering-mathjax": "0.1.4-next.20261007064237",
50
+ "@pie-element/shared-math-rendering-mathjax": "0.1.4-next.20261007175429",
51
51
  "@tiptap/extensions": "3.31.4",
52
52
  "@tiptap/extension-list": "3.31.4",
53
53
  "@tiptap/extension-bubble-menu": "3.31.4",