@bagelink/vue 1.15.75 → 1.15.80
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/dist/components/Image.vue.d.ts +0 -5
- package/dist/components/Image.vue.d.ts.map +1 -1
- package/dist/components/PageTitle.vue.d.ts +10 -0
- package/dist/components/PageTitle.vue.d.ts.map +1 -1
- package/dist/components/RouterWrapper.vue.d.ts +6 -2
- package/dist/components/RouterWrapper.vue.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts +2 -0
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts +5 -0
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/components/lightbox/LightboxImage.vue.d.ts +6 -0
- package/dist/components/lightbox/LightboxImage.vue.d.ts.map +1 -0
- package/dist/components/lightbox/index.d.ts.map +1 -1
- package/dist/composables/index.d.ts +1 -0
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useImageSrc.d.ts +20 -0
- package/dist/composables/useImageSrc.d.ts.map +1 -0
- package/dist/index.cjs +34 -34
- package/dist/index.mjs +4296 -4262
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Image.vue +3 -79
- package/src/components/PageTitle.vue +14 -28
- package/src/components/RouterWrapper.vue +22 -0
- package/src/components/layout/AppContent.vue +3 -1
- package/src/components/layout/AppSidebar.vue +8 -3
- package/src/components/lightbox/Lightbox.vue +2 -1
- package/src/components/lightbox/LightboxImage.vue +14 -0
- package/src/components/lightbox/index.ts +4 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/useImageSrc.ts +88 -0
- package/src/styles/color-variants.css +13 -4
- package/src/utils/index.ts +1 -1
package/package.json
CHANGED
package/src/components/Image.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
defineOptions({ name: 'BglImage', inheritAttrs: true })
|
|
3
|
-
import { Skeleton, normalizeDimension,
|
|
4
|
-
import { computed,
|
|
3
|
+
import { Skeleton, normalizeDimension, Icon, useImageSrc } from '@bagelink/vue'
|
|
4
|
+
import { computed, useSlots } from 'vue'
|
|
5
5
|
|
|
6
6
|
interface ImageProps {
|
|
7
7
|
src?: string
|
|
@@ -23,85 +23,9 @@ interface ImageProps {
|
|
|
23
23
|
rounded?: boolean | number
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
declare global {
|
|
27
|
-
interface Window {
|
|
28
|
-
heic2any: any
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
26
|
const props = defineProps<ImageProps>()
|
|
33
27
|
|
|
34
|
-
const imageSrc
|
|
35
|
-
const loadingError = ref(false)
|
|
36
|
-
|
|
37
|
-
function getImageUrl(): string | undefined {
|
|
38
|
-
if ((props.src === undefined || props.src === '') && (props.pathKey === undefined || props.pathKey === '') && (props.modelValue === undefined || props.modelValue === '')) { return }
|
|
39
|
-
return pathKeyToURL(props.src ?? props.pathKey ?? props.modelValue)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function getCachedImage(url: string): Promise<string | undefined> {
|
|
43
|
-
if (!('caches' in window)) { return undefined }
|
|
44
|
-
try {
|
|
45
|
-
const imgCache = await window.caches.open('img-cache')
|
|
46
|
-
const cachedResponse = await imgCache.match(url)
|
|
47
|
-
if (cachedResponse) {
|
|
48
|
-
return URL.createObjectURL(await cachedResponse.blob())
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.warn('Cache access error:', error)
|
|
52
|
-
}
|
|
53
|
-
return undefined
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function cacheImage(url: string, blob: Blob) {
|
|
57
|
-
if (!('caches' in window)) { return }
|
|
58
|
-
try {
|
|
59
|
-
const imgCache = await window.caches.open('img-cache')
|
|
60
|
-
await imgCache.put(url, new Response(blob))
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.warn('Cache write error:', error)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function convertHeicImage(url: string): Promise<string> {
|
|
67
|
-
await appendScript('https://cdnjs.cloudflare.com/ajax/libs/heic2any/0.0.1/index.min.js')
|
|
68
|
-
const heic2any = await awaitGlobal<(opts: { blob: Blob }) => Promise<Blob>>('heic2any')
|
|
69
|
-
const response = await fetch(normalizeURL(url))
|
|
70
|
-
const blob = await response.blob()
|
|
71
|
-
const convertedBlob = await heic2any({ blob })
|
|
72
|
-
await cacheImage(url, convertedBlob)
|
|
73
|
-
return URL.createObjectURL(convertedBlob)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function loadImage() {
|
|
77
|
-
loadingError.value = false
|
|
78
|
-
const url = getImageUrl()
|
|
79
|
-
if (url === undefined || url === '') {
|
|
80
|
-
imageSrc.value = undefined
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
const ext = url.split('.').pop()?.toLowerCase().split('?')[0]
|
|
86
|
-
|
|
87
|
-
if (ext === 'heic') {
|
|
88
|
-
const cachedSrc = await getCachedImage(url)
|
|
89
|
-
if (cachedSrc !== undefined && cachedSrc !== '') {
|
|
90
|
-
imageSrc.value = cachedSrc
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
imageSrc.value = await convertHeicImage(url)
|
|
94
|
-
} else {
|
|
95
|
-
imageSrc.value = url
|
|
96
|
-
}
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error('Image loading error:', error)
|
|
99
|
-
loadingError.value = true
|
|
100
|
-
imageSrc.value = undefined
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
watch(() => [props.src, props.pathKey, props.modelValue], loadImage, { immediate: true })
|
|
28
|
+
const { imageSrc, loadingError } = useImageSrc(() => props.src ?? props.pathKey ?? props.modelValue)
|
|
105
29
|
|
|
106
30
|
// ── Framed mode (ratio / gradient / blend / rounded / overlay slot) ──────────
|
|
107
31
|
const slots = useSlots()
|
|
@@ -4,36 +4,22 @@ defineProps({
|
|
|
4
4
|
type: String,
|
|
5
5
|
default: '',
|
|
6
6
|
},
|
|
7
|
-
|
|
7
|
+
subtitle: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: '',
|
|
10
|
+
},
|
|
8
11
|
})
|
|
9
12
|
</script>
|
|
10
13
|
|
|
11
14
|
<template>
|
|
12
|
-
<div class="page-top">
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
15
|
+
<div class="page-top flex gap-1">
|
|
16
|
+
<div class="grid gap-025">
|
|
17
|
+
<h1 class="m-0 semibold txt-20 m_txt16 line-height-1">
|
|
18
|
+
<slot /> {{ value }}
|
|
19
|
+
</h1>
|
|
20
|
+
<p v-if="subtitle || $slots.subtitle" class="top-subtitle m-0 txt-13 m_txt12 align-items-center line-height-12 opacity-6">
|
|
21
|
+
<slot name="subtitle">{{ subtitle }}</slot>
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
16
24
|
</div>
|
|
17
|
-
</template>
|
|
18
|
-
|
|
19
|
-
<style>
|
|
20
|
-
|
|
21
|
-
.top-title {
|
|
22
|
-
font-weight: 600;
|
|
23
|
-
font-size: 20px;
|
|
24
|
-
line-height: 1;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
.page-top {
|
|
28
|
-
display: flex;
|
|
29
|
-
align-items: center;
|
|
30
|
-
gap: 1rem;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
@media screen and (max-width: 910px) {
|
|
34
|
-
.top-title {
|
|
35
|
-
font-size: 16px;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
</style>
|
|
25
|
+
</template>
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { nextTick, ref, watch } from 'vue'
|
|
3
|
+
import { useRoute } from 'vue-router'
|
|
2
4
|
import Loading from './Loading.vue'
|
|
5
|
+
|
|
6
|
+
// Reset the scroll position on every route change. The app's scroll container is
|
|
7
|
+
// the inner `.pageContent` element in AppContent (the window itself doesn't
|
|
8
|
+
// scroll), so a plain vue-router `scrollBehavior` wouldn't help. We scroll the
|
|
9
|
+
// nearest `.pageContent` ancestor back to the top after the new view mounts.
|
|
10
|
+
const anchor = ref<HTMLElement | null>(null)
|
|
11
|
+
const route = useRoute()
|
|
12
|
+
|
|
13
|
+
watch(
|
|
14
|
+
() => route.fullPath,
|
|
15
|
+
async () => {
|
|
16
|
+
await nextTick()
|
|
17
|
+
const container = anchor.value?.closest('.pageContent') as HTMLElement | null
|
|
18
|
+
if (container) container.scrollTop = 0
|
|
19
|
+
// Fallback: also reset the window in case a view scrolls the page itself.
|
|
20
|
+
else window.scrollTo({ top: 0 })
|
|
21
|
+
},
|
|
22
|
+
)
|
|
3
23
|
</script>
|
|
4
24
|
|
|
5
25
|
<template>
|
|
26
|
+
<!-- Zero-size anchor used to locate the scrolling `.pageContent` ancestor. -->
|
|
27
|
+
<span ref="anchor" aria-hidden="true" style="display: none" />
|
|
6
28
|
<RouterView v-slot="{ Component, route }">
|
|
7
29
|
<slot v-if="!Component">
|
|
8
30
|
<div class="w-100p h-100vh flex justify-content-center">
|
|
@@ -4,6 +4,8 @@ import { useAppLayout } from './appLayoutContext'
|
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
title?: string
|
|
7
|
+
/** Secondary line under the title (page description / context). */
|
|
8
|
+
subtitle?: string
|
|
7
9
|
showMenuButton?: boolean
|
|
8
10
|
backTo?: string | Record<string, any>
|
|
9
11
|
border?: boolean
|
|
@@ -41,7 +43,7 @@ const { isOpen, toggleMenu, sidebarCardStyle } = useAppLayout()
|
|
|
41
43
|
<Btn v-if="backTo" icon="arrow_back" thin :to="backTo" class="back-btn bg-bg m_-ms-05 m_-me-05 color-black" />
|
|
42
44
|
|
|
43
45
|
<!-- Page Title -->
|
|
44
|
-
<PageTitle v-if="title">
|
|
46
|
+
<PageTitle v-if="title || subtitle" :subtitle="subtitle">
|
|
45
47
|
{{ title }}
|
|
46
48
|
</PageTitle>
|
|
47
49
|
|
|
@@ -24,6 +24,8 @@ interface Props {
|
|
|
24
24
|
activeColor?: string
|
|
25
25
|
logoHeight?: string
|
|
26
26
|
name?: string
|
|
27
|
+
/** Small tagline rendered under the app name (hidden when collapsed). */
|
|
28
|
+
nameSubtitle?: string
|
|
27
29
|
frame?: boolean
|
|
28
30
|
activeRoutes?: string[]
|
|
29
31
|
centerlinks?: boolean
|
|
@@ -162,9 +164,12 @@ const sidebarStyles = computed(() => {
|
|
|
162
164
|
v-if="props.logo" :src="props.logo" :alt="props.logoAlt" class="contain"
|
|
163
165
|
:style="{ height: props.logoHeight }"
|
|
164
166
|
>
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
<slot name="brand" v-bind="{ isOpen: isVisuallyOpen }">
|
|
168
|
+
<span class="nav-text flex column line-height-1 gap-025 align-items-start">
|
|
169
|
+
<span>{{ props.name }}</span>
|
|
170
|
+
<span v-if="props.nameSubtitle" class="txt11 opacity-6">{{ props.nameSubtitle }}</span>
|
|
171
|
+
</span>
|
|
172
|
+
</slot>
|
|
168
173
|
</router-link>
|
|
169
174
|
|
|
170
175
|
<!-- Navigation Links -->
|
|
@@ -3,6 +3,7 @@ import type { LightboxItem } from './lightbox.types'
|
|
|
3
3
|
|
|
4
4
|
import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL, Swiper, downloadFile, useEscapeKey } from '@bagelink/vue'
|
|
5
5
|
import { computed, ref, watch } from 'vue'
|
|
6
|
+
import LightboxImage from './LightboxImage.vue'
|
|
6
7
|
|
|
7
8
|
const isOpen = ref(false)
|
|
8
9
|
const group = ref<LightboxItem[]>([])
|
|
@@ -88,7 +89,7 @@ defineExpose({ open, close })
|
|
|
88
89
|
<Zoomer v-if="item.type === 'image'" v-model:zoom="zoom" :disabled="!item?.enableZoom"
|
|
89
90
|
:mouse-wheel-to-zoom="false" :double-click-to-zoom="true" :max-scale="5" :min-scale="1"
|
|
90
91
|
:aspect-ratio="0" :limit-translation="true" @click.stop>
|
|
91
|
-
<
|
|
92
|
+
<LightboxImage :src="item?.src" />
|
|
92
93
|
</Zoomer>
|
|
93
94
|
|
|
94
95
|
<BglVideo v-else-if="item?.type === 'video' && item?.src" :src="item?.src" autoplay controls
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Plain <img> for the lightbox preview that still gets shared src resolution +
|
|
3
|
+
// HEIC conversion via useImageSrc. A bare <img> (not <Image>) is required here
|
|
4
|
+
// so the natural dimensions drive layout — <Image>'s framed mode renders an
|
|
5
|
+
// absolute-fill img that collapses the zoomer to 0×0.
|
|
6
|
+
import { useImageSrc } from '@bagelink/vue'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<{ src?: string }>()
|
|
9
|
+
const { imageSrc } = useImageSrc(() => props.src)
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<img :draggable="false" :src="imageSrc" alt="Preview" class="lightbox-image">
|
|
14
|
+
</template>
|
|
@@ -70,6 +70,10 @@ function urlToName(url: string): string {
|
|
|
70
70
|
|
|
71
71
|
function determineFileType(url: any): string {
|
|
72
72
|
if (typeof url !== 'string' || !url) { return 'unknown' }
|
|
73
|
+
// Extensionless sources (object URLs, data URLs, signed/CDN URLs) can't be sniffed
|
|
74
|
+
// by extension — fall back to MIME hints so they still preview as images/video.
|
|
75
|
+
if (url.startsWith('blob:') || /^data:image\//i.test(url)) { return 'image' }
|
|
76
|
+
if (/^data:video\//i.test(url)) { return 'video' }
|
|
73
77
|
const extension = (url.split('.').pop() || '').toLowerCase()
|
|
74
78
|
const altExtension = url.split('?')[0].split('.').pop()?.toLowerCase() || ''
|
|
75
79
|
if (IMAGE_FORMATS_REGEXP.test(extension) || IMAGE_FORMATS_REGEXP.test(altExtension)) { return 'image' }
|
package/src/composables/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { useEscapeKey } from './useEscapeKey'
|
|
|
10
10
|
export { useExcel } from './useExcel'
|
|
11
11
|
export type { GradientDir, GradientDirProp, GradientProp } from './useGradientVariant'
|
|
12
12
|
export { useGradientVariant } from './useGradientVariant'
|
|
13
|
+
export { useImageSrc } from './useImageSrc'
|
|
13
14
|
export { useLocalStore } from './useLocalStore'
|
|
14
15
|
export { usePolling } from './usePolling'
|
|
15
16
|
export { useQuery } from './useQuery'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
2
|
+
import { appendScript, awaitGlobal, normalizeURL, pathKeyToURL } from '@bagelink/vue'
|
|
3
|
+
import { ref, toValue, watch } from 'vue'
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
heic2any: any
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function getCachedImage(url: string): Promise<string | undefined> {
|
|
12
|
+
if (!('caches' in window)) { return undefined }
|
|
13
|
+
try {
|
|
14
|
+
const imgCache = await window.caches.open('img-cache')
|
|
15
|
+
const cachedResponse = await imgCache.match(url)
|
|
16
|
+
if (cachedResponse) {
|
|
17
|
+
return URL.createObjectURL(await cachedResponse.blob())
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.warn('Cache access error:', error)
|
|
21
|
+
}
|
|
22
|
+
return undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function cacheImage(url: string, blob: Blob) {
|
|
26
|
+
if (!('caches' in window)) { return }
|
|
27
|
+
try {
|
|
28
|
+
const imgCache = await window.caches.open('img-cache')
|
|
29
|
+
await imgCache.put(url, new Response(blob))
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.warn('Cache write error:', error)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function convertHeicImage(url: string): Promise<string> {
|
|
36
|
+
await appendScript('https://cdnjs.cloudflare.com/ajax/libs/heic2any/0.0.1/index.min.js')
|
|
37
|
+
const heic2any = await awaitGlobal<(opts: { blob: Blob }) => Promise<Blob>>('heic2any')
|
|
38
|
+
const response = await fetch(normalizeURL(url))
|
|
39
|
+
const blob = await response.blob()
|
|
40
|
+
const convertedBlob = await heic2any({ blob })
|
|
41
|
+
await cacheImage(url, convertedBlob)
|
|
42
|
+
return URL.createObjectURL(convertedBlob)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve an image source for display: turns a pathKey/URL into a usable URL via
|
|
47
|
+
* `pathKeyToURL`, and transparently converts `.heic` files to a displayable blob
|
|
48
|
+
* (with a cache) since browsers can't render HEIC in <img>. Shared by <Image>
|
|
49
|
+
* and the lightbox so both get the same resolution + HEIC handling.
|
|
50
|
+
*
|
|
51
|
+
* @param source ref/getter of the raw src or pathKey
|
|
52
|
+
* @returns `{ imageSrc, loadingError }` reactive refs
|
|
53
|
+
*/
|
|
54
|
+
export function useImageSrc(source: MaybeRefOrGetter<string | undefined>) {
|
|
55
|
+
const imageSrc = ref<string | undefined>(undefined)
|
|
56
|
+
const loadingError = ref(false)
|
|
57
|
+
|
|
58
|
+
async function load() {
|
|
59
|
+
loadingError.value = false
|
|
60
|
+
const url = pathKeyToURL(toValue(source))
|
|
61
|
+
if (url === undefined || url === '') {
|
|
62
|
+
imageSrc.value = undefined
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const ext = url.split('.').pop()?.toLowerCase().split('?')[0]
|
|
68
|
+
if (ext === 'heic') {
|
|
69
|
+
const cachedSrc = await getCachedImage(url)
|
|
70
|
+
if (cachedSrc !== undefined && cachedSrc !== '') {
|
|
71
|
+
imageSrc.value = cachedSrc
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
imageSrc.value = await convertHeicImage(url)
|
|
75
|
+
} else {
|
|
76
|
+
imageSrc.value = url
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error('Image loading error:', error)
|
|
80
|
+
loadingError.value = true
|
|
81
|
+
imageSrc.value = undefined
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
watch(() => toValue(source), load, { immediate: true })
|
|
86
|
+
|
|
87
|
+
return { imageSrc, loadingError }
|
|
88
|
+
}
|
|
@@ -137,12 +137,21 @@
|
|
|
137
137
|
* The `via` slot collapses to nothing when unset (2-stop), expands when set
|
|
138
138
|
* (3-stop) — exactly like gradients.css. */
|
|
139
139
|
[class*="pair-"].gradient {
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
/* Auto (single-tone) gradient: a vivid sweep from a lighter take on the tone
|
|
141
|
+
to a darker one, giving real depth without going muddy.
|
|
142
|
+
|
|
143
|
+
IMPORTANT: gradients.css declares --bgl-grad-from/to on :root (= transparent),
|
|
144
|
+
so a `var(--bgl-grad-from, fallback)` would never use the fallback. We instead
|
|
145
|
+
*set* --bgl-grad-from/to right here to the auto values. The Btn/Badge
|
|
146
|
+
components override them again via inline style for explicit 2–3 stop
|
|
147
|
+
gradients, and gradients.css `from-*`/`to-*` utilities still win by being
|
|
148
|
+
later/inline. */
|
|
149
|
+
--bgl-grad-from: color-mix(in srgb, var(--bgl-pair-tone, var(--bgl-primary)) 90%, #fff);
|
|
150
|
+
--bgl-grad-to: color-mix(in srgb, var(--bgl-pair-tone, var(--bgl-primary)) 55%, #000);
|
|
142
151
|
background-image: linear-gradient(
|
|
143
152
|
var(--bgl-grad-angle, 135deg),
|
|
144
|
-
var(--bgl-grad-from
|
|
145
|
-
var(--bgl-grad-via, ) var(--bgl-grad-to
|
|
153
|
+
var(--bgl-grad-from),
|
|
154
|
+
var(--bgl-grad-via, ) var(--bgl-grad-to)
|
|
146
155
|
) !important;
|
|
147
156
|
color: var(--bgl-white) !important;
|
|
148
157
|
border: none !important;
|
package/src/utils/index.ts
CHANGED
|
@@ -257,7 +257,7 @@ export type { ComparisonOperator, FilterCondition, LogicalOperator, QueryConditi
|
|
|
257
257
|
export { anyOf, buildQuery, evaluateQuery, parseQuery, queryFilter, range, search } from './queryFilter'
|
|
258
258
|
export type { ShowdownConverter, ShowdownOptions } from './showdown'
|
|
259
259
|
|
|
260
|
-
const URL_REGEX = /^https
|
|
260
|
+
const URL_REGEX = /^https?:\/\/|^\/\/|^blob:|^data:/
|
|
261
261
|
|
|
262
262
|
export function pathKeyToURL(pathKey?: string | null): string | undefined {
|
|
263
263
|
if (pathKey == null || pathKey === '') {
|