@bagelink/vue 1.18.7 → 1.19.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.18.7",
4
+ "version": "1.19.1",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -30,7 +30,6 @@ import {
30
30
  import type { BagelInputShellProps } from '../bagelInputShell'
31
31
  import { useBagelInputShell } from '../bagelInputShell'
32
32
  import { RICH_TEXT_EXTENSION_REGISTRY_KEY } from './richTextExtensions'
33
- import { normalizeRichTextLink, sanitizeRichTextLinks } from './utils/linkPolicy'
34
33
 
35
34
  // Disable automatic inheritance of non-prop attributes
36
35
  defineOptions({
@@ -697,7 +696,7 @@ function showTooltipMessage(message: string, x?: number, y?: number) {
697
696
  function openLinkModal(selection: Selection, range: Range, existingLink: HTMLAnchorElement | null) {
698
697
  pendingLinkData = { selection, range, existingLink }
699
698
  linkForm.value = {
700
- url: existingLink?.getAttribute('href') || '',
699
+ url: existingLink?.href || '',
701
700
  title: existingLink?.getAttribute('title') || '',
702
701
  openInNewTab: existingLink?.target !== '_self'
703
702
  }
@@ -706,19 +705,17 @@ function openLinkModal(selection: Selection, range: Range, existingLink: HTMLAnc
706
705
 
707
706
  // Function to submit link
708
707
  function submitLink() {
709
- const normalizedUrl = normalizeRichTextLink(linkForm.value.url)
710
- if (!pendingLinkData || !normalizedUrl) { return }
708
+ if (!pendingLinkData || !linkForm.value.url) { return }
711
709
 
712
710
  const { selection, range, existingLink } = pendingLinkData
713
- const { title, openInNewTab } = linkForm.value
711
+ const { url, title, openInNewTab } = linkForm.value
714
712
  const { doc } = editor.state
715
713
 
716
714
  if (!doc) { return }
717
- linkForm.value.url = normalizedUrl
718
715
 
719
716
  if (existingLink) {
720
717
  // Update existing link
721
- existingLink.setAttribute('href', normalizedUrl)
718
+ existingLink.href = url
722
719
  if (title.trim()) {
723
720
  existingLink.title = title.trim()
724
721
  } else {
@@ -734,7 +731,7 @@ function submitLink() {
734
731
  // Create new link
735
732
  try {
736
733
  const anchor = doc.createElement('a')
737
- anchor.setAttribute('href', normalizedUrl)
734
+ anchor.href = url
738
735
  if (title.trim()) {
739
736
  anchor.title = title.trim()
740
737
  }
@@ -769,8 +766,14 @@ function submitLink() {
769
766
 
770
767
  // Function to visit link
771
768
  function visitLink() {
772
- const url = normalizeRichTextLink(linkForm.value.url)
773
- if (!url) { return }
769
+ if (!linkForm.value.url || !isValidUrl(linkForm.value.url)) { return }
770
+
771
+ let url = linkForm.value.url.trim()
772
+
773
+ // Add https:// if no protocol is specified
774
+ if (!url.match(/^https?:\/\//)) {
775
+ url = `https://${url}`
776
+ }
774
777
 
775
778
  if (linkForm.value.openInNewTab) {
776
779
  window.open(url, '_blank', 'noopener,noreferrer')
@@ -781,7 +784,20 @@ function visitLink() {
781
784
 
782
785
  // Function to validate URL
783
786
  function isValidUrl(url: string): boolean {
784
- return normalizeRichTextLink(url) !== null
787
+ if (!url || url.trim() === '') { return false }
788
+
789
+ try {
790
+ URL.canParse(url)
791
+ return true
792
+ } catch {
793
+ // Try with https:// prefix if it's missing
794
+ try {
795
+ URL.canParse(`https://${url}`)
796
+ return true
797
+ } catch {
798
+ return false
799
+ }
800
+ }
785
801
  }
786
802
 
787
803
  // Function to open image modal
@@ -915,7 +931,7 @@ function submitImage() {
915
931
 
916
932
  // Wrap the image in an anchor when a link is provided, otherwise return the bare image
917
933
  function createImageContent(): HTMLElement {
918
- const link = normalizeRichTextLink(imageForm.value.link)
934
+ const link = imageForm.value.link?.trim()
919
935
  if (!link) { return img }
920
936
 
921
937
  const anchor = doc!.createElement('a')
@@ -1985,10 +2001,6 @@ function getCleanHTML(doc: Document | null): string {
1985
2001
  if (fig.classList.length === 0) { fig.removeAttribute('class') }
1986
2002
  })
1987
2003
 
1988
- // Keep imported/pasted legacy links from leaking into saved output. Invalid
1989
- // destinations lose their href, but their visible content is preserved.
1990
- sanitizeRichTextLinks(clone)
1991
-
1992
2004
  const html = clone.innerHTML
1993
2005
  if (!clone.textContent?.trim() && !clone.querySelector('img, video, iframe, table, hr, figure')) {
1994
2006
  return ''
@@ -1,11 +0,0 @@
1
- /**
2
- * Validate and normalize a link authored in RichText.
3
- *
4
- * Relative application paths must be root-relative (`/contact`). Ambiguous
5
- * imported CMS links (`edit?...`, `admin/...`) are rejected instead of being
6
- * silently resolved against the current page.
7
- */
8
- export declare function normalizeRichTextLink(value: unknown): string | null;
9
- /** Remove invalid destinations while preserving their visible linked content. */
10
- export declare function sanitizeRichTextLinks(root: ParentNode): number;
11
- //# sourceMappingURL=linkPolicy.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"linkPolicy.d.ts","sourceRoot":"","sources":["../../../../../../src/components/form/inputs/RichText/utils/linkPolicy.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAoBnE;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAc9D"}
@@ -1,48 +0,0 @@
1
- const EXPLICIT_SAFE_URL = /^(?:https?:\/\/|mailto:|tel:)/i
2
- const BARE_DOMAIN = /^(?:localhost(?::\d+)?|(?:[a-z0-9-]+\.)+[a-z]{2,})(?:[/:?#][^\s]*)?$/i
3
-
4
- /**
5
- * Validate and normalize a link authored in RichText.
6
- *
7
- * Relative application paths must be root-relative (`/contact`). Ambiguous
8
- * imported CMS links (`edit?...`, `admin/...`) are rejected instead of being
9
- * silently resolved against the current page.
10
- */
11
- export function normalizeRichTextLink(value: unknown): string | null {
12
- if (typeof value !== 'string') return null
13
- const url = value.trim()
14
- if (!url || /[\u0000-\u001F\u007F]/.test(url)) return null
15
-
16
- if (url.startsWith('/') && !url.startsWith('//')) return url
17
- if (url.startsWith('#') && url.length > 1) return url
18
-
19
- if (EXPLICIT_SAFE_URL.test(url)) {
20
- if (/^mailto:/i.test(url)) return url.slice(7).trim() ? url : null
21
- if (/^tel:/i.test(url)) return url.slice(4).trim() ? url : null
22
- try {
23
- const parsed = new URL(url)
24
- return parsed.protocol === 'http:' || parsed.protocol === 'https:' ? url : null
25
- } catch {
26
- return null
27
- }
28
- }
29
-
30
- return BARE_DOMAIN.test(url) ? `https://${url}` : null
31
- }
32
-
33
- /** Remove invalid destinations while preserving their visible linked content. */
34
- export function sanitizeRichTextLinks(root: ParentNode): number {
35
- let removed = 0
36
- for (const anchor of Array.from(root.querySelectorAll<HTMLAnchorElement>('a[href]'))) {
37
- const normalized = normalizeRichTextLink(anchor.getAttribute('href'))
38
- if (normalized) {
39
- anchor.setAttribute('href', normalized)
40
- continue
41
- }
42
- anchor.removeAttribute('href')
43
- anchor.removeAttribute('target')
44
- anchor.removeAttribute('rel')
45
- removed++
46
- }
47
- return removed
48
- }