@seafile/sdoc-editor 1.0.217 → 1.0.218-test-0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -62,6 +62,61 @@
62
62
  caret-color: transparent;
63
63
  }
64
64
 
65
+ .sdoc-editor__article .sdoc-image-wrapper .sdoc-image-placeholder-wrapper {
66
+ width: 100%;
67
+ aspect-ratio: 1;
68
+ background-color: #f0f0f0;
69
+ border: 1px solid #ccc;
70
+ padding: 0 3%;
71
+ max-width: 100%;
72
+ user-select: all;
73
+ pointer-events: all;
74
+ }
75
+
76
+ .sdoc-editor__article .sdoc-image-wrapper .sdoc-image-tip-content,
77
+ .sdoc-editor__article .sdoc-image-wrapper .sdoc-image-title {
78
+ user-select: none;
79
+ pointer-events: none;
80
+ }
81
+
82
+ .sdoc-editor__article .sdoc-image-title .sdoc-exclamation-circle {
83
+ font-size: 18px;
84
+ align-items: center;
85
+ justify-content: center;
86
+ color: red;
87
+ display: flex;
88
+ }
89
+
90
+ .sdoc-editor__article .sdoc-image-title .sdoc-image-tip-title {
91
+ color: red;
92
+ font-size: 18px;
93
+ user-select: none;
94
+ pointer-events: none;
95
+ }
96
+
97
+ .sdoc-editor__article .sdoc-image-process-container {
98
+ width: 40px;
99
+ height: 40px;
100
+ display: flex;
101
+ align-items: center;
102
+ }
103
+
104
+ .sdoc-editor__article .sdoc-image-process-container .loading-spinner {
105
+ border: 5px solid #d8d8d8;
106
+ border-top: 5px solid #939393;
107
+ border-radius: 50%;
108
+ width: 30px;
109
+ height: 30px;
110
+ animation: spin 1s linear infinite;
111
+ user-select: none;
112
+ pointer-events: none;
113
+ }
114
+
115
+ @keyframes spin {
116
+ 0% { transform: rotate(0deg); }
117
+ 100% { transform: rotate(360deg); }
118
+ }
119
+
65
120
  .sdoc-editor__article .sdoc-image-inner {
66
121
  position: relative;
67
122
  display: inline-block;
@@ -104,9 +104,12 @@ const getCommonNode = (root, path, ancestor) => {
104
104
  exports.getCommonNode = getCommonNode;
105
105
  const getSelectedNodeByType = (editor, type) => {
106
106
  const match = n => getNodeType(n) === type;
107
+ if (!editor.selection) return;
108
+ const at = _slate.Range.isCollapsed(editor.selection) ? editor.selection : _slate.Editor.start(editor, editor.selection);
107
109
  const [nodeEntry] = _slate.Editor.nodes(editor, {
108
110
  match,
109
- universal: true
111
+ universal: true,
112
+ at
110
113
  });
111
114
  return nodeEntry ? nodeEntry[0] : null;
112
115
  };
@@ -72,16 +72,42 @@ const setCheckListItemType = (editor, newType, insertPosition) => {
72
72
  });
73
73
  _slate.Transforms.select(editor, [path[0] + 1]);
74
74
  }
75
- const path = _slate.Editor.path(editor, editor.selection);
76
- if (path) {
77
- const [targetNode, targetPath] = _slate.Editor.node(editor, [path[0]]);
78
- if (targetNode && [_constants.ORDERED_LIST, _constants.UNORDERED_LIST].includes(targetNode === null || targetNode === void 0 ? void 0 : targetNode.type)) {
79
- convertToCheck(editor, targetNode, targetPath);
80
- return;
75
+ if (!(0, _core.isRangeAcrossBlocks)(editor)) {
76
+ const path = _slate.Editor.path(editor, editor.selection);
77
+ if (path) {
78
+ const [targetNode, targetPath] = _slate.Editor.node(editor, [path[0]]);
79
+ if (targetNode && [_constants.ORDERED_LIST, _constants.UNORDERED_LIST].includes(targetNode === null || targetNode === void 0 ? void 0 : targetNode.type)) {
80
+ convertToCheck(editor, targetNode, targetPath);
81
+ return;
82
+ }
81
83
  }
84
+ _slate.Transforms.setNodes(editor, {
85
+ type: newType
86
+ });
87
+ } else {
88
+ const nodes = _slate.Editor.nodes(editor, {
89
+ match: n => {
90
+ const type = (0, _core.getNodeType)(n);
91
+ if (!type) return;
92
+ if (type === _constants.PARAGRAPH) return true;
93
+ if (type === _constants.CHECK_LIST_ITEM) return true;
94
+ if (type.startsWith(_constants.HEADER)) return true;
95
+ if (type === _constants.TITLE) return true;
96
+ if (type === _constants.SUBTITLE) return true;
97
+ return false;
98
+ },
99
+ universal: false,
100
+ mode: 'highest' // Match top level
101
+ });
102
+ const nodesEntry = Array.from(nodes);
103
+ if (nodesEntry.length === 0) return;
104
+ nodesEntry.forEach(nodeEntry => {
105
+ _slate.Transforms.setNodes(editor, {
106
+ type: newType
107
+ }, {
108
+ at: nodeEntry[1]
109
+ });
110
+ });
82
111
  }
83
- _slate.Transforms.setNodes(editor, {
84
- type: newType
85
- });
86
112
  };
87
113
  exports.setCheckListItemType = setCheckListItemType;
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.updateImage = exports.selectImageWhenSelectPartial = exports.resetCursor = exports.queryCopyMoveProgressView = exports.isInsertImageMenuDisabled = exports.insertImageFiles = exports.insertImage = exports.hasSdocImages = exports.handleBase64Image = exports.getSingleImageFromFragment = exports.getImageURL = exports.getImageData = exports.generateImageNode = void 0;
7
+ exports.updateImageNode = exports.updateImage = exports.selectImageWhenSelectPartial = exports.resetCursor = exports.queryCopyMoveProgressView = exports.isInsertImageMenuDisabled = exports.insertImageFiles = exports.insertImage = exports.hasSdocImages = exports.handleBase64Image = exports.getSingleImageFromFragment = exports.getImageURL = exports.getImageData = exports.generateImageNode = void 0;
8
8
  var _urlJoin = _interopRequireDefault(require("url-join"));
9
9
  var _slate = require("@seafile/slate");
10
10
  var _slateReact = require("@seafile/slate-react");
@@ -133,6 +133,24 @@ const updateImage = (editor, data) => {
133
133
  });
134
134
  };
135
135
  exports.updateImage = updateImage;
136
+ const updateImageNode = (editor, originalUrl, newUrl) => {
137
+ _slate.Editor.nodes(editor, {
138
+ match: n => n.type === _constants2.IMAGE && n.data.src === originalUrl,
139
+ at: []
140
+ }).forEach(_ref => {
141
+ let [node, path] = _ref;
142
+ const newData = {
143
+ ...node.data,
144
+ src: newUrl
145
+ };
146
+ _slate.Transforms.setNodes(editor, {
147
+ data: newData
148
+ }, {
149
+ at: path
150
+ });
151
+ });
152
+ };
153
+ exports.updateImageNode = updateImageNode;
136
154
  const getImageURL = (data, editor) => {
137
155
  const {
138
156
  src: url,
@@ -21,6 +21,8 @@ var _constants2 = require("./constants");
21
21
  var _constants3 = require("../../constants");
22
22
  var _constants4 = require("../../../../constants");
23
23
  var _imagePlaceholder = _interopRequireDefault(require("../../../assets/images/image-placeholder.png"));
24
+ var _context = _interopRequireDefault(require("../../../../context"));
25
+ var _copyImageErrorSvg = _interopRequireDefault(require("../../../../components/copy-image-error-svg"));
24
26
  const Image = _ref => {
25
27
  var _imageRef$current, _imageRef$current2;
26
28
  let {
@@ -50,6 +52,7 @@ const Image = _ref => {
50
52
  const imageRef = (0, _react.useRef)(null);
51
53
  const resizerRef = (0, _react.useRef)(null);
52
54
  const imageCaptionInputRef = (0, _react.useRef)(null);
55
+ const isLoadingRef = (0, _react.useRef)(false);
53
56
  const scrollRef = (0, _useScrollContext.useScrollContext)();
54
57
  const [movingWidth, setMovingWidth] = (0, _react.useState)(null);
55
58
  const [isResizing, setIsResizing] = (0, _react.useState)(false);
@@ -57,6 +60,9 @@ const Image = _ref => {
57
60
  const [isShowImageHoverMenu, setIsShowImageHoverMenu] = (0, _react.useState)(false);
58
61
  const [menuPosition, setMenuPosition] = (0, _react.useState)({});
59
62
  const [caption, setCaption] = (0, _react.useState)((data === null || data === void 0 ? void 0 : data.caption) || '');
63
+ const [isLoading, setIsLoading] = (0, _react.useState)(false);
64
+ const [isCopyError, setIsCopyError] = (0, _react.useState)(false);
65
+ console.log(data);
60
66
  const registerEvent = (0, _react.useCallback)(eventList => {
61
67
  eventList.forEach(element => {
62
68
  document.addEventListener(element.eventName, element.event);
@@ -198,10 +204,19 @@ const Image = _ref => {
198
204
  }
199
205
  }, [data, editor]);
200
206
  const onImageLoadError = (0, _react.useCallback)(() => {
207
+ console.log(isLoadingRef.current);
208
+ console.log(data);
201
209
  // Check is due to the image is pasted from the clipboard in base64
202
210
  if (data.src.startsWith('data:image/jpeg;base64')) {
203
211
  return (0, _helpers.handleBase64Image)(editor, path, data);
204
212
  }
213
+
214
+ // Check whether copying images is wrong
215
+ if (data.src.startsWith('attachment') || data.src.endsWith('_copy_error')) {
216
+ setIsCopyError(true);
217
+ return;
218
+ }
219
+ if (isLoadingRef.current) return;
205
220
  setIsShowImagePlaceholder(true);
206
221
  // External network images do not reload after failure to load
207
222
  if (!data.src.startsWith('http')) {
@@ -224,7 +239,57 @@ const Image = _ref => {
224
239
  });
225
240
  }
226
241
  }, [data, editor, element]);
227
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, isShowImagePlaceholder && /*#__PURE__*/_react.default.createElement("span", Object.assign({
242
+ (0, _react.useEffect)(() => {
243
+ isLoadingRef.current = isLoading;
244
+ }, [isLoading]);
245
+ (0, _react.useEffect)(() => {
246
+ const {
247
+ src: url
248
+ } = data;
249
+ console.log(0, data);
250
+ if (url && !url.startsWith('http')) return;
251
+ if (url && url.endsWith('_copy_error')) {
252
+ setIsCopyError(true);
253
+ return;
254
+ }
255
+ // Return if copying image from local server
256
+ const serviceUrl = _context.default.getSetting('serviceUrl');
257
+ if (url && url.startsWith(serviceUrl)) return;
258
+ if (url && url.startsWith('http')) {
259
+ setIsLoading(true);
260
+ const downloadAndUploadImages = async url => {
261
+ try {
262
+ const response = await fetch(url);
263
+ const blob = await response.blob();
264
+ const file = new File([blob], 'downloaded_image.png', {
265
+ type: blob.type
266
+ });
267
+ const imageUrl = await _context.default.uploadLocalImage([file]);
268
+ if (imageUrl && imageUrl[0]) {
269
+ (0, _helpers.updateImageNode)(editor, url, imageUrl[0]);
270
+ }
271
+ } catch (error) {
272
+ console.error(error);
273
+ const newUrl = url + '_copy_error';
274
+ (0, _helpers.updateImageNode)(editor, url, newUrl);
275
+ } finally {
276
+ setIsLoading(false);
277
+ }
278
+ };
279
+ downloadAndUploadImages(url);
280
+ }
281
+ // eslint-disable-next-line react-hooks/exhaustive-deps
282
+ }, []);
283
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, isLoading && /*#__PURE__*/_react.default.createElement("div", {
284
+ className: "sdoc-image-process-container",
285
+ style: {
286
+ display: 'inline-block'
287
+ }
288
+ }, /*#__PURE__*/_react.default.createElement("div", {
289
+ className: "loading-spinner"
290
+ }, /*#__PURE__*/_react.default.createElement("div", {
291
+ className: "spinner"
292
+ }))), isShowImagePlaceholder && /*#__PURE__*/_react.default.createElement("span", Object.assign({
228
293
  className: (0, _classnames.default)('sdoc-image-wrapper', className)
229
294
  }, attributes, {
230
295
  style: {
@@ -239,7 +304,20 @@ const Image = _ref => {
239
304
  style: getImageStyle(),
240
305
  draggable: false,
241
306
  alt: ""
242
- }), children), !isShowImagePlaceholder && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", Object.assign({
307
+ }), children), isCopyError && /*#__PURE__*/_react.default.createElement("span", Object.assign({
308
+ className: (0, _classnames.default)('sdoc-image-wrapper', className)
309
+ }, attributes, {
310
+ style: {
311
+ ...style
312
+ },
313
+ onMouseOver: e => (0, _helpers.selectImageWhenSelectPartial)(e, editor, element, isSelected),
314
+ contentEditable: "false",
315
+ suppressContentEditableWarning: true
316
+ }), /*#__PURE__*/_react.default.createElement(_copyImageErrorSvg.default, {
317
+ t: t,
318
+ isSelected: isSelected,
319
+ imageRef: imageRef
320
+ }), children), !isShowImagePlaceholder && !isCopyError && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", Object.assign({
243
321
  "data-id": element.id,
244
322
  className: (0, _classnames.default)('sdoc-image-wrapper', className)
245
323
  }, attributes, {
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.default = void 0;
8
+ var _react = _interopRequireDefault(require("react"));
9
+ var _classnames = _interopRequireDefault(require("classnames"));
10
+ const Svg = _ref => {
11
+ let {
12
+ t,
13
+ isSelected,
14
+ imageRef
15
+ } = _ref;
16
+ return /*#__PURE__*/_react.default.createElement("svg", {
17
+ xmlns: "http://www.w3.org/2000/svg",
18
+ viewBox: "0 0 360 360",
19
+ preserveAspectRatio: "xMinYMin meet",
20
+ className: (0, _classnames.default)('sdoc-image-placeholder-wrapper', {
21
+ 'image-selected': isSelected
22
+ }),
23
+ ref: imageRef,
24
+ draggable: false
25
+ }, /*#__PURE__*/_react.default.createElement("rect", {
26
+ width: "100%",
27
+ height: "100%",
28
+ fill: "#f0f0f0"
29
+ }), /*#__PURE__*/_react.default.createElement("g", {
30
+ className: "sdoc-image-content-wrapper",
31
+ transform: "translate(180, 180) scale(1)"
32
+ }, /*#__PURE__*/_react.default.createElement("g", {
33
+ className: "sdoc-image-title",
34
+ transform: "translate(0, 0)"
35
+ }, /*#__PURE__*/_react.default.createElement("foreignObject", {
36
+ x: "-85",
37
+ y: "-16",
38
+ width: "20",
39
+ height: "28"
40
+ }, /*#__PURE__*/_react.default.createElement("div", {
41
+ className: "sdocfont sdoc-exclamation-circle"
42
+ })), /*#__PURE__*/_react.default.createElement("text", {
43
+ className: "sdoc-image-tip-title",
44
+ transform: "translate(-55, 4)",
45
+ fontSize: "20",
46
+ fill: "red",
47
+ fontFamily: "Arial, sans-serif"
48
+ }, t('Image_copy_error'))), /*#__PURE__*/_react.default.createElement("text", {
49
+ className: "sdoc-image-tip-content",
50
+ transform: "translate(0, 35)",
51
+ fontSize: "12",
52
+ textAnchor: "middle",
53
+ fill: "black",
54
+ fontFamily: "Arial, sans-serif"
55
+ }, t('Image_cannot_be_copied_Please_download_the_source_image')), /*#__PURE__*/_react.default.createElement("text", {
56
+ className: "sdoc-image-tip-content",
57
+ transform: "translate(0, 55)",
58
+ fontSize: "12",
59
+ textAnchor: "middle",
60
+ fill: "black",
61
+ fontFamily: "Arial, sans-serif"
62
+ }, t('And_select_insert_-_image_to_upload'))));
63
+ };
64
+ var _default = exports.default = Svg;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seafile/sdoc-editor",
3
- "version": "1.0.217",
3
+ "version": "1.0.218-test-0.0.1",
4
4
  "private": false,
5
5
  "description": "This is a sdoc editor",
6
6
  "main": "dist/index.js",
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Add video link",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Videolink hinzufügen",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Add video link",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Add video link",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Add video link",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Add video link",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Add video link",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Add video link",
611
611
  "Link_Seafile_video_file": "Link Seafile video file",
612
612
  "Select_video_file": "Select video file",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Image cannot be copied. Please download the source image,",
615
+ "And_select_insert_-_image_to_upload": "and select 「insert」 - 「image」 to upload.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "Добавить ссылку на видео",
611
611
  "Link_Seafile_video_file": "Ссылка на видео файл Seafile",
612
612
  "Select_video_file": "Выбрать видео файл",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "Support Youtube, Tencent, Bilibili and more",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "Изображение не может быть скопировано. Скачайте исходное изображение,",
615
+ "And_select_insert_-_image_to_upload": "и выбрать 「вставить」 - 「изображение」 для загрузки.",
616
+ "Image_copy_error": "Image copy error"
614
617
  }
@@ -610,5 +610,8 @@
610
610
  "Add_video_link": "添加视频链接",
611
611
  "Link_Seafile_video_file": "链接Seafile视频文件",
612
612
  "Select_video_file": "选择视频文件",
613
- "Support_Youtube_Tencent_Bilibili_and_more": "支持Youtube,腾讯视频,B站及其他平台"
613
+ "Support_Youtube_Tencent_Bilibili_and_more": "支持Youtube,腾讯视频,B站及其他平台",
614
+ "Image_cannot_be_copied_Please_download_the_source_image": "此照片不支持复制,请下载原图",
615
+ "And_select_insert_-_image_to_upload": "后点击工具栏「插入」- 「照片」上传",
616
+ "Image_copy_error": "图片复制错误"
614
617
  }