@limetech/lime-elements 38.13.0 → 38.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/limel-markdown.cjs.entry.js +1 -1
  3. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +228 -71
  4. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js.map +1 -1
  5. package/dist/cjs/{markdown-parser-564adb69.js → markdown-parser-563c73db.js} +144 -386
  6. package/dist/cjs/{markdown-parser-564adb69.js.map → markdown-parser-563c73db.js.map} +1 -1
  7. package/dist/collection/components/markdown/link-markdown-plugin.js +30 -0
  8. package/dist/collection/components/markdown/link-markdown-plugin.js.map +1 -0
  9. package/dist/collection/components/markdown/markdown-parser.js +4 -4
  10. package/dist/collection/components/markdown/markdown-parser.js.map +1 -1
  11. package/dist/collection/components/text-editor/prosemirror-adapter/menu/menu-commands.js +3 -42
  12. package/dist/collection/components/text-editor/prosemirror-adapter/menu/menu-commands.js.map +1 -1
  13. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js +59 -9
  14. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/image/inserter.js.map +1 -1
  15. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/link-mark-spec.js +33 -0
  16. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/link-mark-spec.js.map +1 -0
  17. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/{link-plugin.js → link/link-plugin.js} +135 -22
  18. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/link-plugin.js.map +1 -0
  19. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/utils.js +39 -0
  20. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/utils.js.map +1 -0
  21. package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js +3 -1
  22. package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js.map +1 -1
  23. package/dist/esm/limel-markdown.entry.js +1 -1
  24. package/dist/esm/limel-prosemirror-adapter.entry.js +228 -71
  25. package/dist/esm/limel-prosemirror-adapter.entry.js.map +1 -1
  26. package/dist/esm/{markdown-parser-1c1fdedc.js → markdown-parser-41d15994.js} +144 -387
  27. package/dist/esm/markdown-parser-41d15994.js.map +1 -0
  28. package/dist/lime-elements/lime-elements.esm.js +1 -1
  29. package/dist/lime-elements/p-6b5d588e.entry.js +2 -0
  30. package/dist/lime-elements/p-6b5d588e.entry.js.map +1 -0
  31. package/dist/lime-elements/{p-ce152b39.entry.js → p-895ae176.entry.js} +2 -2
  32. package/dist/lime-elements/p-95a71e89.js +8 -0
  33. package/dist/lime-elements/p-95a71e89.js.map +1 -0
  34. package/dist/types/components/markdown/link-markdown-plugin.d.ts +9 -0
  35. package/dist/types/components/text-editor/prosemirror-adapter/menu/menu-commands.d.ts +0 -2
  36. package/dist/types/components/text-editor/prosemirror-adapter/plugins/link/link-mark-spec.d.ts +10 -0
  37. package/dist/types/components/text-editor/prosemirror-adapter/plugins/link/utils.d.ts +3 -0
  38. package/package.json +1 -2
  39. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link-plugin.js.map +0 -1
  40. package/dist/esm/markdown-parser-1c1fdedc.js.map +0 -1
  41. package/dist/lime-elements/p-587f6b6d.entry.js +0 -2
  42. package/dist/lime-elements/p-587f6b6d.entry.js.map +0 -1
  43. package/dist/lime-elements/p-cf87519f.js +0 -8
  44. package/dist/lime-elements/p-cf87519f.js.map +0 -1
  45. /package/dist/lime-elements/{p-ce152b39.entry.js.map → p-895ae176.entry.js.map} +0 -0
  46. /package/dist/types/components/text-editor/prosemirror-adapter/plugins/{link-plugin.d.ts → link/link-plugin.d.ts} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { r as registerInstance, c as createEvent, h, H as Host, g as getElement } from './index-2714248e.js';
2
+ import { g as getLinkAttributes, d as decodeHTML, m as markdownToHTML, s as sanitizeHTML } from './markdown-parser-41d15994.js';
2
3
  import { k as keys$1, g as getSymbols, U as Uint8Array, a as Stack, i as getAllKeys } from './_baseIsEqual-7961b1ee.js';
3
4
  import { a as assignValue } from './_assignValue-1f62c5ff.js';
4
5
  import { b as baseAssignValue } from './_baseAssignValue-ba9887b3.js';
@@ -8,19 +9,18 @@ import { d as isPrototype, g as getTag, e as baseUnary, n as nodeUtil, a as isBu
8
9
  import { g as getPrototype } from './_getPrototype-8096728a.js';
9
10
  import { i as isArray } from './isArray-80298bc7.js';
10
11
  import { i as isObjectLike } from './isObjectLike-38996507.js';
11
- import { d as decodeHTML, m as markdownToHTML, s as sanitizeHTML } from './markdown-parser-1c1fdedc.js';
12
12
  import { t as translate$1 } from './translations-489f20b3.js';
13
13
  import { c as createRandomString } from './random-string-e74dc48d.js';
14
14
  import { i as isItem } from './isItem-b0459122.js';
15
15
  import { c as createFileInfo } from './files-2be62a61.js';
16
16
  import { i as isEqual } from './isEqual-2c139334.js';
17
17
  import { d as debounce } from './debounce-9a05c91c.js';
18
+ import './_commonjsHelpers-9b95d21f.js';
18
19
  import './eq-c1c7f528.js';
19
20
  import './_getNative-93d6bfe9.js';
20
21
  import './_isIndex-6de44c7b.js';
21
22
  import './isArrayLike-6597c815.js';
22
23
  import './_defineProperty-2105cb48.js';
23
- import './_commonjsHelpers-9b95d21f.js';
24
24
  import './file-metadata-01403ecd.js';
25
25
  import './get-icon-props-5a77e17e.js';
26
26
  import './toNumber-a6ed64f0.js';
@@ -16898,24 +16898,15 @@ const setActiveMethodForWrap = (command, nodeType) => {
16898
16898
  const createInsertLinkCommand = (schema, _, link) => {
16899
16899
  const command = (state, dispatch) => {
16900
16900
  const { from, to } = state.selection;
16901
+ const linkMark = schema.marks.link.create(getLinkAttributes(link.href, link.href));
16901
16902
  if (from === to) {
16902
16903
  // If no text is selected, insert new text with link
16903
- const linkMark = schema.marks.link.create({
16904
- href: link.href,
16905
- title: link.href,
16906
- target: isExternalLink(link.href) ? '_blank' : null,
16907
- });
16908
16904
  const linkText = link.text || link.href;
16909
16905
  const newLink = schema.text(linkText, [linkMark]);
16910
16906
  dispatch(state.tr.insert(from, newLink));
16911
16907
  }
16912
16908
  else {
16913
16909
  // If text is selected, replace selected text with link text
16914
- const linkMark = schema.marks.link.create({
16915
- href: link.href,
16916
- title: link.href,
16917
- target: isExternalLink(link.href) ? '_blank' : null,
16918
- });
16919
16910
  const selectedText = state.doc.textBetween(from, to, ' ');
16920
16911
  const newLink = schema.text(link.text || selectedText, [linkMark]);
16921
16912
  dispatch(state.tr.replaceWith(from, to, newLink));
@@ -16989,15 +16980,6 @@ const toggleNodeType = (schema, type, attrs = {}, shouldWrap = false) => {
16989
16980
  return false;
16990
16981
  };
16991
16982
  };
16992
- const isValidUrl = (text) => {
16993
- try {
16994
- new URL(text);
16995
- }
16996
- catch (_a) {
16997
- return false;
16998
- }
16999
- return true;
17000
- };
17001
16983
  const createSetNodeTypeCommand = (schema, nodeType, level) => {
17002
16984
  const type = schema.nodes[nodeType];
17003
16985
  if (!type) {
@@ -17070,27 +17052,6 @@ const createListCommand = (schema, listType) => {
17070
17052
  setActiveMethodForWrap(command, type);
17071
17053
  return command;
17072
17054
  };
17073
- const copyPasteLinkCommand = (state, dispatch) => {
17074
- const { from, to } = state.selection;
17075
- if (from === to) {
17076
- return false;
17077
- }
17078
- const clipboardData = window.clipboardData;
17079
- if (!clipboardData) {
17080
- return false;
17081
- }
17082
- const copyPastedText = clipboardData.getData('text');
17083
- if (!isValidUrl(copyPastedText)) {
17084
- return false;
17085
- }
17086
- const linkMark = state.schema.marks.link.create({
17087
- href: copyPastedText,
17088
- target: isExternalLink(copyPastedText) ? '_blank' : null,
17089
- });
17090
- const selectedText = state.doc.textBetween(from, to, ' ');
17091
- const newLink = state.schema.text(selectedText, [linkMark]);
17092
- dispatch(state.tr.replaceWith(from, to, newLink));
17093
- };
17094
17055
  const commandMapping = {
17095
17056
  strong: createToggleMarkCommand,
17096
17057
  em: createToggleMarkCommand,
@@ -17129,7 +17090,6 @@ class MenuCommandFactory {
17129
17090
  'Mod-Shift-X': this.getCommand(EditorMenuTypes.Strikethrough),
17130
17091
  'Mod-`': this.getCommand(EditorMenuTypes.Code),
17131
17092
  'Mod-Shift-C': this.getCommand(EditorMenuTypes.CodeBlock),
17132
- 'Mod-v': copyPasteLinkCommand,
17133
17093
  };
17134
17094
  }
17135
17095
  }
@@ -26256,32 +26216,145 @@ const processClickEvent = (view, event) => {
26256
26216
  }, DOUBLE_CLICK_DELAY);
26257
26217
  return true;
26258
26218
  };
26259
- const processPasteEvent$1 = (view, event) => {
26260
- const clipboardData = event.clipboardData;
26261
- if (!clipboardData) {
26219
+ /**
26220
+ * Regular expression for matching URLs, mailto links, and phone links
26221
+ */
26222
+ const URL_REGEX = /(https?:\/\/[^\s<>"']+|mailto:[^\s<>"']+|tel:[^\s<>"']+)/g;
26223
+ /**
26224
+ * Checks if the text contains any URLs, mailto links, or phone links
26225
+ */
26226
+ const hasUrls = (text) => {
26227
+ // Reset regex before use
26228
+ URL_REGEX.lastIndex = 0;
26229
+ return URL_REGEX.test(text);
26230
+ };
26231
+ /**
26232
+ * Creates a text node with the provided content
26233
+ */
26234
+ const createTextNode = (schema, content) => {
26235
+ return schema.text(content);
26236
+ };
26237
+ /**
26238
+ * Creates a link node with the provided URL
26239
+ */
26240
+ const createLinkNode = (schema, url) => {
26241
+ const linkMark = schema.marks.link.create(getLinkAttributes(url, url));
26242
+ return schema.text(url, [linkMark]);
26243
+ };
26244
+ /**
26245
+ * Finds all link matches in the provided text
26246
+ */
26247
+ const findLinkMatches = (text) => {
26248
+ const matches = [];
26249
+ let match;
26250
+ // Reset regex before use
26251
+ URL_REGEX.lastIndex = 0;
26252
+ while ((match = URL_REGEX.exec(text)) !== null) {
26253
+ matches.push({
26254
+ url: match[0],
26255
+ start: match.index,
26256
+ end: match.index + match[0].length,
26257
+ });
26258
+ }
26259
+ return matches;
26260
+ };
26261
+ /**
26262
+ * Creates text nodes with links for any URLs, mailto links, or phone links found in the text
26263
+ */
26264
+ const createNodesWithLinks = (text, schema) => {
26265
+ const nodes = [];
26266
+ const matches = findLinkMatches(text);
26267
+ if (matches.length === 0) {
26268
+ // No links found, just return the text as a single node
26269
+ return [createTextNode(schema, text)];
26270
+ }
26271
+ let lastIndex = 0;
26272
+ // Process each match
26273
+ for (const match of matches) {
26274
+ // Add text before the current link if any
26275
+ if (match.start > lastIndex) {
26276
+ nodes.push(createTextNode(schema, text.slice(lastIndex, match.start)));
26277
+ }
26278
+ // Add the link node
26279
+ nodes.push(createLinkNode(schema, match.url));
26280
+ lastIndex = match.end;
26281
+ }
26282
+ // Add any remaining text after the last link
26283
+ if (lastIndex < text.length) {
26284
+ nodes.push(createTextNode(schema, text.slice(lastIndex)));
26285
+ }
26286
+ return nodes;
26287
+ };
26288
+ /**
26289
+ * Pastes nodes at the current selection
26290
+ * @param view - The editor view
26291
+ * @param nodes - Array of nodes to paste
26292
+ */
26293
+ const pasteAsLink = (view, nodes) => {
26294
+ if (nodes.length === 0) {
26295
+ return;
26296
+ }
26297
+ if (isSingleLinkNode(nodes)) {
26298
+ insertSingleLink(view, nodes[0]);
26299
+ }
26300
+ else {
26301
+ insertNodeFragment(view, nodes);
26302
+ }
26303
+ };
26304
+ /**
26305
+ * Checks if the nodes array contains just a single link node
26306
+ */
26307
+ const isSingleLinkNode = (nodes) => {
26308
+ if (nodes.length !== 1) {
26262
26309
  return false;
26263
26310
  }
26264
- const text = clipboardData.getData('text/plain');
26265
- // Process as a link if the text is a valid URL
26266
- if (isValidUrl(text)) {
26267
- pasteAsLink(view, text);
26268
- return true;
26311
+ const node = nodes[0];
26312
+ // Must be text with non-empty content
26313
+ if (!node.isText || !node.text || node.text.trim() === '') {
26314
+ return false;
26269
26315
  }
26270
- return false;
26316
+ // Must have a link mark (even if there are other marks, we just care about link presence)
26317
+ return !!node.marks.find((mark) => mark.type.name === 'link');
26271
26318
  };
26272
- const pasteAsLink = (view, href) => {
26319
+ /**
26320
+ * Inserts a single link node, applying it to selected text if present
26321
+ */
26322
+ const insertSingleLink = (view, linkNode) => {
26273
26323
  const { state, dispatch } = view;
26274
26324
  const { from, to } = state.selection;
26275
- const linkMark = schema$1.marks.link.create({
26276
- href: href,
26277
- title: href,
26278
- target: isExternalLink(href) ? '_blank' : null,
26279
- });
26280
- const selectedText = state.doc.textBetween(from, to, ' ') || href;
26281
- const transaction = state.tr
26325
+ const linkMark = linkNode.marks.find((mark) => mark.type.name === 'link');
26326
+ // Use selected text if there's a selection, otherwise use the URL
26327
+ const selectedText = state.doc.textBetween(from, to, ' ') || linkMark.attrs.href;
26328
+ // Insert the text and add the link mark
26329
+ dispatch(state.tr
26282
26330
  .insertText(selectedText, from, to)
26283
- .addMark(from, from + selectedText.length, linkMark);
26284
- dispatch(transaction);
26331
+ .addMark(from, from + selectedText.length, linkMark));
26332
+ };
26333
+ /**
26334
+ * Inserts multiple nodes as a fragment at the current selection
26335
+ * @param view - The editor view
26336
+ * @param nodes - Array of nodes to insert
26337
+ */
26338
+ const insertNodeFragment = (view, nodes) => {
26339
+ const { state, dispatch } = view;
26340
+ const { from, to } = state.selection;
26341
+ // Create a fragment from the array of nodes
26342
+ const fragment = Fragment.fromArray(nodes);
26343
+ // Replace the current selection with the fragment
26344
+ dispatch(state.tr.replaceWith(from, to, fragment));
26345
+ };
26346
+ /**
26347
+ * Handles pasted content, converting URLs to links
26348
+ */
26349
+ const processPasteEvent$1 = (view, event) => {
26350
+ var _a;
26351
+ const text = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text/plain');
26352
+ if (!text || !hasUrls(text)) {
26353
+ return false;
26354
+ }
26355
+ const nodes = createNodesWithLinks(text, view.state.schema);
26356
+ pasteAsLink(view, nodes);
26357
+ return true;
26285
26358
  };
26286
26359
  const createLinkPlugin = (updateLinkCallback) => {
26287
26360
  return new Plugin({
@@ -26323,6 +26396,39 @@ const createLinkPlugin = (updateLinkCallback) => {
26323
26396
  });
26324
26397
  };
26325
26398
 
26399
+ const linkMarkSpec = {
26400
+ attrs: {
26401
+ href: { default: '' },
26402
+ title: { default: null },
26403
+ target: { default: null },
26404
+ rel: { default: null },
26405
+ referrerpolicy: { default: null },
26406
+ },
26407
+ inclusive: false,
26408
+ parseDOM: [
26409
+ {
26410
+ tag: 'a[href]',
26411
+ getAttrs: (dom) => {
26412
+ return {
26413
+ href: dom.getAttribute('href') || '',
26414
+ title: dom.getAttribute('title'),
26415
+ target: dom.getAttribute('target'),
26416
+ rel: dom.getAttribute('rel'),
26417
+ referrerpolicy: dom.getAttribute('referrerpolicy'),
26418
+ };
26419
+ },
26420
+ },
26421
+ ],
26422
+ toDOM: (mark) => {
26423
+ const target = mark.attrs.target || null;
26424
+ const securityAttrs = {
26425
+ rel: target === '_blank' ? 'noopener noreferrer' : null,
26426
+ referrerpolicy: target === '_blank' ? 'noreferrer' : null,
26427
+ };
26428
+ return ['a', Object.assign(Object.assign({}, mark.attrs), securityAttrs), 0];
26429
+ },
26430
+ };
26431
+
26326
26432
  const pluginKey = new PluginKey('imageInserterPlugin');
26327
26433
  const createImageInserterPlugin = (imagePastedCallback) => {
26328
26434
  return new Plugin({
@@ -26454,9 +26560,29 @@ const processPasteEvent = (view, event, slice) => {
26454
26560
  if (!clipboardData) {
26455
26561
  return false;
26456
26562
  }
26563
+ const isImageFilePasted = handlePastedImages(view, clipboardData);
26564
+ const filteredSlice = new Slice(filterImageNodes(slice.content), slice.openStart, slice.openEnd);
26565
+ if (filteredSlice.content.childCount < slice.content.childCount) {
26566
+ const { state, dispatch } = view;
26567
+ const tr = state.tr.replaceSelection(filteredSlice);
26568
+ dispatch(tr);
26569
+ return true;
26570
+ }
26571
+ return isImageFilePasted;
26572
+ };
26573
+ /**
26574
+ * Processes any image files found in the clipboard data and dispatches an imagePasted event.
26575
+ *
26576
+ * @param view - The ProseMirror editor view
26577
+ * @param clipboardData - The clipboard data transfer object containing potential image files
26578
+ * @returns True if at least one valid image file was found and processed, false otherwise
26579
+ */
26580
+ function handlePastedImages(view, clipboardData) {
26581
+ let isImageFilePasted = false;
26457
26582
  const files = Array.from(clipboardData.files || []);
26458
26583
  for (const file of files) {
26459
- if (file.type.startsWith('image/')) {
26584
+ if (isImageFile(file, clipboardData)) {
26585
+ isImageFilePasted = true;
26460
26586
  const reader = new FileReader();
26461
26587
  reader.onloadend = () => {
26462
26588
  view.dom.dispatchEvent(new CustomEvent('imagePasted', {
@@ -26466,15 +26592,45 @@ const processPasteEvent = (view, event, slice) => {
26466
26592
  reader.readAsDataURL(file);
26467
26593
  }
26468
26594
  }
26469
- const filteredSlice = new Slice(filterImageNodes(slice.content), slice.openStart, slice.openEnd);
26470
- if (filteredSlice.content.childCount < slice.content.childCount) {
26471
- const { state, dispatch } = view;
26472
- const tr = state.tr.replaceSelection(filteredSlice);
26473
- dispatch(tr);
26474
- return true;
26595
+ return isImageFilePasted;
26596
+ }
26597
+ /**
26598
+ * Determines if a file is an image that should be processed by the image handler.
26599
+ *
26600
+ * This function checks both the file's MIME type and the clipboard HTML content.
26601
+ * It filters out HTML content from Excel and HTML tables, as they are not relevant for image processing.
26602
+ *
26603
+ * @param file - The file object to check
26604
+ * @param clipboardData - The full clipboard data transfer object to examine for context
26605
+ * @returns True if the file is an image that should be processed, false otherwise
26606
+ */
26607
+ function isImageFile(file, clipboardData) {
26608
+ var _a, _b;
26609
+ if (!isContentTypeImage(file)) {
26610
+ return false;
26475
26611
  }
26476
- return files.length > 0;
26477
- };
26612
+ const html = (_b = (_a = clipboardData === null || clipboardData === void 0 ? void 0 : clipboardData.getData('text/html')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '';
26613
+ return !isHtmlFromExcel(html) && !isHtmlTable(html);
26614
+ }
26615
+ function isContentTypeImage(file) {
26616
+ if (!(file === null || file === void 0 ? void 0 : file.type)) {
26617
+ return false;
26618
+ }
26619
+ return file.type.startsWith('image/');
26620
+ }
26621
+ function isHtmlFromExcel(html) {
26622
+ if (!html) {
26623
+ return false;
26624
+ }
26625
+ return (html.includes('name=generator content="microsoft excel"') ||
26626
+ html.includes('xmlns:x="urn:schemas-microsoft-com:office:excel"'));
26627
+ }
26628
+ function isHtmlTable(html) {
26629
+ if (!html) {
26630
+ return false;
26631
+ }
26632
+ return html.includes('<table');
26633
+ }
26478
26634
 
26479
26635
  const MIN_WIDTH = 10;
26480
26636
  const createImageViewPlugin = (language) => {
@@ -28660,6 +28816,7 @@ const ProsemirrorAdapter = class {
28660
28816
  nodes: nodes,
28661
28817
  marks: schema$1.spec.marks.append({
28662
28818
  strikethrough: strikethrough,
28819
+ link: linkMarkSpec,
28663
28820
  }),
28664
28821
  });
28665
28822
  }