@rimelight/ui 0.0.47 → 0.0.49

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 (197) hide show
  1. package/dist/index.d.mts +4 -5
  2. package/dist/preset.mjs +73 -1
  3. package/dist/resolver.d.mts +3 -4
  4. package/dist/resolver.mjs +2 -4
  5. package/dist/shortcuts.d.mts +10 -11
  6. package/dist/stores.d.mts +3 -4
  7. package/package.json +8 -6
  8. package/src/components/3d-hover/RLS3dHover.tsx +21 -0
  9. package/src/components/accordion/RLSAccordion.tsx +112 -0
  10. package/src/components/app/RLSApp.tsx +21 -0
  11. package/src/components/aspect-ratio/RLSAspectRatio.tsx +30 -0
  12. package/src/components/audio/RLSAudio.tsx +27 -0
  13. package/src/components/avatar/RLSAvatar.tsx +142 -0
  14. package/src/components/avatar/avatar.ts +22 -21
  15. package/src/components/avatar-group/RLSAvatarGroup.tsx +163 -0
  16. package/src/components/badge/RLSBadge.tsx +1 -1
  17. package/src/components/badge/RLVBadge.vue +5 -3
  18. package/src/components/banner/RLSBanner.tsx +151 -0
  19. package/src/components/breadcrumb/RLSBreadcrumb.tsx +194 -0
  20. package/src/components/browser-mockup/RLSBrowserMockup.tsx +21 -0
  21. package/src/components/button/RLSButton.tsx +49 -40
  22. package/src/components/button/RLVButton.vue +44 -32
  23. package/src/components/calendar/RLSCalendar.tsx +26 -0
  24. package/src/components/callout/RLSCallout.tsx +151 -0
  25. package/src/components/card/RLSCard.tsx +4 -0
  26. package/src/components/card/RLVCard.vue +4 -0
  27. package/src/components/card/card.ts +10 -0
  28. package/src/components/carousel/RLACarousel.astro +14 -1
  29. package/src/components/carousel/RLSCarousel.tsx +260 -0
  30. package/src/components/chart/RLSChart.tsx +299 -0
  31. package/src/components/chat/RLSChat.tsx +27 -0
  32. package/src/components/chat-message/RLSChatMessage.tsx +27 -0
  33. package/src/components/chat-messages/RLSChatMessages.tsx +27 -0
  34. package/src/components/chat-palette/RLSChatPalette.tsx +27 -0
  35. package/src/components/chat-prompt/RLSChatPrompt.tsx +27 -0
  36. package/src/components/chat-prompt-submit/RLSChatPromptSubmit.tsx +27 -0
  37. package/src/components/chat-reasoning/RLSChatReasoning.tsx +27 -0
  38. package/src/components/chat-shimmer/RLSChatShimmer.tsx +27 -0
  39. package/src/components/chat-tool/RLSChatTool.tsx +27 -0
  40. package/src/components/checkbox/RLACheckbox.astro +1 -0
  41. package/src/components/checkbox/RLSCheckbox.tsx +1 -1
  42. package/src/components/checkbox/checkbox.theme.ts +2 -2
  43. package/src/components/chip/RLSChip.tsx +65 -0
  44. package/src/components/collapsible/RLSCollapsible.tsx +89 -0
  45. package/src/components/color-area/RLSColorArea.tsx +554 -0
  46. package/src/components/color-field/RLSColorField.tsx +236 -0
  47. package/src/components/color-picker/RLSColorPicker.tsx +26 -0
  48. package/src/components/color-slider/RLSColorSlider.tsx +373 -0
  49. package/src/components/command-palette/RLSCommandPalette.tsx +26 -0
  50. package/src/components/compare/RLSCompare.tsx +26 -0
  51. package/src/components/confirm/RLSConfirm.tsx +173 -0
  52. package/src/components/container/RLSContainer.tsx +24 -0
  53. package/src/components/context-menu/RLSContextMenu.tsx +26 -0
  54. package/src/components/copy-markdown/RLSCopyMarkdown.tsx +57 -0
  55. package/src/components/dashboard-group/RLSDashboardGroup.tsx +29 -0
  56. package/src/components/dashboard-navbar/RLSDashboardNavbar.tsx +116 -0
  57. package/src/components/dashboard-panel/RLSDashboardPanel.tsx +84 -0
  58. package/src/components/dashboard-resize-handle/RLSDashboardResizeHandle.tsx +35 -0
  59. package/src/components/dashboard-search/RLADashboardSearch.astro +1 -1
  60. package/src/components/dashboard-search/RLSDashboardSearch.tsx +125 -0
  61. package/src/components/dashboard-search-button/RLSDashboardSearchButton.tsx +82 -0
  62. package/src/components/dashboard-sidebar/RLSDashboardSidebar.tsx +184 -0
  63. package/src/components/dashboard-sidebar-collapse/RLSDashboardSidebarCollapse.tsx +46 -0
  64. package/src/components/dashboard-sidebar-toggle/RLSDashboardSidebarToggle.tsx +35 -0
  65. package/src/components/dashboard-toolbar/RLSDashboardToolbar.tsx +49 -0
  66. package/src/components/date/RLSDate.tsx +34 -0
  67. package/src/components/dock/RLSDock.tsx +26 -0
  68. package/src/components/drawer/RLSDrawer.tsx +177 -0
  69. package/src/components/dropdown-menu/RLADropdownMenu.astro +250 -241
  70. package/src/components/dropdown-menu/RLSDropdownMenu.tsx +183 -86
  71. package/src/components/editor/RLSEditor.tsx +26 -0
  72. package/src/components/empty/RLSEmpty.tsx +132 -0
  73. package/src/components/error/RLSError.tsx +26 -0
  74. package/src/components/field-group/RLSFieldGroup.tsx +46 -0
  75. package/src/components/fieldset/RLSFieldset.tsx +26 -0
  76. package/src/components/file-upload/RLSFileUpload.tsx +163 -0
  77. package/src/components/floating-action/RLSFloatingAction.tsx +26 -0
  78. package/src/components/footer/RLSFooter.tsx +56 -0
  79. package/src/components/form/RLSForm.tsx +34 -0
  80. package/src/components/gallery/RLSGallery.tsx +21 -0
  81. package/src/components/gamepad/RLSGamepad.tsx +269 -0
  82. package/src/components/grid/RLSGrid.tsx +29 -0
  83. package/src/components/head/RLAHead.astro +9 -0
  84. package/src/components/head/RLSHead.tsx +42 -0
  85. package/src/components/head/UIHead.astro +3 -0
  86. package/src/components/header/RLSHeader.tsx +87 -0
  87. package/src/components/hover-card/RLSHoverCard.tsx +72 -0
  88. package/src/components/icon/RLSIcon.tsx +3 -1
  89. package/src/components/image/RLAImage.astro +447 -138
  90. package/src/components/image/RLSImage.tsx +545 -0
  91. package/src/components/image/image.theme.ts +19 -3
  92. package/src/components/image/image.ts +31 -1
  93. package/src/components/input/RLAInput.astro +62 -51
  94. package/src/components/input/RLSInput.tsx +1 -1
  95. package/src/components/input-date/RLSInputDate.tsx +92 -0
  96. package/src/components/input-menu/RLSInputMenu.tsx +199 -0
  97. package/src/components/input-number/RLSInputNumber.tsx +184 -0
  98. package/src/components/input-pin/RLSInputPin.tsx +185 -0
  99. package/src/components/input-rating/RLSInputRating.tsx +198 -0
  100. package/src/components/input-rating/input-rating.ts +4 -0
  101. package/src/components/input-tags/RLSInputTags.tsx +94 -0
  102. package/src/components/input-time/RLSInputTime.tsx +92 -0
  103. package/src/components/kbd/RLSKbd.tsx +37 -0
  104. package/src/components/keyboard/RLSKeyboard.tsx +476 -0
  105. package/src/components/knob/RLSKnob.tsx +27 -0
  106. package/src/components/label/RLSLabel.tsx +27 -0
  107. package/src/components/layout-grid/RLSLayoutGrid.tsx +402 -0
  108. package/src/components/layout-grid/layout-grid.ts +25 -1
  109. package/src/components/link/RLSLink.tsx +2 -1
  110. package/src/components/link/link.ts +21 -12
  111. package/src/components/link-group/RLSLinkGroup.tsx +76 -0
  112. package/src/components/listbox/RLSListbox.tsx +27 -0
  113. package/src/components/locale-selector/RLALocaleSelector.astro +2 -1
  114. package/src/components/locale-selector/RLSLocaleSelector.tsx +178 -0
  115. package/src/components/logo/RLALogo.astro +69 -4
  116. package/src/components/logo/RLSLogo.tsx +129 -0
  117. package/src/components/main/RLSMain.tsx +40 -0
  118. package/src/components/marquee/RLSMarquee.tsx +80 -0
  119. package/src/components/meter/RLSMeter.tsx +27 -0
  120. package/src/components/modal/RLAModal.astro +1 -1
  121. package/src/components/modal/RLSModal.tsx +109 -72
  122. package/src/components/navigation-menu/RLSNavigationMenu.tsx +228 -0
  123. package/src/components/navigation-menu/navigation-menu.ts +2 -2
  124. package/src/components/package-managers/RLSPackageManagers.tsx +204 -0
  125. package/src/components/page/RLSPage.tsx +21 -0
  126. package/src/components/page-aside/RLSPageAside.tsx +21 -0
  127. package/src/components/page-body/RLSPageBody.tsx +21 -0
  128. package/src/components/page-header/RLSPageHeader.tsx +21 -0
  129. package/src/components/page-section/RLSPageSection.tsx +201 -0
  130. package/src/components/pagination/RLSPagination.tsx +114 -0
  131. package/src/components/password/RLSPassword.tsx +27 -0
  132. package/src/components/phone-mockup/RLSPhoneMockup.tsx +27 -0
  133. package/src/components/placeholder/RLSPlaceholder.tsx +47 -0
  134. package/src/components/popover/RLSPopover.tsx +124 -0
  135. package/src/components/post/RLSPost.tsx +134 -0
  136. package/src/components/pricing-plan/RLSPricingPlan.tsx +26 -0
  137. package/src/components/pricing-table/RLSPricingTable.tsx +26 -0
  138. package/src/components/progress/RLSProgress.tsx +53 -0
  139. package/src/components/prose/RLSProse.tsx +26 -0
  140. package/src/components/qr-code/RLSQrCode.tsx +26 -0
  141. package/src/components/quote/RLAQuote.astro +10 -6
  142. package/src/components/quote/RLSQuote.tsx +37 -0
  143. package/src/components/quote/quote.theme.ts +5 -2
  144. package/src/components/radio-group/RLSRadioGroup.tsx +37 -0
  145. package/src/components/rating/RLSRating.tsx +27 -0
  146. package/src/components/scroll-area/RLSScrollArea.tsx +210 -0
  147. package/src/components/scroll-to-top/RLAScrollToTop.astro +1 -0
  148. package/src/components/scroll-to-top/RLSScrollToTop.tsx +145 -0
  149. package/src/components/search/RLSSearch.tsx +237 -0
  150. package/src/components/select/RLASelect.astro +67 -4
  151. package/src/components/select/RLSSelect.tsx +107 -39
  152. package/src/components/select/RLVSelect.vue +50 -1
  153. package/src/components/select/select.theme.ts +30 -10
  154. package/src/components/select/select.ts +20 -1
  155. package/src/components/separator/RLSSeparator.tsx +92 -0
  156. package/src/components/share/RLSShare.tsx +66 -0
  157. package/src/components/shortcuts/RLSShortcuts.tsx +393 -0
  158. package/src/components/sidebar/RLSSidebar.tsx +271 -0
  159. package/src/components/skeleton/RLSSkeleton.tsx +21 -0
  160. package/src/components/slideover/RLASlideover.astro +1 -1
  161. package/src/components/slideover/RLSSlideover.tsx +159 -0
  162. package/src/components/slider/RLSSlider.tsx +112 -0
  163. package/src/components/speed-dial/RLSSpeedDial.tsx +27 -0
  164. package/src/components/spinner/RLSSpinner.tsx +22 -0
  165. package/src/components/splitter/RLSSplitter.tsx +178 -0
  166. package/src/components/spoiler/RLSSpoiler.tsx +26 -0
  167. package/src/components/stepper/RLSStepper.tsx +205 -0
  168. package/src/components/steps/RLSSteps.tsx +108 -0
  169. package/src/components/sticky-surface/RLSStickySurface.tsx +63 -0
  170. package/src/components/surround/RLSSurround.tsx +57 -0
  171. package/src/components/switch/RLSSwitch.tsx +56 -0
  172. package/src/components/table/RLATable.astro +1044 -579
  173. package/src/components/table/RLSTable.tsx +893 -0
  174. package/src/components/table/table.theme.ts +47 -16
  175. package/src/components/table/table.ts +104 -66
  176. package/src/components/table-of-contents/RLSTableOfContents.tsx +196 -0
  177. package/src/components/tabs/RLSTabs.tsx +298 -0
  178. package/src/components/textarea/RLATextarea.astro +1 -0
  179. package/src/components/textarea/RLSTextarea.tsx +3 -1
  180. package/src/components/theme-selector/RLAThemeSelector.astro +85 -56
  181. package/src/components/theme-selector/RLSThemeSelector.tsx +131 -0
  182. package/src/components/theme-selector/theme-selector.theme.ts +5 -7
  183. package/src/components/theme-selector/theme-selector.ts +11 -1
  184. package/src/components/timeline/RLSTimeline.tsx +171 -0
  185. package/src/components/toast/RLSToast.tsx +179 -0
  186. package/src/components/toaster/RLSToaster.tsx +82 -0
  187. package/src/components/tooltip/RLSTooltip.tsx +48 -0
  188. package/src/components/tour/RLSTour.tsx +26 -0
  189. package/src/components/tree/RLSTree.tsx +26 -0
  190. package/src/components/user/RLSUser.tsx +26 -0
  191. package/src/components/video/RLAVideo.astro +4 -1
  192. package/src/components/video/RLSVideo.tsx +158 -0
  193. package/src/components/watermark/RLSWatermark.tsx +27 -0
  194. package/src/components/window-mockup/RLSWindowMockup.tsx +27 -0
  195. package/src/preset.ts +73 -1
  196. package/src/resolver.ts +6 -1
  197. package/src/virtual.d.ts +6 -0
@@ -5,18 +5,64 @@ import RLAIcon from "../icon/RLAIcon.astro"
5
5
  import type { ImageProps } from "./image"
6
6
  import { imageTheme } from "./image.theme"
7
7
 
8
+ const localImages = import.meta.glob<{ default: ImageMetadata }>([
9
+ "/src/assets/**/*.{jpeg,jpg,png,tiff,webp,gif,svg,avif,JPEG,JPG,PNG,TIFF,WEBP,GIF,SVG,AVIF}",
10
+ "/src/images/**/*.{jpeg,jpg,png,tiff,webp,gif,svg,avif,JPEG,JPG,PNG,TIFF,WEBP,GIF,SVG,AVIF}",
11
+ ], { eager: true })
8
12
 
13
+ function resolveAsset(input?: string | ImageMetadata): ImageMetadata | string | undefined {
14
+ if (!input || typeof input !== "string") return input
15
+ if (
16
+ input.startsWith("http://") ||
17
+ input.startsWith("https://") ||
18
+ input.startsWith("//") ||
19
+ input.startsWith("data:")
20
+ ) {
21
+ return input
22
+ }
23
+
24
+ const clean = input.startsWith("/") ? input : `/${input}`
25
+ if (localImages[clean]) {
26
+ return localImages[clean].default ?? (localImages[clean] as unknown as ImageMetadata)
27
+ }
28
+
29
+ const withSrc = clean.startsWith("/src/") ? clean : `/src/${clean.replace(/^\//, "")}`
30
+ if (localImages[withSrc]) {
31
+ return localImages[withSrc].default ?? (localImages[withSrc] as unknown as ImageMetadata)
32
+ }
33
+
34
+ const stripped = input.replace(/^(@\/|~|\/src\/|\/)/, "")
35
+ for (const [key, mod] of Object.entries(localImages)) {
36
+ if (key.endsWith(stripped) || key.replace(/^\/src\//, "").endsWith(stripped)) {
37
+ return mod.default ?? (mod as unknown as ImageMetadata)
38
+ }
39
+ }
40
+
41
+ return input
42
+ }
9
43
 
10
44
  const {
11
- src,
12
- mobileSrc,
13
- desktopSrc,
45
+ src: rawSrc,
46
+ lightSrc: rawLightSrc,
47
+ darkSrc: rawDarkSrc,
48
+ mobileSrc: rawMobileSrc,
49
+ desktopSrc: rawDesktopSrc,
14
50
  breakpoint,
15
51
  formats,
52
+ widths,
53
+ sizes,
54
+ densities,
55
+ caption,
56
+ figure: propFigure,
57
+ lightbox: propLightbox,
58
+ interactive: propInteractive,
16
59
  alt = "",
17
60
  height,
18
61
  width,
19
62
  loading = "lazy",
63
+ decoding = "async",
64
+ fit,
65
+ theme,
20
66
  metadata: preMetadata,
21
67
  metaSource = true,
22
68
  metaFormat = true,
@@ -27,10 +73,22 @@ const {
27
73
  ...rest
28
74
  } = Astro.props as ImageProps
29
75
 
76
+ const isInteractive = propLightbox ?? propInteractive ?? true
77
+ const isFigure = propFigure ?? !!caption
78
+
79
+ const src = resolveAsset(rawSrc)
80
+ const lightSrc = resolveAsset(rawLightSrc)
81
+ const darkSrc = resolveAsset(rawDarkSrc)
82
+ const mobileSrc = resolveAsset(rawMobileSrc)
83
+ const desktopSrc = resolveAsset(rawDesktopSrc)
84
+
30
85
  const classes = imageTheme(Astro.props)
31
86
 
32
87
  const isResponsive = !!(mobileSrc && desktopSrc)
33
- const isFormatFallback = !isResponsive && !!(formats && formats.length > 0)
88
+ const effectiveLight = lightSrc || (!darkSrc ? src : (src || lightSrc))
89
+ const effectiveDark = darkSrc
90
+ const hasColorMode = !isResponsive && !effectiveLight ? false : !isResponsive && !!(effectiveLight && effectiveDark && effectiveLight !== effectiveDark)
91
+ const isFormatFallback = !isResponsive && !hasColorMode && !!(formats && formats.length > 0)
34
92
  const breakpointVal = breakpoint ?? 768
35
93
 
36
94
  let mobileImg = null
@@ -40,154 +98,339 @@ if (isResponsive) {
40
98
  mobileImg = await getImage({
41
99
  src: mobileSrc!,
42
100
  format: "webp",
101
+ ...(widths ? { widths: widths as any } : {}),
102
+ ...(densities ? { densities: densities as any } : {}),
43
103
  })
44
104
  desktopImg = await getImage({
45
105
  src: desktopSrc!,
46
106
  format: "webp",
107
+ ...(widths ? { widths: widths as any } : {}),
108
+ ...(densities ? { densities: densities as any } : {}),
47
109
  })
48
110
  }
49
111
 
50
- const activeSrc = isResponsive ? desktopSrc : src
112
+ const activeSrc = isResponsive ? desktopSrc : (effectiveLight || src)
51
113
  const isMetadata = typeof activeSrc !== "string" && activeSrc !== undefined
52
114
  const imgWidth = width ?? (isMetadata ? (activeSrc as any).width : undefined)
53
115
  const imgHeight = height ?? (isMetadata ? (activeSrc as any).height : undefined)
54
116
  const imgFormat = isMetadata ? (activeSrc as any).format : (preMetadata?.format ?? undefined)
55
117
  const rawName = activeSrc
56
- ? (typeof activeSrc === "string" ? activeSrc.split("/").pop() || "image" : (activeSrc as any).src.split("/").pop() || "image")
118
+ ? (typeof activeSrc === "string" ? activeSrc.split("/").pop() || "image" : ((activeSrc as any).src || "").split("/").pop() || "image")
57
119
  : "image"
58
120
  const fileName = rawName.split("?")[0]
59
121
  const fileExtension = imgFormat?.toUpperCase() || (fileName.includes(".") ? fileName.split(".").pop()?.toUpperCase() : "IMG")
60
122
  const initialSize = preMetadata?.size ?? undefined
123
+
124
+ const lightUrl = effectiveLight ? (typeof effectiveLight === "string" ? effectiveLight : (effectiveLight as ImageMetadata).src) : ""
125
+ const darkUrl = effectiveDark ? (typeof effectiveDark === "string" ? effectiveDark : (effectiveDark as ImageMetadata).src) : ""
126
+
127
+ const baseImageProps: Record<string, any> = {
128
+ alt,
129
+ loading,
130
+ decoding,
131
+ class: classes.triggerImage,
132
+ ...(widths ? { widths } : { width: imgWidth ?? 800, height: imgHeight ?? 400 }),
133
+ ...(sizes ? { sizes } : {}),
134
+ ...(densities ? { densities } : {}),
135
+ }
61
136
  ---
62
137
 
63
138
  <rla-image {...rest}>
64
- <div class={classes.root} data-slot="root">
65
- <slot name="trigger">
66
- <button
67
- type="button"
68
- class={classes.trigger}
69
- data-image-trigger
70
- >
71
- {isResponsive && mobileImg && desktopImg ? (
72
- <picture>
73
- <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
74
- <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
75
- <img src={desktopImg.src} alt={alt} loading={loading} class="w-full h-full object-cover rounded-lg" />
76
- </picture>
77
- ) : isFormatFallback ? (
78
- typeof src === "string" ? (
79
- <Picture src={src} formats={formats!} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class="w-full h-full object-cover rounded-lg" />
80
- ) : (
81
- <Picture src={src as ImageMetadata} formats={formats!} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class="w-full h-full object-cover rounded-lg" />
82
- )
83
- ) : (
84
- typeof src === "string" ? (
85
- <Image src={src} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class={classes.triggerImage} />
139
+ <div class={classes.root} data-slot="root" data-theme={theme} style={theme ? `color-scheme: ${theme};` : undefined}>
140
+ {isFigure ? (
141
+ <figure class={classes.figure} data-slot="figure">
142
+ <slot name="trigger">
143
+ {isInteractive ? (
144
+ <button
145
+ type="button"
146
+ class={classes.trigger}
147
+ data-image-trigger
148
+ >
149
+ {isResponsive && mobileImg && desktopImg ? (
150
+ <picture>
151
+ <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
152
+ <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
153
+ <img src={desktopImg.src} alt={alt} loading={loading} decoding={decoding} class={classes.triggerImage} />
154
+ </picture>
155
+ ) : hasColorMode ? (
156
+ <div
157
+ class="rla-image-color-mode w-full h-full relative overflow-hidden rounded-lg flex items-center justify-center"
158
+ style={`--rla-img-light: url('${lightUrl}'); --rla-img-dark: url('${darkUrl}');`}
159
+ >
160
+ <Image
161
+ {...(baseImageProps as any)}
162
+ src={effectiveLight as any}
163
+ class:list={[classes.triggerImage, "rla-img-light w-full h-full object-cover rounded-lg"]}
164
+ />
165
+ <Image
166
+ {...(baseImageProps as any)}
167
+ src={effectiveDark as any}
168
+ class:list={[classes.triggerImage, "rla-img-dark w-full h-full object-cover rounded-lg hidden"]}
169
+ />
170
+ </div>
171
+ ) : isFormatFallback ? (
172
+ <Picture
173
+ {...(baseImageProps as any)}
174
+ src={src as any}
175
+ formats={formats!}
176
+ />
177
+ ) : (
178
+ <Image
179
+ {...(baseImageProps as any)}
180
+ src={src as any}
181
+ />
182
+ )}
183
+ </button>
86
184
  ) : (
87
- <Image src={src as ImageMetadata} alt={alt} width={imgWidth ?? 800} height={imgHeight ?? 400} loading={loading} class={classes.triggerImage} />
88
- )
89
- )}
90
- </button>
91
- </slot>
92
-
93
- <dialog
94
- class={classes.dialog}
95
- data-image-dialog
96
- >
97
- <div class="flex flex-col bg-default text-highlighted max-h-[90vh]">
98
- <div class="flex-1 min-h-0 flex items-center justify-center p-4 overflow-hidden">
99
- {isResponsive && mobileImg && desktopImg ? (
100
- <picture class="max-w-full max-h-[65vh] flex items-center justify-center overflow-hidden">
101
- <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
102
- <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
103
- <img src={desktopImg.src} alt={alt} class={classes.image} data-modal-img />
104
- </picture>
185
+ <div class="overflow-hidden rounded-lg block">
186
+ {isResponsive && mobileImg && desktopImg ? (
187
+ <picture>
188
+ <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
189
+ <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
190
+ <img src={desktopImg.src} alt={alt} loading={loading} decoding={decoding} class={classes.triggerImage} />
191
+ </picture>
192
+ ) : hasColorMode ? (
193
+ <div
194
+ class="rla-image-color-mode w-full h-full relative overflow-hidden rounded-lg flex items-center justify-center"
195
+ style={`--rla-img-light: url('${lightUrl}'); --rla-img-dark: url('${darkUrl}');`}
196
+ >
197
+ <Image
198
+ {...(baseImageProps as any)}
199
+ src={effectiveLight as any}
200
+ class:list={[classes.triggerImage, "rla-img-light w-full h-full object-cover rounded-lg"]}
201
+ />
202
+ <Image
203
+ {...(baseImageProps as any)}
204
+ src={effectiveDark as any}
205
+ class:list={[classes.triggerImage, "rla-img-dark w-full h-full object-cover rounded-lg hidden"]}
206
+ />
207
+ </div>
208
+ ) : isFormatFallback ? (
209
+ <Picture
210
+ {...(baseImageProps as any)}
211
+ src={src as any}
212
+ formats={formats!}
213
+ />
214
+ ) : (
215
+ <Image
216
+ {...(baseImageProps as any)}
217
+ src={src as any}
218
+ />
219
+ )}
220
+ </div>
221
+ )}
222
+ </slot>
223
+ {caption && <figcaption class={classes.caption} data-slot="caption">{caption}</figcaption>}
224
+ <slot name="caption" />
225
+ </figure>
226
+ ) : (
227
+ <>
228
+ <slot name="trigger">
229
+ {isInteractive ? (
230
+ <button
231
+ type="button"
232
+ class={classes.trigger}
233
+ data-image-trigger
234
+ >
235
+ {isResponsive && mobileImg && desktopImg ? (
236
+ <picture>
237
+ <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
238
+ <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
239
+ <img src={desktopImg.src} alt={alt} loading={loading} decoding={decoding} class={classes.triggerImage} />
240
+ </picture>
241
+ ) : hasColorMode ? (
242
+ <div
243
+ class="rla-image-color-mode w-full h-full relative overflow-hidden rounded-lg flex items-center justify-center"
244
+ style={`--rla-img-light: url('${lightUrl}'); --rla-img-dark: url('${darkUrl}');`}
245
+ >
246
+ <Image
247
+ {...(baseImageProps as any)}
248
+ src={effectiveLight as any}
249
+ class:list={[classes.triggerImage, "rla-img-light w-full h-full object-cover rounded-lg"]}
250
+ />
251
+ <Image
252
+ {...(baseImageProps as any)}
253
+ src={effectiveDark as any}
254
+ class:list={[classes.triggerImage, "rla-img-dark w-full h-full object-cover rounded-lg hidden"]}
255
+ />
256
+ </div>
257
+ ) : isFormatFallback ? (
258
+ <Picture
259
+ {...(baseImageProps as any)}
260
+ src={src as any}
261
+ formats={formats!}
262
+ />
263
+ ) : (
264
+ <Image
265
+ {...(baseImageProps as any)}
266
+ src={src as any}
267
+ />
268
+ )}
269
+ </button>
105
270
  ) : (
106
- <img
107
- src={typeof src === "string" ? src : src!.src}
108
- alt={alt}
109
- class={classes.image}
110
- data-modal-img
111
- />
271
+ <div class="overflow-hidden rounded-lg block">
272
+ {isResponsive && mobileImg && desktopImg ? (
273
+ <picture>
274
+ <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
275
+ <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
276
+ <img src={desktopImg.src} alt={alt} loading={loading} decoding={decoding} class={classes.triggerImage} />
277
+ </picture>
278
+ ) : hasColorMode ? (
279
+ <div
280
+ class="rla-image-color-mode w-full h-full relative overflow-hidden rounded-lg flex items-center justify-center"
281
+ style={`--rla-img-light: url('${lightUrl}'); --rla-img-dark: url('${darkUrl}');`}
282
+ >
283
+ <Image
284
+ {...(baseImageProps as any)}
285
+ src={effectiveLight as any}
286
+ class:list={[classes.triggerImage, "rla-img-light w-full h-full object-cover rounded-lg"]}
287
+ />
288
+ <Image
289
+ {...(baseImageProps as any)}
290
+ src={effectiveDark as any}
291
+ class:list={[classes.triggerImage, "rla-img-dark w-full h-full object-cover rounded-lg hidden"]}
292
+ />
293
+ </div>
294
+ ) : isFormatFallback ? (
295
+ <Picture
296
+ {...(baseImageProps as any)}
297
+ src={src as any}
298
+ formats={formats!}
299
+ />
300
+ ) : (
301
+ <Image
302
+ {...(baseImageProps as any)}
303
+ src={src as any}
304
+ />
305
+ )}
306
+ </div>
112
307
  )}
113
- </div>
308
+ </slot>
309
+ {caption && <p class={classes.caption} data-slot="caption">{caption}</p>}
310
+ <slot name="caption" />
311
+ </>
312
+ )}
114
313
 
115
- <hr class="border-default mx-4 mb-3 shrink-0" />
314
+ {isInteractive && (
315
+ <dialog
316
+ class={classes.dialog}
317
+ data-image-dialog
318
+ >
319
+ <div class="flex flex-col bg-default text-highlighted max-h-[90vh]">
320
+ <div class="flex-1 min-h-0 flex items-center justify-center p-4 overflow-hidden">
321
+ {isResponsive && mobileImg && desktopImg ? (
322
+ <picture class="max-w-full max-h-[65vh] flex items-center justify-center overflow-hidden">
323
+ <source media={`(max-width: ${breakpointVal - 1}px)`} srcset={mobileImg.src} />
324
+ <source media={`(min-width: ${breakpointVal}px)`} srcset={desktopImg.src} />
325
+ <img src={desktopImg.src} alt={alt} class={classes.image} data-modal-img />
326
+ </picture>
327
+ ) : hasColorMode ? (
328
+ <div
329
+ class="rla-image-color-mode max-w-full max-h-[65vh] relative flex items-center justify-center overflow-hidden"
330
+ style={`--rla-img-light: url('${lightUrl}'); --rla-img-dark: url('${darkUrl}');`}
331
+ >
332
+ <img
333
+ src={lightUrl}
334
+ alt={alt}
335
+ class:list={[classes.image, "rla-img-light"]}
336
+ data-modal-img
337
+ data-light-src={lightUrl}
338
+ data-dark-src={darkUrl}
339
+ />
340
+ <img
341
+ src={darkUrl}
342
+ alt={alt}
343
+ class:list={[classes.image, "rla-img-dark hidden"]}
344
+ data-modal-img-dark
345
+ />
346
+ </div>
347
+ ) : (
348
+ <img
349
+ src={typeof src === "string" ? src : src!.src}
350
+ alt={alt}
351
+ class={classes.image}
352
+ data-modal-img
353
+ />
354
+ )}
355
+ </div>
356
+
357
+ <hr class="border-default mx-4 mb-3 shrink-0" />
358
+
359
+ <div class={classes.fileInfo}>
360
+ <div class="flex flex-col gap-xs text-sm">
361
+ {
362
+ metaSource && (
363
+ <div class="flex flex-row gap-xs items-center">
364
+ <RLAIcon name="image" class="size-4 shrink-0 text-muted" />
365
+ <span class="text-muted">Source:</span>
366
+ <span class="text-dimmed truncate max-w-[40vw]">{fileName}</span>
367
+ </div>
368
+ )
369
+ }
370
+ {
371
+ metaFormat && (
372
+ <div class="flex flex-row gap-xs items-center">
373
+ <RLAIcon name="fileQuestion" class="size-4 shrink-0 text-muted" />
374
+ <span class="text-muted">Format:</span>
375
+ <span class="text-dimmed" data-metadata-format>{fileExtension || "IMG"}</span>
376
+ </div>
377
+ )
378
+ }
379
+ {
380
+ metaDimensions && (
381
+ <div class="flex flex-row gap-xs items-center">
382
+ <RLAIcon name="imageUpscale" class="size-4 shrink-0 text-muted" />
383
+ <span class="text-muted">Dimensions:</span>
384
+ <span class="text-dimmed">{(imgWidth || "?")} x {(imgHeight || "?")}</span>
385
+ </div>
386
+ )
387
+ }
388
+ {
389
+ metaSize && (
390
+ <div class="flex flex-row gap-xs items-center">
391
+ <RLAIcon name="hardDrive" class="size-4 shrink-0 text-muted" />
392
+ <span class="text-muted">Size:</span>
393
+ <span class="text-dimmed" data-metadata-size>{initialSize !== undefined ? `${initialSize} bytes` : "fetching..."}</span>
394
+ </div>
395
+ )
396
+ }
397
+ {
398
+ metaAlt && (
399
+ <div class="flex flex-row gap-xs items-center">
400
+ <RLAIcon name="alignLeft" class="size-4 shrink-0 text-muted" />
401
+ <span class="text-muted">Alt:</span>
402
+ <span class="text-dimmed truncate max-w-[30vw]" class:list={{ "italic opacity-50": !alt }}>{alt || "none"}</span>
403
+ </div>
404
+ )
405
+ }
406
+ </div>
116
407
 
117
- <div class={classes.fileInfo}>
118
- <div class="flex flex-col gap-xs text-sm">
119
- {
120
- metaSource && (
121
- <div class="flex flex-row gap-xs items-center">
122
- <RLAIcon name="image" class="size-4 shrink-0 text-muted" />
123
- <span class="text-muted">Source:</span>
124
- <span class="text-dimmed truncate max-w-[40vw]">{fileName}</span>
125
- </div>
126
- )
127
- }
128
- {
129
- metaFormat && (
130
- <div class="flex flex-row gap-xs items-center">
131
- <RLAIcon name="fileQuestion" class="size-4 shrink-0 text-muted" />
132
- <span class="text-muted">Format:</span>
133
- <span class="text-dimmed" data-metadata-format>{fileExtension || "IMG"}</span>
134
- </div>
135
- )
136
- }
137
- {
138
- metaDimensions && (
139
- <div class="flex flex-row gap-xs items-center">
140
- <RLAIcon name="imageUpscale" class="size-4 shrink-0 text-muted" />
141
- <span class="text-muted">Dimensions:</span>
142
- <span class="text-dimmed">{(imgWidth || "?")} x {(imgHeight || "?")}</span>
143
- </div>
144
- )
145
- }
146
- {
147
- metaSize && (
148
- <div class="flex flex-row gap-xs items-center">
149
- <RLAIcon name="hardDrive" class="size-4 shrink-0 text-muted" />
150
- <span class="text-muted">Size:</span>
151
- <span class="text-dimmed" data-metadata-size>{initialSize !== undefined ? `${initialSize} bytes` : "fetching..."}</span>
152
- </div>
153
- )
154
- }
155
408
  {
156
- metaAlt && (
157
- <div class="flex flex-row gap-xs items-center">
158
- <RLAIcon name="alignLeft" class="size-4 shrink-0 text-muted" />
159
- <span class="text-muted">Alt:</span>
160
- <span class="text-dimmed truncate max-w-[30vw]" class:list={{ "italic opacity-50": !alt }}>{alt || "none"}</span>
161
- </div>
409
+ metaDownload && (
410
+ <button
411
+ type="button"
412
+ class={classes.download}
413
+ data-image-download
414
+ >
415
+ <RLAIcon name="download" class="size-4 shrink-0" />
416
+ Download Original
417
+ </button>
162
418
  )
163
419
  }
164
420
  </div>
165
421
 
166
- {
167
- metaDownload && (
168
- <button
169
- type="button"
170
- class={classes.download}
171
- data-image-download
172
- >
173
- <RLAIcon name="download" class="size-4 shrink-0" />
174
- Download Original
175
- </button>
176
- )
177
- }
422
+ <form method="dialog" class="absolute top-2 right-2">
423
+ <button
424
+ type="submit"
425
+ class={classes.closeButton}
426
+ aria-label="Close"
427
+ >
428
+ <RLAIcon name="close" class="size-4" />
429
+ </button>
430
+ </form>
178
431
  </div>
179
-
180
- <form method="dialog" class="absolute top-2 right-2">
181
- <button
182
- type="submit"
183
- class={classes.closeButton}
184
- aria-label="Close"
185
- >
186
- <RLAIcon name="close" class="size-4" />
187
- </button>
188
- </form>
189
- </div>
190
- </dialog>
432
+ </dialog>
433
+ )}
191
434
  </div>
192
435
  </rla-image>
193
436
 
@@ -195,6 +438,63 @@ const initialSize = preMetadata?.size ?? undefined
195
438
  rla-image {
196
439
  display: contents;
197
440
  }
441
+
442
+ :global(:root[data-theme="dark"]) .rla-img-light,
443
+ :global([data-theme="dark"]) .rla-img-light,
444
+ :global([data-theme="dark"]) .rla-image-color-mode .rla-img-light,
445
+ :global(.rla-image-color-mode[data-theme="dark"]) .rla-img-light {
446
+ display: none !important;
447
+ }
448
+
449
+ :global(:root[data-theme="dark"]) .rla-img-dark,
450
+ :global([data-theme="dark"]) .rla-img-dark,
451
+ :global([data-theme="dark"]) .rla-image-color-mode .rla-img-dark,
452
+ :global(.rla-image-color-mode[data-theme="dark"]) .rla-img-dark {
453
+ display: block !important;
454
+ }
455
+
456
+ :global(:root[data-theme="light"]) .rla-img-light,
457
+ :global([data-theme="light"]) .rla-img-light,
458
+ :global([data-theme="light"]) .rla-image-color-mode .rla-img-light,
459
+ :global(.rla-image-color-mode[data-theme="light"]) .rla-img-light {
460
+ display: block !important;
461
+ }
462
+
463
+ :global(:root[data-theme="light"]) .rla-img-dark,
464
+ :global([data-theme="light"]) .rla-img-dark,
465
+ :global([data-theme="light"]) .rla-image-color-mode .rla-img-dark,
466
+ :global(.rla-image-color-mode[data-theme="light"]) .rla-img-dark {
467
+ display: none !important;
468
+ }
469
+
470
+ @media (prefers-color-scheme: dark) {
471
+ :global(:root:not([data-theme="light"])) .rla-img-light {
472
+ display: none !important;
473
+ }
474
+ :global(:root:not([data-theme="light"])) .rla-img-dark {
475
+ display: block !important;
476
+ }
477
+ :global(:root:not([data-theme="light"])) :global([data-theme="light"]) .rla-img-light {
478
+ display: block !important;
479
+ }
480
+ :global(:root:not([data-theme="light"])) :global([data-theme="light"]) .rla-img-dark {
481
+ display: none !important;
482
+ }
483
+ }
484
+
485
+ @supports (background-image: light-dark(none, none)) {
486
+ .rla-image-color-mode {
487
+ background-image: light-dark(var(--rla-img-light), var(--rla-img-dark));
488
+ background-size: cover;
489
+ background-position: center;
490
+ background-repeat: no-repeat;
491
+ }
492
+ .rla-image-color-mode > .rla-img-light,
493
+ .rla-image-color-mode > .rla-img-dark {
494
+ opacity: 0 !important;
495
+ visibility: hidden !important;
496
+ }
497
+ }
198
498
  </style>
199
499
 
200
500
  <script>
@@ -210,22 +510,32 @@ const initialSize = preMetadata?.size ?? undefined
210
510
  const formatEl = this.querySelector("[data-metadata-format]");
211
511
  let fetched = false;
212
512
 
513
+ const getActiveImgSrc = () => {
514
+ const rootTheme = document.documentElement.getAttribute('data-theme');
515
+ const isDark = rootTheme === 'dark' || (window.matchMedia('(prefers-color-scheme: dark)').matches && rootTheme !== 'light');
516
+ const imgEl = dialog.querySelector("[data-modal-img]");
517
+ if (imgEl) {
518
+ if (isDark && imgEl.getAttribute("data-dark-src")) {
519
+ return imgEl.getAttribute("data-dark-src");
520
+ }
521
+ return imgEl.getAttribute("src");
522
+ }
523
+ return null;
524
+ };
525
+
213
526
  trigger.addEventListener("click", () => {
214
527
  if (!fetched) {
215
528
  fetched = true;
216
- const imgEl = dialog.querySelector("[data-modal-img]");
217
- if (imgEl) {
218
- const src = imgEl.getAttribute("src");
219
- if (src) {
220
- fetch(src).then(r => r.blob()).then(blob => {
221
- if (sizeEl) sizeEl.textContent = (blob.size / 1024).toFixed(2) + " KB";
222
- if (formatEl && blob.type) {
223
- const mime = blob.type.split("/").pop()?.toUpperCase();
224
- if (mime && mime !== "OCTET-STREAM") formatEl.textContent = mime;
225
- }
226
- return;
227
- }).catch(() => { if (sizeEl) sizeEl.textContent = "unavailable"; });
228
- }
529
+ const src = getActiveImgSrc();
530
+ if (src) {
531
+ fetch(src).then(r => r.blob()).then(blob => {
532
+ if (sizeEl) sizeEl.textContent = (blob.size / 1024).toFixed(2) + " KB";
533
+ if (formatEl && blob.type) {
534
+ const mime = blob.type.split("/").pop()?.toUpperCase();
535
+ if (mime && mime !== "OCTET-STREAM") formatEl.textContent = mime;
536
+ }
537
+ return;
538
+ }).catch(() => { if (sizeEl) sizeEl.textContent = "unavailable"; });
229
539
  }
230
540
  }
231
541
  dialog.showModal();
@@ -238,8 +548,7 @@ const initialSize = preMetadata?.size ?? undefined
238
548
  const downloadBtn = dialog.querySelector("[data-image-download]");
239
549
  if (downloadBtn) {
240
550
  downloadBtn.addEventListener("click", async () => {
241
- const img = dialog.querySelector("img");
242
- const imgSrc = img?.getAttribute("src");
551
+ const imgSrc = getActiveImgSrc();
243
552
  if (!imgSrc) return;
244
553
  try {
245
554
  const response = await fetch(imgSrc);
@@ -249,7 +558,7 @@ const initialSize = preMetadata?.size ?? undefined
249
558
  link.href = url;
250
559
  const triggerImg = trigger.querySelector("img");
251
560
  const srcAttr = triggerImg ? triggerImg.getAttribute("src") || "" : "";
252
- link.download = (srcAttr.split("/").pop() || "").split("?")[0] || `image-${Date.now()}`;
561
+ link.download = (srcAttr.split("/").pop() || "").split("?")[0] || (imgSrc.split("/").pop() || "").split("?")[0] || `image-${Date.now()}`;
253
562
  document.body.appendChild(link);
254
563
  link.click();
255
564
  document.body.removeChild(link);