@limetech/lime-elements 38.13.1 → 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 (44) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/limel-markdown.cjs.entry.js +1 -1
  3. package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +169 -62
  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/link/link-mark-spec.js +33 -0
  14. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/link-mark-spec.js.map +1 -0
  15. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/{link-plugin.js → link/link-plugin.js} +135 -22
  16. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/link-plugin.js.map +1 -0
  17. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/utils.js +39 -0
  18. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link/utils.js.map +1 -0
  19. package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js +3 -1
  20. package/dist/collection/components/text-editor/prosemirror-adapter/prosemirror-adapter.js.map +1 -1
  21. package/dist/esm/limel-markdown.entry.js +1 -1
  22. package/dist/esm/limel-prosemirror-adapter.entry.js +169 -62
  23. package/dist/esm/limel-prosemirror-adapter.entry.js.map +1 -1
  24. package/dist/esm/{markdown-parser-1c1fdedc.js → markdown-parser-41d15994.js} +144 -387
  25. package/dist/esm/markdown-parser-41d15994.js.map +1 -0
  26. package/dist/lime-elements/lime-elements.esm.js +1 -1
  27. package/dist/lime-elements/p-6b5d588e.entry.js +2 -0
  28. package/dist/lime-elements/p-6b5d588e.entry.js.map +1 -0
  29. package/dist/lime-elements/{p-ce152b39.entry.js → p-895ae176.entry.js} +2 -2
  30. package/dist/lime-elements/p-95a71e89.js +8 -0
  31. package/dist/lime-elements/p-95a71e89.js.map +1 -0
  32. package/dist/types/components/markdown/link-markdown-plugin.d.ts +9 -0
  33. package/dist/types/components/text-editor/prosemirror-adapter/menu/menu-commands.d.ts +0 -2
  34. package/dist/types/components/text-editor/prosemirror-adapter/plugins/link/link-mark-spec.d.ts +10 -0
  35. package/dist/types/components/text-editor/prosemirror-adapter/plugins/link/utils.d.ts +3 -0
  36. package/package.json +1 -2
  37. package/dist/collection/components/text-editor/prosemirror-adapter/plugins/link-plugin.js.map +0 -1
  38. package/dist/esm/markdown-parser-1c1fdedc.js.map +0 -1
  39. package/dist/lime-elements/p-cf87519f.js +0 -8
  40. package/dist/lime-elements/p-cf87519f.js.map +0 -1
  41. package/dist/lime-elements/p-d5ac8f59.entry.js +0 -2
  42. package/dist/lime-elements/p-d5ac8f59.entry.js.map +0 -1
  43. /package/dist/lime-elements/{p-ce152b39.entry.js.map → p-895ae176.entry.js.map} +0 -0
  44. /package/dist/types/components/text-editor/prosemirror-adapter/plugins/{link-plugin.d.ts → link/link-plugin.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [38.13.2](https://github.com/Lundalogik/lime-elements/compare/v38.13.1...v38.13.2) (2025-05-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+
7
+ * **markdown/text-editor:** properly handle internal vs external link targets ([e42123b](https://github.com/Lundalogik/lime-elements/commit/e42123b41fe2f3025c910325e37b2ac12c907cfb))
8
+ * **text-editor:** don't interpret `:` as links when pasting ([9509540](https://github.com/Lundalogik/lime-elements/commit/9509540f3591ab7401a53a5de48ca774a63ef2b2))
9
+
1
10
  ## [38.13.1](https://github.com/Lundalogik/lime-elements/compare/v38.13.0...v38.13.1) (2025-05-21)
2
11
 
3
12
 
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const index = require('./index-174a078a.js');
6
- const markdownParser = require('./markdown-parser-564adb69.js');
6
+ const markdownParser = require('./markdown-parser-563c73db.js');
7
7
  const config = require('./config-e7e1a299.js');
8
8
  require('./_commonjsHelpers-a5111d61.js');
9
9
 
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const index = require('./index-174a078a.js');
6
+ const markdownParser = require('./markdown-parser-563c73db.js');
6
7
  const _baseIsEqual = require('./_baseIsEqual-244a20fe.js');
7
8
  const _assignValue = require('./_assignValue-7a5440a6.js');
8
9
  const _baseAssignValue = require('./_baseAssignValue-e84dd769.js');
@@ -12,19 +13,18 @@ const _getTag = require('./_getTag-92cb60fe.js');
12
13
  const _getPrototype = require('./_getPrototype-18d2118e.js');
13
14
  const isArray = require('./isArray-d188a04f.js');
14
15
  const isObjectLike = require('./isObjectLike-3e3f0cba.js');
15
- const markdownParser = require('./markdown-parser-564adb69.js');
16
16
  const translations = require('./translations-1d376e48.js');
17
17
  const randomString = require('./random-string-e8ad4419.js');
18
18
  const isItem = require('./isItem-3f8ad629.js');
19
19
  const files = require('./files-c08d24d4.js');
20
20
  const isEqual = require('./isEqual-a4bccf32.js');
21
21
  const debounce = require('./debounce-2e5f4b7e.js');
22
+ require('./_commonjsHelpers-a5111d61.js');
22
23
  require('./eq-9a943b00.js');
23
24
  require('./_getNative-60328036.js');
24
25
  require('./_isIndex-b40f4fc5.js');
25
26
  require('./isArrayLike-e840b044.js');
26
27
  require('./_defineProperty-8f56146d.js');
27
- require('./_commonjsHelpers-a5111d61.js');
28
28
  require('./file-metadata-e309a7a4.js');
29
29
  require('./get-icon-props-65f39e40.js');
30
30
  require('./toNumber-062ea29c.js');
@@ -16902,24 +16902,15 @@ const setActiveMethodForWrap = (command, nodeType) => {
16902
16902
  const createInsertLinkCommand = (schema, _, link) => {
16903
16903
  const command = (state, dispatch) => {
16904
16904
  const { from, to } = state.selection;
16905
+ const linkMark = schema.marks.link.create(markdownParser.getLinkAttributes(link.href, link.href));
16905
16906
  if (from === to) {
16906
16907
  // If no text is selected, insert new text with link
16907
- const linkMark = schema.marks.link.create({
16908
- href: link.href,
16909
- title: link.href,
16910
- target: isExternalLink(link.href) ? '_blank' : null,
16911
- });
16912
16908
  const linkText = link.text || link.href;
16913
16909
  const newLink = schema.text(linkText, [linkMark]);
16914
16910
  dispatch(state.tr.insert(from, newLink));
16915
16911
  }
16916
16912
  else {
16917
16913
  // If text is selected, replace selected text with link text
16918
- const linkMark = schema.marks.link.create({
16919
- href: link.href,
16920
- title: link.href,
16921
- target: isExternalLink(link.href) ? '_blank' : null,
16922
- });
16923
16914
  const selectedText = state.doc.textBetween(from, to, ' ');
16924
16915
  const newLink = schema.text(link.text || selectedText, [linkMark]);
16925
16916
  dispatch(state.tr.replaceWith(from, to, newLink));
@@ -16993,15 +16984,6 @@ const toggleNodeType = (schema, type, attrs = {}, shouldWrap = false) => {
16993
16984
  return false;
16994
16985
  };
16995
16986
  };
16996
- const isValidUrl = (text) => {
16997
- try {
16998
- new URL(text);
16999
- }
17000
- catch (_a) {
17001
- return false;
17002
- }
17003
- return true;
17004
- };
17005
16987
  const createSetNodeTypeCommand = (schema, nodeType, level) => {
17006
16988
  const type = schema.nodes[nodeType];
17007
16989
  if (!type) {
@@ -17074,27 +17056,6 @@ const createListCommand = (schema, listType) => {
17074
17056
  setActiveMethodForWrap(command, type);
17075
17057
  return command;
17076
17058
  };
17077
- const copyPasteLinkCommand = (state, dispatch) => {
17078
- const { from, to } = state.selection;
17079
- if (from === to) {
17080
- return false;
17081
- }
17082
- const clipboardData = window.clipboardData;
17083
- if (!clipboardData) {
17084
- return false;
17085
- }
17086
- const copyPastedText = clipboardData.getData('text');
17087
- if (!isValidUrl(copyPastedText)) {
17088
- return false;
17089
- }
17090
- const linkMark = state.schema.marks.link.create({
17091
- href: copyPastedText,
17092
- target: isExternalLink(copyPastedText) ? '_blank' : null,
17093
- });
17094
- const selectedText = state.doc.textBetween(from, to, ' ');
17095
- const newLink = state.schema.text(selectedText, [linkMark]);
17096
- dispatch(state.tr.replaceWith(from, to, newLink));
17097
- };
17098
17059
  const commandMapping = {
17099
17060
  strong: createToggleMarkCommand,
17100
17061
  em: createToggleMarkCommand,
@@ -17133,7 +17094,6 @@ class MenuCommandFactory {
17133
17094
  'Mod-Shift-X': this.getCommand(EditorMenuTypes.Strikethrough),
17134
17095
  'Mod-`': this.getCommand(EditorMenuTypes.Code),
17135
17096
  'Mod-Shift-C': this.getCommand(EditorMenuTypes.CodeBlock),
17136
- 'Mod-v': copyPasteLinkCommand,
17137
17097
  };
17138
17098
  }
17139
17099
  }
@@ -26260,32 +26220,145 @@ const processClickEvent = (view, event) => {
26260
26220
  }, DOUBLE_CLICK_DELAY);
26261
26221
  return true;
26262
26222
  };
26263
- const processPasteEvent$1 = (view, event) => {
26264
- const clipboardData = event.clipboardData;
26265
- if (!clipboardData) {
26223
+ /**
26224
+ * Regular expression for matching URLs, mailto links, and phone links
26225
+ */
26226
+ const URL_REGEX = /(https?:\/\/[^\s<>"']+|mailto:[^\s<>"']+|tel:[^\s<>"']+)/g;
26227
+ /**
26228
+ * Checks if the text contains any URLs, mailto links, or phone links
26229
+ */
26230
+ const hasUrls = (text) => {
26231
+ // Reset regex before use
26232
+ URL_REGEX.lastIndex = 0;
26233
+ return URL_REGEX.test(text);
26234
+ };
26235
+ /**
26236
+ * Creates a text node with the provided content
26237
+ */
26238
+ const createTextNode = (schema, content) => {
26239
+ return schema.text(content);
26240
+ };
26241
+ /**
26242
+ * Creates a link node with the provided URL
26243
+ */
26244
+ const createLinkNode = (schema, url) => {
26245
+ const linkMark = schema.marks.link.create(markdownParser.getLinkAttributes(url, url));
26246
+ return schema.text(url, [linkMark]);
26247
+ };
26248
+ /**
26249
+ * Finds all link matches in the provided text
26250
+ */
26251
+ const findLinkMatches = (text) => {
26252
+ const matches = [];
26253
+ let match;
26254
+ // Reset regex before use
26255
+ URL_REGEX.lastIndex = 0;
26256
+ while ((match = URL_REGEX.exec(text)) !== null) {
26257
+ matches.push({
26258
+ url: match[0],
26259
+ start: match.index,
26260
+ end: match.index + match[0].length,
26261
+ });
26262
+ }
26263
+ return matches;
26264
+ };
26265
+ /**
26266
+ * Creates text nodes with links for any URLs, mailto links, or phone links found in the text
26267
+ */
26268
+ const createNodesWithLinks = (text, schema) => {
26269
+ const nodes = [];
26270
+ const matches = findLinkMatches(text);
26271
+ if (matches.length === 0) {
26272
+ // No links found, just return the text as a single node
26273
+ return [createTextNode(schema, text)];
26274
+ }
26275
+ let lastIndex = 0;
26276
+ // Process each match
26277
+ for (const match of matches) {
26278
+ // Add text before the current link if any
26279
+ if (match.start > lastIndex) {
26280
+ nodes.push(createTextNode(schema, text.slice(lastIndex, match.start)));
26281
+ }
26282
+ // Add the link node
26283
+ nodes.push(createLinkNode(schema, match.url));
26284
+ lastIndex = match.end;
26285
+ }
26286
+ // Add any remaining text after the last link
26287
+ if (lastIndex < text.length) {
26288
+ nodes.push(createTextNode(schema, text.slice(lastIndex)));
26289
+ }
26290
+ return nodes;
26291
+ };
26292
+ /**
26293
+ * Pastes nodes at the current selection
26294
+ * @param view - The editor view
26295
+ * @param nodes - Array of nodes to paste
26296
+ */
26297
+ const pasteAsLink = (view, nodes) => {
26298
+ if (nodes.length === 0) {
26299
+ return;
26300
+ }
26301
+ if (isSingleLinkNode(nodes)) {
26302
+ insertSingleLink(view, nodes[0]);
26303
+ }
26304
+ else {
26305
+ insertNodeFragment(view, nodes);
26306
+ }
26307
+ };
26308
+ /**
26309
+ * Checks if the nodes array contains just a single link node
26310
+ */
26311
+ const isSingleLinkNode = (nodes) => {
26312
+ if (nodes.length !== 1) {
26266
26313
  return false;
26267
26314
  }
26268
- const text = clipboardData.getData('text/plain');
26269
- // Process as a link if the text is a valid URL
26270
- if (isValidUrl(text)) {
26271
- pasteAsLink(view, text);
26272
- return true;
26315
+ const node = nodes[0];
26316
+ // Must be text with non-empty content
26317
+ if (!node.isText || !node.text || node.text.trim() === '') {
26318
+ return false;
26273
26319
  }
26274
- return false;
26320
+ // Must have a link mark (even if there are other marks, we just care about link presence)
26321
+ return !!node.marks.find((mark) => mark.type.name === 'link');
26275
26322
  };
26276
- const pasteAsLink = (view, href) => {
26323
+ /**
26324
+ * Inserts a single link node, applying it to selected text if present
26325
+ */
26326
+ const insertSingleLink = (view, linkNode) => {
26277
26327
  const { state, dispatch } = view;
26278
26328
  const { from, to } = state.selection;
26279
- const linkMark = schema$1.marks.link.create({
26280
- href: href,
26281
- title: href,
26282
- target: isExternalLink(href) ? '_blank' : null,
26283
- });
26284
- const selectedText = state.doc.textBetween(from, to, ' ') || href;
26285
- const transaction = state.tr
26329
+ const linkMark = linkNode.marks.find((mark) => mark.type.name === 'link');
26330
+ // Use selected text if there's a selection, otherwise use the URL
26331
+ const selectedText = state.doc.textBetween(from, to, ' ') || linkMark.attrs.href;
26332
+ // Insert the text and add the link mark
26333
+ dispatch(state.tr
26286
26334
  .insertText(selectedText, from, to)
26287
- .addMark(from, from + selectedText.length, linkMark);
26288
- dispatch(transaction);
26335
+ .addMark(from, from + selectedText.length, linkMark));
26336
+ };
26337
+ /**
26338
+ * Inserts multiple nodes as a fragment at the current selection
26339
+ * @param view - The editor view
26340
+ * @param nodes - Array of nodes to insert
26341
+ */
26342
+ const insertNodeFragment = (view, nodes) => {
26343
+ const { state, dispatch } = view;
26344
+ const { from, to } = state.selection;
26345
+ // Create a fragment from the array of nodes
26346
+ const fragment = Fragment.fromArray(nodes);
26347
+ // Replace the current selection with the fragment
26348
+ dispatch(state.tr.replaceWith(from, to, fragment));
26349
+ };
26350
+ /**
26351
+ * Handles pasted content, converting URLs to links
26352
+ */
26353
+ const processPasteEvent$1 = (view, event) => {
26354
+ var _a;
26355
+ const text = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text/plain');
26356
+ if (!text || !hasUrls(text)) {
26357
+ return false;
26358
+ }
26359
+ const nodes = createNodesWithLinks(text, view.state.schema);
26360
+ pasteAsLink(view, nodes);
26361
+ return true;
26289
26362
  };
26290
26363
  const createLinkPlugin = (updateLinkCallback) => {
26291
26364
  return new Plugin({
@@ -26327,6 +26400,39 @@ const createLinkPlugin = (updateLinkCallback) => {
26327
26400
  });
26328
26401
  };
26329
26402
 
26403
+ const linkMarkSpec = {
26404
+ attrs: {
26405
+ href: { default: '' },
26406
+ title: { default: null },
26407
+ target: { default: null },
26408
+ rel: { default: null },
26409
+ referrerpolicy: { default: null },
26410
+ },
26411
+ inclusive: false,
26412
+ parseDOM: [
26413
+ {
26414
+ tag: 'a[href]',
26415
+ getAttrs: (dom) => {
26416
+ return {
26417
+ href: dom.getAttribute('href') || '',
26418
+ title: dom.getAttribute('title'),
26419
+ target: dom.getAttribute('target'),
26420
+ rel: dom.getAttribute('rel'),
26421
+ referrerpolicy: dom.getAttribute('referrerpolicy'),
26422
+ };
26423
+ },
26424
+ },
26425
+ ],
26426
+ toDOM: (mark) => {
26427
+ const target = mark.attrs.target || null;
26428
+ const securityAttrs = {
26429
+ rel: target === '_blank' ? 'noopener noreferrer' : null,
26430
+ referrerpolicy: target === '_blank' ? 'noreferrer' : null,
26431
+ };
26432
+ return ['a', Object.assign(Object.assign({}, mark.attrs), securityAttrs), 0];
26433
+ },
26434
+ };
26435
+
26330
26436
  const pluginKey = new PluginKey('imageInserterPlugin');
26331
26437
  const createImageInserterPlugin = (imagePastedCallback) => {
26332
26438
  return new Plugin({
@@ -28714,6 +28820,7 @@ const ProsemirrorAdapter = class {
28714
28820
  nodes: nodes,
28715
28821
  marks: schema$1.spec.marks.append({
28716
28822
  strikethrough: strikethrough,
28823
+ link: linkMarkSpec,
28717
28824
  }),
28718
28825
  });
28719
28826
  }