@lobehub/editor 3.14.1 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { HistoryState } from '@lexical/history';
2
2
  import EventEmitter from 'eventemitter3';
3
3
  import { CommandListener, CommandListenerPriority, CommandPayloadType, LexicalCommand, LexicalEditor, LexicalNodeConfig } from 'lexical';
4
4
  import { HotkeyId } from "../types/hotkey";
5
- import { IDecorator, IEditor, IEditorKernel, IEditorPluginConstructor, IPlugin, IServiceID } from "../types/kernel";
5
+ import { IDecorator, IDocumentOptions, IEditor, IEditorKernel, IEditorPluginConstructor, IPlugin, ISelectionObject, IServiceID } from "../types/kernel";
6
6
  import { ILocaleKeys } from "../types/locale";
7
7
  import { HotkeyOptions, HotkeysEvent } from "../utils/hotkey/registerHotkey";
8
8
  import DataSource from './data-source';
@@ -22,6 +22,7 @@ export declare class Kernel extends EventEmitter implements IEditorKernel {
22
22
  private historyState;
23
23
  private editor?;
24
24
  constructor();
25
+ cloneNodeEditor(): IEditorKernel;
25
26
  getHistoryState(): HistoryState;
26
27
  isEditable(): boolean;
27
28
  private detectDevelopmentMode;
@@ -40,7 +41,12 @@ export declare class Kernel extends EventEmitter implements IEditorKernel {
40
41
  setRootElement(dom: HTMLElement, editable?: boolean): LexicalEditor;
41
42
  setEditable(editable: boolean): void;
42
43
  initNodeEditor(): LexicalEditor;
43
- setDocument(type: string, content: any, options?: Record<string, unknown>): void;
44
+ setDocument(type: string, content: any, options?: IDocumentOptions): void;
45
+ setSelection(selection: ISelectionObject, opt?: {
46
+ collapseToEnd?: boolean;
47
+ collapseToStart?: boolean;
48
+ }): Promise<boolean>;
49
+ getSelection(): ISelectionObject | null;
44
50
  focus(): void;
45
51
  blur(): void;
46
52
  getDocument(type: string): DataSource | undefined;
@@ -3,6 +3,10 @@ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread n
3
3
  function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
4
4
  function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
5
5
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
7
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
8
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
9
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
6
10
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
7
11
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
8
12
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
@@ -20,16 +24,17 @@ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key i
20
24
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
21
25
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
22
26
  import { createEmptyHistoryState } from '@lexical/history';
27
+ import { $createTableSelection, $isTableNode, $isTableSelection } from '@lexical/table';
23
28
  import { get, merge, template, templateSettings } from 'es-toolkit/compat';
24
29
  import EventEmitter from 'eventemitter3';
25
- import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_CRITICAL, KEY_DOWN_COMMAND, createEditor } from 'lexical';
30
+ import { $createNodeSelection, $createRangeSelection, $getNodeByKey, $getSelection, $isElementNode, $isNodeSelection, $isRangeSelection, $setSelection, COMMAND_PRIORITY_CRITICAL, KEY_DOWN_COMMAND, createEditor } from 'lexical';
26
31
  import defaultLocale from "../locale";
27
32
  import { $isRootTextContentEmpty } from "../plugins/common/utils";
28
33
  import { createDebugLogger } from "../utils/debug";
29
34
  import { getHotkeyById, registerHotkey as _registerHotkey } from "../utils/hotkey/registerHotkey";
30
35
  import { registerEvent } from "./event";
31
36
  import { KernelPlugin } from "./plugin";
32
- import { EDITOR_THEME_KEY, createEmptyEditorState, generateEditorId, noop, registerEditorKernel, unregisterEditorKernel } from "./utils";
37
+ import { $closest, EDITOR_THEME_KEY, createEmptyEditorState, generateEditorId, noop, registerEditorKernel, unregisterEditorKernel } from "./utils";
33
38
  templateSettings.interpolate = /{{([\S\s]+?)}}/g;
34
39
  export var Kernel = /*#__PURE__*/function (_EventEmitter) {
35
40
  _inherits(Kernel, _EventEmitter);
@@ -61,6 +66,17 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
61
66
  return _this;
62
67
  }
63
68
  _createClass(Kernel, [{
69
+ key: "cloneNodeEditor",
70
+ value: function cloneNodeEditor() {
71
+ var newKernel = new Kernel();
72
+ newKernel.registerPlugins(this.plugins);
73
+ newKernel.initNodeEditor();
74
+ newKernel.setDocument('json', this.getDocument('json') || '{}', {
75
+ keepId: true
76
+ });
77
+ return newKernel;
78
+ }
79
+ }, {
64
80
  key: "getHistoryState",
65
81
  value: function getHistoryState() {
66
82
  return this.historyState;
@@ -271,13 +287,141 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
271
287
  this.logger.error('❌ Editor not initialized');
272
288
  throw new Error("Editor is not initialized.");
273
289
  }
274
- this.historyState.redoStack = [];
275
- this.historyState.undoStack = [];
276
- this.historyState.current = null;
290
+ // Clear history if not keeping history
291
+ if (!(options !== null && options !== void 0 && options.keepHistory)) {
292
+ this.historyState.redoStack = [];
293
+ this.historyState.undoStack = [];
294
+ this.historyState.current = null;
295
+ }
277
296
  datasource.read(this.editor, content, options);
278
297
  this.emit('documentChange', type, content);
279
298
  this.logger.debug("\uD83D\uDCE5 Set ".concat(type, " document"));
280
299
  }
300
+ }, {
301
+ key: "setSelection",
302
+ value: function setSelection(selection, opt) {
303
+ var _this4 = this;
304
+ if (!this.editor) {
305
+ this.logger.error('❌ Editor not initialized');
306
+ throw new Error("Editor is not initialized.");
307
+ }
308
+ var editor = this.editor;
309
+ return new Promise(function (resolve) {
310
+ editor.update(function () {
311
+ try {
312
+ if (selection.type === 'range') {
313
+ var startNodeId = opt !== null && opt !== void 0 && opt.collapseToEnd ? selection.endNodeId : selection.startNodeId;
314
+ var endNodeId = opt !== null && opt !== void 0 && opt.collapseToStart ? selection.startNodeId : selection.endNodeId;
315
+ var startOffset = opt !== null && opt !== void 0 && opt.collapseToEnd ? selection.endOffset : selection.startOffset;
316
+ var endOffset = opt !== null && opt !== void 0 && opt.collapseToStart ? selection.startOffset : selection.endOffset;
317
+ var anchorNode = $getNodeByKey(startNodeId);
318
+ var focusNode = $getNodeByKey(endNodeId);
319
+ if (!anchorNode || !focusNode) {
320
+ _this4.logger.error('❌ Nodes for selection not found');
321
+ resolve(false);
322
+ return;
323
+ }
324
+ var rng = $createRangeSelection();
325
+ rng.anchor.set(anchorNode.getKey(), startOffset, $isElementNode(anchorNode) ? 'element' : 'text');
326
+ rng.focus.set(focusNode.getKey(), endOffset, $isElementNode(focusNode) ? 'element' : 'text');
327
+ $setSelection(rng);
328
+ resolve(true);
329
+ return;
330
+ }
331
+ if (selection.type === 'node') {
332
+ var node = $getNodeByKey(selection.startNodeId);
333
+ if (!node) {
334
+ _this4.logger.error('❌ Node for selection not found');
335
+ resolve(false);
336
+ return;
337
+ }
338
+ var nodeSel = $createNodeSelection();
339
+ nodeSel.add(selection.startNodeId);
340
+ $setSelection(nodeSel);
341
+ resolve(true);
342
+ return;
343
+ }
344
+ if (selection.type === 'table') {
345
+ var startNode = $getNodeByKey(selection.startNodeId);
346
+ if (!startNode) {
347
+ _this4.logger.error('❌ Node for selection not found');
348
+ resolve(false);
349
+ return;
350
+ }
351
+ var endNode = $getNodeByKey(selection.endNodeId);
352
+ if (!endNode) {
353
+ _this4.logger.error('❌ Node for selection not found');
354
+ resolve(false);
355
+ return;
356
+ }
357
+ var table1 = $closest(startNode, function (n) {
358
+ return $isTableNode(n);
359
+ });
360
+ var table2 = $closest(endNode, function (n) {
361
+ return $isTableNode(n);
362
+ });
363
+ if (!table1 || !table2 || table1 !== table2) {
364
+ _this4.logger.error('❌ Table nodes for selection not found or do not match');
365
+ resolve(false);
366
+ return;
367
+ }
368
+ var _rng = $createTableSelection();
369
+ _rng.set(table1.getKey(), selection.startNodeId, selection.endNodeId);
370
+ $setSelection(_rng);
371
+ resolve(true);
372
+ return;
373
+ }
374
+ resolve(false);
375
+ } catch (error) {
376
+ _this4.logger.error('❌ Error setting selection:', error);
377
+ resolve(false);
378
+ }
379
+ });
380
+ });
381
+ }
382
+ }, {
383
+ key: "getSelection",
384
+ value: function getSelection() {
385
+ if (!this.editor) {
386
+ this.logger.error('❌ Editor not initialized');
387
+ throw new Error("Editor is not initialized.");
388
+ }
389
+ return this.editor.read(function () {
390
+ var selection = $getSelection();
391
+ if ($isRangeSelection(selection)) {
392
+ return {
393
+ endNodeId: selection.focus.getNode().getKey(),
394
+ endOffset: selection.focus.offset,
395
+ startNodeId: selection.anchor.getNode().getKey(),
396
+ startOffset: selection.anchor.offset,
397
+ type: 'range'
398
+ };
399
+ }
400
+ if ($isNodeSelection(selection)) {
401
+ return {
402
+ endNodeId: selection.getNodes()[0].getKey(),
403
+ endOffset: 0,
404
+ startNodeId: selection.getNodes()[0].getKey(),
405
+ startOffset: 0,
406
+ type: 'node'
407
+ };
408
+ }
409
+ if ($isTableSelection(selection)) {
410
+ var _selection$getStartEn = selection.getStartEndPoints(),
411
+ _selection$getStartEn2 = _slicedToArray(_selection$getStartEn, 2),
412
+ start = _selection$getStartEn2[0],
413
+ end = _selection$getStartEn2[1];
414
+ return {
415
+ endNodeId: end.getNode().getKey(),
416
+ endOffset: end.offset,
417
+ startNodeId: start.getNode().getKey(),
418
+ startOffset: start.offset,
419
+ type: 'table'
420
+ };
421
+ }
422
+ return null;
423
+ });
424
+ }
281
425
  }, {
282
426
  key: "focus",
283
427
  value: function focus() {
@@ -608,12 +752,12 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
608
752
  }, {
609
753
  key: "isEmpty",
610
754
  get: function get() {
611
- var _this4 = this;
755
+ var _this5 = this;
612
756
  if (!this.editor) {
613
757
  return true;
614
758
  }
615
759
  return this.editor.getEditorState().read(function () {
616
- return $isRootTextContentEmpty(_this4.editor.isComposing(), true);
760
+ return $isRootTextContentEmpty(_this5.editor.isComposing(), true);
617
761
  });
618
762
  }
619
763
  }, {
@@ -656,7 +800,7 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
656
800
  }, {
657
801
  key: "registerHighCommand",
658
802
  value: function registerHighCommand(command, listener, priority) {
659
- var _this5 = this;
803
+ var _this6 = this;
660
804
  var lexicalEditor = this.editor;
661
805
  if (!lexicalEditor) {
662
806
  throw new Error('Editor is not initialized.');
@@ -667,7 +811,7 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
667
811
  commandsMap.set(command, [new Set(), new Set(), new Set(), new Set(), new Set()]);
668
812
  this._commandsClean.set(command, lexicalEditor.registerCommand(command, function (payload) {
669
813
  for (var i = 4; i >= 0; i--) {
670
- var listenerInPriorityOrder = _this5._commands.get(command);
814
+ var listenerInPriorityOrder = _this6._commands.get(command);
671
815
  if (listenerInPriorityOrder !== undefined) {
672
816
  var listenersSet = listenerInPriorityOrder[i];
673
817
  var _iterator5 = _createForOfIteratorHelper(listenersSet),
@@ -704,9 +848,9 @@ export var Kernel = /*#__PURE__*/function (_EventEmitter) {
704
848
  if (listenersInPriorityOrder.every(function (listenersSet) {
705
849
  return listenersSet.size === 0;
706
850
  })) {
707
- var _this5$_commandsClean;
851
+ var _this6$_commandsClean;
708
852
  commandsMap.delete(command);
709
- (_this5$_commandsClean = _this5._commandsClean.get(command)) === null || _this5$_commandsClean === void 0 || _this5$_commandsClean();
853
+ (_this6$_commandsClean = _this6._commandsClean.get(command)) === null || _this6$_commandsClean === void 0 || _this6$_commandsClean();
710
854
  }
711
855
  };
712
856
  }
package/es/index.d.ts CHANGED
@@ -27,6 +27,7 @@ export { getHotkeyById } from './utils/hotkey/registerHotkey';
27
27
  export { isPureUrl, isValidUrl } from './utils/url';
28
28
  export { browserDebug, createDebugLogger, debugLogger, debugLoggers, devConsole, prodSafeLogger, } from './utils/debug';
29
29
  export { Kernel } from './editor-kernel/kernel';
30
+ export { scrollIntoView } from './utils/scrollIntoView';
30
31
  /**
31
32
  * Enable hot reload mode globally for all editor instances
32
33
  * Call this in your app's entry point during development
package/es/index.js CHANGED
@@ -33,6 +33,7 @@ export { browserDebug, createDebugLogger, debugLogger, debugLoggers, devConsole,
33
33
 
34
34
  // Hot reload utilities
35
35
  export { Kernel } from "./editor-kernel/kernel";
36
+ export { scrollIntoView } from "./utils/scrollIntoView";
36
37
 
37
38
  /**
38
39
  * Enable hot reload mode globally for all editor instances
@@ -17,6 +17,7 @@ declare const _default: {
17
17
  };
18
18
  image: {
19
19
  broken: string;
20
+ replace: string;
20
21
  };
21
22
  link: {
22
23
  edit: string;
@@ -16,7 +16,8 @@ export default {
16
16
  uploading: 'Uploading file...'
17
17
  },
18
18
  image: {
19
- broken: 'Broken image'
19
+ broken: 'Broken image',
20
+ replace: 'Replace'
20
21
  },
21
22
  link: {
22
23
  edit: 'Edit Link',
@@ -42,6 +42,7 @@ var ReactImagePlugin = function ReactImagePlugin(_ref) {
42
42
  renderImage: function renderImage(node) {
43
43
  return /*#__PURE__*/_jsx(Image, {
44
44
  className: className,
45
+ handleUpload: handleUpload || defaultUpload,
45
46
  node: node
46
47
  });
47
48
  },
@@ -3,6 +3,9 @@ import { BlockImageNode } from '../../node/block-image-node';
3
3
  import { ImageNode } from '../../node/image-node';
4
4
  export interface ImageProps {
5
5
  className?: string;
6
+ handleUpload?: (file: File) => Promise<{
7
+ url: string;
8
+ }>;
6
9
  node: ImageNode | BlockImageNode;
7
10
  /** Whether to show scale info when resizing. Defaults to false */
8
11
  showScaleInfo?: boolean;
@@ -4,25 +4,28 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
4
4
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
+ import { Icon } from '@lobehub/ui';
7
8
  import { cx } from 'antd-style';
8
9
  import { COMMAND_PRIORITY_LOW, SELECTION_CHANGE_COMMAND } from 'lexical';
9
- import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
10
+ import { LoaderCircleIcon } from 'lucide-react';
11
+ import React, { Suspense, memo, useCallback, useMemo, useRef, useState } from 'react';
10
12
  import { useLexicalEditor } from "../../../../editor-kernel/react/useLexicalEditor";
11
13
  import { useLexicalNodeSelection } from "../../../../editor-kernel/react/useLexicalNodeSelection";
12
14
  import { $isBlockImageNode } from "../../node/block-image-node";
13
15
  import BrokenImage from "./BrokenImage";
16
+ import ImageEditPopover from "./ImageEditPopover";
14
17
  import LazyImage from "./LazyImage";
15
18
  import { ResizeHandle } from "./ResizeHandle";
16
19
  import { styles } from "./style";
17
20
  import { jsx as _jsx } from "react/jsx-runtime";
18
21
  import { jsxs as _jsxs } from "react/jsx-runtime";
19
22
  import { Fragment as _Fragment } from "react/jsx-runtime";
20
- // Keep memo: Complex resize logic, state management, and multiple event handlers
21
23
  var Image = /*#__PURE__*/memo(function (_ref) {
22
24
  var node = _ref.node,
23
25
  className = _ref.className,
24
26
  _ref$showScaleInfo = _ref.showScaleInfo,
25
- showScaleInfo = _ref$showScaleInfo === void 0 ? false : _ref$showScaleInfo;
27
+ showScaleInfo = _ref$showScaleInfo === void 0 ? false : _ref$showScaleInfo,
28
+ handleUpload = _ref.handleUpload;
26
29
  var _useLexicalNodeSelect = useLexicalNodeSelection(node.getKey()),
27
30
  _useLexicalNodeSelect2 = _slicedToArray(_useLexicalNodeSelect, 2),
28
31
  isSelected = _useLexicalNodeSelect2[0],
@@ -53,6 +56,7 @@ var Image = /*#__PURE__*/memo(function (_ref) {
53
56
  });
54
57
  var editorRef = useRef(null);
55
58
  var startWidthRef = useRef(0);
59
+ var lastLoadedSrcRef = useRef(null);
56
60
  var isBlock = useMemo(function () {
57
61
  return $isBlockImageNode(node);
58
62
  }, [node]);
@@ -149,15 +153,28 @@ var Image = /*#__PURE__*/memo(function (_ref) {
149
153
  case 'uploaded':
150
154
  case 'loading':
151
155
  {
152
- return /*#__PURE__*/_jsx(LazyImage, {
153
- className: className,
154
- newWidth: newWidth,
155
- node: node,
156
- onLoad: function onLoad(size) {
157
- originalSizeRef.current.width = size.width;
158
- originalSizeRef.current.height = size.height;
159
- setSize(size);
156
+ var fallback = lastLoadedSrcRef.current ? /*#__PURE__*/_jsx("img", {
157
+ alt: node.altText,
158
+ className: cx(styles.lazyImage, className || undefined),
159
+ draggable: "false",
160
+ src: lastLoadedSrcRef.current,
161
+ style: {
162
+ width: '100%'
160
163
  }
164
+ }) : null;
165
+ return /*#__PURE__*/_jsx(Suspense, {
166
+ fallback: fallback,
167
+ children: /*#__PURE__*/_jsx(LazyImage, {
168
+ className: className,
169
+ newWidth: newWidth,
170
+ node: node,
171
+ onLoad: function onLoad(loadedSize) {
172
+ lastLoadedSrcRef.current = node.src;
173
+ originalSizeRef.current.width = loadedSize.width;
174
+ originalSizeRef.current.height = loadedSize.height;
175
+ setSize(loadedSize);
176
+ }
177
+ })
161
178
  });
162
179
  }
163
180
  default:
@@ -195,39 +212,51 @@ var Image = /*#__PURE__*/memo(function (_ref) {
195
212
  }
196
213
  });
197
214
  }, [node]);
198
- return /*#__PURE__*/_jsxs("div", {
199
- className: cx(styles.imageContainer, {
200
- selected: isSelected
201
- }),
202
- draggable: false,
203
- onClick: handleClick,
204
- onDoubleClick: handleDoubleClick,
205
- onMouseEnter: handleMouseEnter,
206
- onMouseLeave: handleMouseLeave,
207
- ref: imageRef,
208
- style: {
209
- width: size.width || 'auto'
210
- },
211
- children: [children, showScaleInfo && isSelected && scale !== 1 && /*#__PURE__*/_jsxs("div", {
212
- className: styles.scaleInfo,
213
- children: [Math.round(scale * 100), "%"]
214
- }), isHovered && node.status === 'uploaded' && /*#__PURE__*/_jsxs(_Fragment, {
215
- children: [/*#__PURE__*/_jsx(ResizeHandle, {
216
- imageRef: imageRef,
217
- isBlock: isBlock,
218
- onResize: handleResize,
219
- onResizeEnd: handleResizeEnd,
220
- onResizeStart: handleResizeStart,
221
- position: "left"
222
- }), /*#__PURE__*/_jsx(ResizeHandle, {
223
- imageRef: imageRef,
224
- isBlock: isBlock,
225
- onResize: handleResize,
226
- onResizeEnd: handleResizeEnd,
227
- onResizeStart: handleResizeStart,
228
- position: "right"
215
+ return /*#__PURE__*/_jsx(ImageEditPopover, {
216
+ handleUpload: handleUpload,
217
+ node: node,
218
+ children: /*#__PURE__*/_jsxs("div", {
219
+ className: cx(styles.imageContainer, {
220
+ loading: node.status === 'loading',
221
+ selected: isSelected
222
+ }),
223
+ draggable: false,
224
+ onClick: handleClick,
225
+ onDoubleClick: handleDoubleClick,
226
+ onMouseEnter: handleMouseEnter,
227
+ onMouseLeave: handleMouseLeave,
228
+ ref: imageRef,
229
+ style: {
230
+ width: size.width || 'auto'
231
+ },
232
+ children: [children, node.status === 'loading' && /*#__PURE__*/_jsx("div", {
233
+ className: styles.loadingIcon,
234
+ children: /*#__PURE__*/_jsx(Icon, {
235
+ icon: LoaderCircleIcon,
236
+ size: 24,
237
+ spin: true
238
+ })
239
+ }), showScaleInfo && isSelected && scale !== 1 && /*#__PURE__*/_jsxs("div", {
240
+ className: styles.scaleInfo,
241
+ children: [Math.round(scale * 100), "%"]
242
+ }), isHovered && node.status === 'uploaded' && /*#__PURE__*/_jsxs(_Fragment, {
243
+ children: [/*#__PURE__*/_jsx(ResizeHandle, {
244
+ imageRef: imageRef,
245
+ isBlock: isBlock,
246
+ onResize: handleResize,
247
+ onResizeEnd: handleResizeEnd,
248
+ onResizeStart: handleResizeStart,
249
+ position: "left"
250
+ }), /*#__PURE__*/_jsx(ResizeHandle, {
251
+ imageRef: imageRef,
252
+ isBlock: isBlock,
253
+ onResize: handleResize,
254
+ onResizeEnd: handleResizeEnd,
255
+ onResizeStart: handleResizeStart,
256
+ position: "right"
257
+ })]
229
258
  })]
230
- })]
259
+ })
231
260
  });
232
261
  });
233
262
  Image.displayName = 'Image';
@@ -0,0 +1,12 @@
1
+ import { type FC, type ReactNode } from 'react';
2
+ import { BlockImageNode } from '../../node/block-image-node';
3
+ import { ImageNode } from '../../node/image-node';
4
+ interface ImageEditPopoverProps {
5
+ children: ReactNode;
6
+ handleUpload?: (file: File) => Promise<{
7
+ url: string;
8
+ }>;
9
+ node: ImageNode | BlockImageNode;
10
+ }
11
+ declare const ImageEditPopover: FC<ImageEditPopoverProps>;
12
+ export default ImageEditPopover;
@@ -0,0 +1,196 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
3
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
4
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
5
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
6
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
8
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
9
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
10
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
11
+ import { Button, Flexbox, Icon, Input, Popover } from '@lobehub/ui';
12
+ import { cssVar } from 'antd-style';
13
+ import { LinkIcon, UploadIcon } from 'lucide-react';
14
+ import { memo, useCallback, useRef, useState } from 'react';
15
+ import { useEditable } from "../../../../editor-kernel/react/useEditable";
16
+ import { useLexicalEditor } from "../../../../editor-kernel/react/useLexicalEditor";
17
+ import { useTranslation } from "../../../../editor-kernel/react/useTranslation";
18
+ import { jsx as _jsx } from "react/jsx-runtime";
19
+ import { Fragment as _Fragment } from "react/jsx-runtime";
20
+ import { jsxs as _jsxs } from "react/jsx-runtime";
21
+ var ImageEditPopover = /*#__PURE__*/memo(function (_ref) {
22
+ var children = _ref.children,
23
+ node = _ref.node,
24
+ handleUpload = _ref.handleUpload;
25
+ var _useState = useState(false),
26
+ _useState2 = _slicedToArray(_useState, 2),
27
+ open = _useState2[0],
28
+ setOpen = _useState2[1];
29
+ var _useState3 = useState(''),
30
+ _useState4 = _slicedToArray(_useState3, 2),
31
+ url = _useState4[0],
32
+ setUrl = _useState4[1];
33
+ var _useState5 = useState(false),
34
+ _useState6 = _slicedToArray(_useState5, 2),
35
+ uploading = _useState6[0],
36
+ setUploading = _useState6[1];
37
+ var fileInputRef = useRef(null);
38
+ var editorRef = useRef(null);
39
+ var _useEditable = useEditable(),
40
+ editable = _useEditable.editable;
41
+ var t = useTranslation();
42
+ useLexicalEditor(function (editor) {
43
+ editorRef.current = editor;
44
+ return function () {
45
+ editorRef.current = null;
46
+ };
47
+ }, []);
48
+ var handleOpenChange = useCallback(function (nextOpen) {
49
+ if (nextOpen && node.status !== 'uploaded') return;
50
+ if (nextOpen) setUrl(node.src);
51
+ setOpen(nextOpen);
52
+ }, [node]);
53
+ var handleClose = useCallback(function () {
54
+ setOpen(false);
55
+ }, []);
56
+ var handleUrlSubmit = useCallback(function () {
57
+ var editor = editorRef.current;
58
+ if (!editor || !url.trim()) return;
59
+ var trimmedUrl = url.trim();
60
+ handleClose();
61
+ editor.update(function () {
62
+ node.setUploaded(trimmedUrl);
63
+ });
64
+ }, [node, url, handleClose]);
65
+ var handleFileChange = useCallback( /*#__PURE__*/function () {
66
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(e) {
67
+ var _e$target$files;
68
+ var file, editor, result, _editor;
69
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
70
+ while (1) switch (_context.prev = _context.next) {
71
+ case 0:
72
+ file = (_e$target$files = e.target.files) === null || _e$target$files === void 0 ? void 0 : _e$target$files[0];
73
+ if (!(!file || !handleUpload)) {
74
+ _context.next = 3;
75
+ break;
76
+ }
77
+ return _context.abrupt("return");
78
+ case 3:
79
+ handleClose();
80
+ setUploading(true);
81
+ _context.prev = 5;
82
+ editor = editorRef.current;
83
+ if (editor) {
84
+ _context.next = 9;
85
+ break;
86
+ }
87
+ return _context.abrupt("return");
88
+ case 9:
89
+ editor.update(function () {
90
+ node.setStatus('loading');
91
+ });
92
+ _context.next = 12;
93
+ return handleUpload(file);
94
+ case 12:
95
+ result = _context.sent;
96
+ editor.update(function () {
97
+ node.setUploaded(result.url);
98
+ });
99
+ _context.next = 20;
100
+ break;
101
+ case 16:
102
+ _context.prev = 16;
103
+ _context.t0 = _context["catch"](5);
104
+ _editor = editorRef.current;
105
+ if (_editor) {
106
+ _editor.update(function () {
107
+ node.setError('Upload failed');
108
+ });
109
+ }
110
+ case 20:
111
+ _context.prev = 20;
112
+ setUploading(false);
113
+ if (fileInputRef.current) fileInputRef.current.value = '';
114
+ return _context.finish(20);
115
+ case 24:
116
+ case "end":
117
+ return _context.stop();
118
+ }
119
+ }, _callee, null, [[5, 16, 20, 24]]);
120
+ }));
121
+ return function (_x) {
122
+ return _ref2.apply(this, arguments);
123
+ };
124
+ }(), [node, handleUpload, handleClose]);
125
+ var content = /*#__PURE__*/_jsxs(Flexbox, {
126
+ gap: 8,
127
+ style: {
128
+ width: 320
129
+ },
130
+ children: [/*#__PURE__*/_jsx(Input, {
131
+ onChange: function onChange(e) {
132
+ return setUrl(e.target.value);
133
+ },
134
+ onKeyDown: function onKeyDown(e) {
135
+ if (e.key === 'Enter') handleUrlSubmit();
136
+ if (e.key === 'Escape') handleClose();
137
+ },
138
+ placeholder: "https://...",
139
+ prefix: /*#__PURE__*/_jsx(Icon, {
140
+ color: cssVar.colorTextDescription,
141
+ icon: LinkIcon
142
+ }),
143
+ value: url
144
+ }), /*#__PURE__*/_jsxs(Flexbox, {
145
+ gap: 8,
146
+ horizontal: true,
147
+ justify: "flex-end",
148
+ children: [handleUpload && /*#__PURE__*/_jsxs(_Fragment, {
149
+ children: [/*#__PURE__*/_jsx("input", {
150
+ accept: "image/*",
151
+ onChange: handleFileChange,
152
+ ref: fileInputRef,
153
+ style: {
154
+ display: 'none'
155
+ },
156
+ type: "file"
157
+ }), /*#__PURE__*/_jsx(Button, {
158
+ icon: /*#__PURE__*/_jsx(Icon, {
159
+ icon: UploadIcon
160
+ }),
161
+ loading: uploading,
162
+ onClick: function onClick() {
163
+ var _fileInputRef$current;
164
+ return (_fileInputRef$current = fileInputRef.current) === null || _fileInputRef$current === void 0 ? void 0 : _fileInputRef$current.click();
165
+ },
166
+ size: "small",
167
+ type: "text",
168
+ children: t('image.replace')
169
+ })]
170
+ }), /*#__PURE__*/_jsx(Button, {
171
+ onClick: handleUrlSubmit,
172
+ size: "small",
173
+ type: "text",
174
+ variant: "filled",
175
+ children: t('confirm')
176
+ })]
177
+ })]
178
+ });
179
+ return /*#__PURE__*/_jsx(Popover, {
180
+ arrow: false,
181
+ content: content,
182
+ disabled: !editable || node.status !== 'uploaded',
183
+ onOpenChange: handleOpenChange,
184
+ open: open,
185
+ placement: "bottom",
186
+ styles: {
187
+ content: {
188
+ padding: 12
189
+ }
190
+ },
191
+ trigger: "click",
192
+ children: children
193
+ });
194
+ });
195
+ ImageEditPopover.displayName = 'ImageEditPopover';
196
+ export default ImageEditPopover;
@@ -2,6 +2,7 @@ export declare const styles: {
2
2
  brokenImage: string;
3
3
  imageContainer: string;
4
4
  lazyImage: string;
5
+ loadingIcon: string;
5
6
  resizeHandle: string;
6
7
  resizeHandleLeft: string;
7
8
  resizeHandleRight: string;
@@ -1,4 +1,4 @@
1
- var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7;
1
+ var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8;
2
2
  function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
3
3
  import { createStaticStyles } from 'antd-style';
4
4
  export var styles = createStaticStyles(function (_ref) {
@@ -6,11 +6,12 @@ export var styles = createStaticStyles(function (_ref) {
6
6
  cssVar = _ref.cssVar;
7
7
  return {
8
8
  brokenImage: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n width: 200px;\n height: auto;\n "]))),
9
- imageContainer: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n cursor: default;\n user-select: none;\n\n position: relative;\n\n overflow: hidden;\n display: inline-block;\n\n width: auto;\n max-width: 100%;\n height: auto;\n border-radius: ", ";\n\n transition: border-color 0.2s ease;\n\n &.selected {\n cursor: pointer;\n outline: none;\n\n &::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n z-index: 10;\n inset: 0;\n\n background-color: color-mix(in srgb, ", " 10%, transparent);\n }\n }\n "])), cssVar.borderRadiusSM, cssVar.yellow),
9
+ imageContainer: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n cursor: default;\n user-select: none;\n\n position: relative;\n\n overflow: hidden;\n display: inline-block;\n\n width: auto;\n max-width: 100%;\n height: auto;\n border-radius: ", ";\n\n transition: border-color 0.2s ease;\n\n &.selected {\n cursor: pointer;\n outline: none;\n\n &::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n z-index: 10;\n inset: 0;\n\n background-color: color-mix(in srgb, ", " 10%, transparent);\n }\n }\n\n &.loading {\n &::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n z-index: 10;\n inset: 0;\n\n background-color: color-mix(in srgb, ", " 40%, transparent);\n\n animation: image-loading-pulse 1.5s ease-in-out infinite;\n }\n\n @keyframes image-loading-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n\n 50% {\n opacity: 0.4;\n }\n }\n }\n "])), cssVar.borderRadiusSM, cssVar.yellow, cssVar.colorBgContainer),
10
10
  lazyImage: css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n cursor: default;\n "]))),
11
- resizeHandle: css(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n pointer-events: auto;\n cursor: col-resize;\n\n position: absolute;\n z-index: 9999;\n inset-block-start: 0;\n\n width: 6px;\n height: 100%;\n\n &::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 0;\n transform: translateY(-50%);\n\n width: 6px;\n height: min(80px, 80%);\n border: 1px solid color-mix(in srgb, rgb(255, 255, 255) 75%, transparent);\n border-radius: 3px;\n\n background-color: color-mix(in srgb, rgb(0, 0, 0) 50%, transparent);\n }\n "]))),
12
- resizeHandleLeft: css(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["\n inset-inline-start: 8px;\n "]))),
13
- resizeHandleRight: css(_templateObject6 || (_templateObject6 = _taggedTemplateLiteral(["\n inset-inline-end: 8px;\n "]))),
14
- scaleInfo: css(_templateObject7 || (_templateObject7 = _taggedTemplateLiteral(["\n pointer-events: none;\n\n position: absolute;\n z-index: 11;\n inset-block-start: 2px;\n inset-inline-start: 2px;\n\n padding-block: 2px;\n padding-inline: 6px;\n border-radius: ", ";\n\n font-size: 12px;\n color: white;\n\n background-color: color-mix(in srgb, rgb(0, 0, 0) 50%, transparent);\n "])), cssVar.borderRadiusSM)
11
+ loadingIcon: css(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n pointer-events: none;\n\n position: absolute;\n z-index: 11;\n inset: 0;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n color: ", ";\n "])), cssVar.colorTextSecondary),
12
+ resizeHandle: css(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["\n pointer-events: auto;\n cursor: col-resize;\n\n position: absolute;\n z-index: 9999;\n inset-block-start: 0;\n\n width: 6px;\n height: 100%;\n\n &::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 0;\n transform: translateY(-50%);\n\n width: 6px;\n height: min(80px, 80%);\n border: 1px solid color-mix(in srgb, rgb(255, 255, 255) 75%, transparent);\n border-radius: 3px;\n\n background-color: color-mix(in srgb, rgb(0, 0, 0) 50%, transparent);\n }\n "]))),
13
+ resizeHandleLeft: css(_templateObject6 || (_templateObject6 = _taggedTemplateLiteral(["\n inset-inline-start: 8px;\n "]))),
14
+ resizeHandleRight: css(_templateObject7 || (_templateObject7 = _taggedTemplateLiteral(["\n inset-inline-end: 8px;\n "]))),
15
+ scaleInfo: css(_templateObject8 || (_templateObject8 = _taggedTemplateLiteral(["\n pointer-events: none;\n\n position: absolute;\n z-index: 11;\n inset-block-start: 2px;\n inset-inline-start: 2px;\n\n padding-block: 2px;\n padding-inline: 6px;\n border-radius: ", ";\n\n font-size: 12px;\n color: white;\n\n background-color: color-mix(in srgb, rgb(0, 0, 0) 50%, transparent);\n "])), cssVar.borderRadiusSM)
15
16
  };
16
17
  });
@@ -1,8 +1,12 @@
1
1
  import { HistoryState, HistoryStateEntry } from '@lexical/history';
2
2
  import { LexicalEditor } from 'lexical';
3
+ import { IEditorKernel } from "../../../types";
3
4
  import { MarkdownShortCutService } from '../service/shortcut';
4
5
  export declare const INSERT_MARKDOWN_COMMAND: import("lexical").LexicalCommand<{
5
6
  historyState: HistoryStateEntry | null;
6
7
  markdown: string;
7
8
  }>;
8
- export declare function registerMarkdownCommand(editor: LexicalEditor, service: MarkdownShortCutService, history: HistoryState): () => void;
9
+ export declare const GET_MARKDOWN_SELECTION_COMMAND: import("lexical").LexicalCommand<{
10
+ onResult: (startLine: number, endLine: number) => void;
11
+ }>;
12
+ export declare function registerMarkdownCommand(editor: LexicalEditor, kernel: IEditorKernel, service: MarkdownShortCutService, history: HistoryState): () => void;
@@ -1,9 +1,11 @@
1
- import { $getSelection, CAN_UNDO_COMMAND, COMMAND_PRIORITY_HIGH, HISTORIC_TAG, createCommand } from 'lexical';
1
+ import { mergeRegister } from '@lexical/utils';
2
+ import { $getSelection, $isRangeSelection, CAN_UNDO_COMMAND, COMMAND_PRIORITY_HIGH, HISTORIC_TAG, createCommand } from 'lexical';
2
3
  import { createDebugLogger } from "../../../utils/debug";
3
4
  import { parseMarkdownToLexical } from "../data-source/markdown/parse";
4
5
  import { $generateNodesFromSerializedNodes, $insertGeneratedNodes } from "../utils";
5
6
  var logger = createDebugLogger('plugin', 'markdown');
6
7
  export var INSERT_MARKDOWN_COMMAND = createCommand('INSERT_MARKDOWN_COMMAND');
8
+ export var GET_MARKDOWN_SELECTION_COMMAND = createCommand('GET_MARKDOWN_SELECTION_COMMAND');
7
9
  function undoToEntry(editor, historyState, entry) {
8
10
  var undoStack = historyState.undoStack;
9
11
  var current = historyState.current;
@@ -19,8 +21,12 @@ function undoToEntry(editor, historyState, entry) {
19
21
  }
20
22
  return editor.getEditorState();
21
23
  }
22
- export function registerMarkdownCommand(editor, service, history) {
23
- return editor.registerCommand(INSERT_MARKDOWN_COMMAND, function (payload) {
24
+ var SPICAL_TEXT = "\uFFF0";
25
+ var getLineNumber = function getLineNumber(content, charIndex) {
26
+ return content.slice(0, Math.max(0, charIndex)).split('\n').length;
27
+ };
28
+ export function registerMarkdownCommand(editor, kernel, service, history) {
29
+ return mergeRegister(editor.registerCommand(INSERT_MARKDOWN_COMMAND, function (payload) {
24
30
  var markdown = payload.markdown;
25
31
  logger.debug('INSERT_MARKDOWN_COMMAND payload:', payload);
26
32
  undoToEntry(editor, history, payload.historyState);
@@ -41,5 +47,48 @@ export function registerMarkdownCommand(editor, service, history) {
41
47
  });
42
48
  return false;
43
49
  }, COMMAND_PRIORITY_HIGH // Priority
44
- );
50
+ ), editor.registerCommand(GET_MARKDOWN_SELECTION_COMMAND, function (payload) {
51
+ var newEditor = kernel.cloneNodeEditor();
52
+ var s = kernel.getSelection();
53
+ if (s) {
54
+ var _newEditor$getLexical;
55
+ newEditor.setSelection(s);
56
+ (_newEditor$getLexical = newEditor.getLexicalEditor()) === null || _newEditor$getLexical === void 0 || _newEditor$getLexical.update(function () {
57
+ var sel = $getSelection();
58
+ if (!sel) {
59
+ return;
60
+ }
61
+ if ($isRangeSelection(sel)) {
62
+ var _sel$anchor = sel.anchor,
63
+ anchorKey = _sel$anchor.key,
64
+ anchorOffset = _sel$anchor.offset,
65
+ anchorType = _sel$anchor.type;
66
+ var _sel$focus = sel.focus,
67
+ focusKey = _sel$focus.key,
68
+ focusOffset = _sel$focus.offset,
69
+ focusType = _sel$focus.type;
70
+ var newRang = sel.clone();
71
+ newRang.anchor.set(anchorKey, anchorOffset, anchorType);
72
+ newRang.focus.set(anchorKey, anchorOffset, anchorType);
73
+ newRang.insertText(SPICAL_TEXT);
74
+ newRang.focus.set(focusKey, focusOffset, focusType);
75
+ newRang.anchor.set(focusKey, focusOffset, focusType);
76
+ newRang.insertText(SPICAL_TEXT);
77
+ }
78
+ }, {
79
+ onUpdate: function onUpdate() {
80
+ var markdownContent = newEditor.getDocument('markdown');
81
+ var startIndex = markdownContent.indexOf(SPICAL_TEXT);
82
+ var endIndex = markdownContent.lastIndexOf(SPICAL_TEXT);
83
+ var startLine = getLineNumber(markdownContent, startIndex);
84
+ var endLine = getLineNumber(markdownContent, endIndex);
85
+ payload.onResult(startLine, endLine);
86
+ logger.debug('GET_MARKDOWN_SELECTION_COMMAND markdownContent:', markdownContent);
87
+ logger.debug('GET_MARKDOWN_SELECTION_COMMAND startLine:', startLine, 'endLine:', endLine);
88
+ return markdownContent;
89
+ }
90
+ });
91
+ }
92
+ return false;
93
+ }, COMMAND_PRIORITY_HIGH));
45
94
  }
@@ -1,4 +1,4 @@
1
- export { INSERT_MARKDOWN_COMMAND } from './command';
1
+ export { GET_MARKDOWN_SELECTION_COMMAND, INSERT_MARKDOWN_COMMAND } from './command';
2
2
  export { MarkdownPlugin } from './plugin';
3
3
  export { default as ReactMarkdownPlugin } from './react';
4
4
  export type { MARKDOWN_READER_LEVEL } from './service/shortcut';
@@ -1,4 +1,4 @@
1
- export { INSERT_MARKDOWN_COMMAND } from "./command";
1
+ export { GET_MARKDOWN_SELECTION_COMMAND, INSERT_MARKDOWN_COMMAND } from "./command";
2
2
  export { MarkdownPlugin } from "./plugin";
3
3
  export { default as ReactMarkdownPlugin } from "./react";
4
4
  export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX } from "./service/shortcut";
@@ -159,7 +159,7 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
159
159
  }
160
160
  return false;
161
161
  }, COMMAND_PRIORITY_CRITICAL));
162
- this.register(registerMarkdownCommand(editor, this.service, this.kernel.getHistoryState()));
162
+ this.register(registerMarkdownCommand(editor, this.kernel, this.service, this.kernel.getHistoryState()));
163
163
  }
164
164
 
165
165
  /**
@@ -44,8 +44,8 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
44
44
  _createClass(SlashPlugin, [{
45
45
  key: "triggerClose",
46
46
  value: function triggerClose() {
47
- var _this$config;
48
- (_this$config = this.config) === null || _this$config === void 0 || _this$config.triggerClose();
47
+ var _this$config, _this$config$triggerC;
48
+ (_this$config = this.config) === null || _this$config === void 0 || (_this$config$triggerC = _this$config.triggerClose) === null || _this$config$triggerC === void 0 || _this$config$triggerC.call(_this$config);
49
49
  this.currentSlashTrigger = null;
50
50
  this.currentSlashTriggerIndex = -1;
51
51
  // After an explicit close, suppress reopening until next typing input
@@ -18,6 +18,19 @@ export type IServiceID<Service> = {
18
18
  readonly __serviceId: string;
19
19
  __serviceType?: Service;
20
20
  };
21
+ export interface ISelectionObject {
22
+ endNodeId: string;
23
+ endOffset: number;
24
+ startNodeId: string;
25
+ startOffset: number;
26
+ type: 'range' | 'node' | 'table';
27
+ }
28
+ export interface IDocumentOptions {
29
+ [key: string]: unknown;
30
+ keepHistory?: boolean;
31
+ /** only work for json datasource */
32
+ keepId?: boolean;
33
+ }
21
34
  export interface IKernelEventMap {
22
35
  /**
23
36
  * Document change event
@@ -88,6 +101,10 @@ export interface IEditor {
88
101
  * Get document editor root node
89
102
  */
90
103
  getRootElement(): HTMLElement | null;
104
+ /**
105
+ * Get editor selection information
106
+ */
107
+ getSelection(): ISelectionObject | null;
91
108
  /**
92
109
  * Get document editor selection content of specified type
93
110
  * @param type
@@ -186,7 +203,7 @@ export interface IEditor {
186
203
  * @param type
187
204
  * @param content
188
205
  */
189
- setDocument(type: string, content: any, options?: Record<string, unknown>): void;
206
+ setDocument(type: string, content: any, options?: IDocumentOptions): void;
190
207
  /**
191
208
  * Enable or disable editor editing capability
192
209
  * @param editable
@@ -197,6 +214,14 @@ export interface IEditor {
197
214
  * @param dom
198
215
  */
199
216
  setRootElement(dom: HTMLElement, editable?: boolean): LexicalEditor;
217
+ /**
218
+ * set editor selection
219
+ * @param selection
220
+ */
221
+ setSelection(selection: ISelectionObject, opt?: {
222
+ collapseToEnd?: boolean;
223
+ collapseToStart?: boolean;
224
+ }): Promise<boolean>;
200
225
  /**
201
226
  * Get translation text
202
227
  * @param key Translation key
@@ -214,6 +239,10 @@ export interface IEditor {
214
239
  * API provided to plugins
215
240
  */
216
241
  export interface IEditorKernel extends IEditor {
242
+ /**
243
+ * Clone the current editor kernel instance
244
+ */
245
+ cloneNodeEditor(): IEditorKernel;
217
246
  emit<T extends keyof IKernelEventMap>(event: T, params: Parameters<IKernelEventMap[T]>[0]): void;
218
247
  /**
219
248
  * Get editor Node decorator for specific Node rendering
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Scroll the current selection into view, centered vertically in the viewport
3
+ * @param offsetY Optional vertical offset from center (default: 0)
4
+ */
5
+ export declare function scrollIntoView(offsetY?: number): void;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Scroll the current selection into view, centered vertically in the viewport
3
+ * @param offsetY Optional vertical offset from center (default: 0)
4
+ */
5
+ export function scrollIntoView() {
6
+ var offsetY = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
7
+ // Skip on server side
8
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
9
+ return;
10
+ }
11
+ var selection = window.getSelection();
12
+ if (!selection || selection.rangeCount === 0) {
13
+ return;
14
+ }
15
+ var range = selection.getRangeAt(0);
16
+ var rect = range.getBoundingClientRect();
17
+
18
+ // If selection has no visible rect, try to get it from the focus node
19
+ if (rect.height === 0 && rect.width === 0) {
20
+ var focusNode = selection.focusNode;
21
+ if (focusNode) {
22
+ var element = focusNode.nodeType === Node.ELEMENT_NODE ? focusNode : focusNode.parentElement;
23
+ if (element) {
24
+ element.scrollIntoView({
25
+ behavior: 'smooth',
26
+ block: 'center'
27
+ });
28
+ }
29
+ }
30
+ return;
31
+ }
32
+
33
+ // Calculate the center position of the selection
34
+ var selectionCenter = rect.top + rect.height / 2;
35
+ var viewportCenter = window.innerHeight / 2;
36
+
37
+ // Calculate scroll amount needed to center the selection
38
+ var scrollAmount = selectionCenter - viewportCenter + offsetY;
39
+
40
+ // Perform smooth scroll
41
+ window.scrollBy({
42
+ behavior: 'smooth',
43
+ top: scrollAmount
44
+ });
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "3.14.1",
3
+ "version": "3.16.0",
4
4
  "description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
5
5
  "keywords": [
6
6
  "lobehub",