@rimelight/ui 0.0.34 → 0.0.36

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 (185) hide show
  1. package/README.md +1 -0
  2. package/package.json +19 -24
  3. package/src/components/astro/RLAAccordion.astro +94 -18
  4. package/src/components/astro/RLAAvatar.astro +135 -68
  5. package/src/components/astro/RLABadge.astro +4 -2
  6. package/src/components/astro/RLABanner.astro +138 -23
  7. package/src/components/astro/RLABreadcrumb.astro +13 -0
  8. package/src/components/astro/RLAButton.astro +8 -4
  9. package/src/components/astro/RLACallout.astro +112 -0
  10. package/src/components/astro/RLACard.astro +6 -5
  11. package/src/components/astro/RLACarousel.astro +104 -75
  12. package/src/components/astro/RLAChart.astro +4 -1
  13. package/src/components/astro/RLAChip.astro +11 -5
  14. package/src/components/astro/RLACollapsible.astro +4 -1
  15. package/src/components/astro/RLAColorArea.astro +432 -0
  16. package/src/components/astro/RLAColorField.astro +300 -0
  17. package/src/components/astro/RLAColorSlider.astro +413 -0
  18. package/src/components/astro/RLAConfirm.astro +13 -11
  19. package/src/components/astro/RLACopyMarkdown.astro +78 -0
  20. package/src/components/astro/RLADate.astro +30 -0
  21. package/src/components/astro/RLADrawer.astro +50 -3
  22. package/src/components/astro/RLADropdownMenu.astro +78 -51
  23. package/src/components/astro/RLAFieldGroup.astro +4 -1
  24. package/src/components/astro/RLAFileUpload.astro +114 -100
  25. package/src/components/astro/RLAFooter.astro +2 -1
  26. package/src/components/astro/RLAFormField.astro +10 -6
  27. package/src/components/astro/RLAGrid.astro +31 -39
  28. package/src/components/astro/RLAHead.astro +139 -39
  29. package/src/components/astro/RLAHeader.astro +2 -1
  30. package/src/components/astro/RLAImage.astro +274 -251
  31. package/src/components/astro/RLAInput.astro +14 -9
  32. package/src/components/astro/RLAInputMenu.astro +119 -83
  33. package/src/components/astro/RLAKbd.astro +5 -5
  34. package/src/components/astro/RLALayoutGrid.astro +395 -0
  35. package/src/components/astro/RLALink.astro +23 -1
  36. package/src/components/astro/RLALinkGroup.astro +99 -99
  37. package/src/components/astro/RLALocaleSelector.astro +34 -41
  38. package/src/components/astro/RLALogo.astro +117 -90
  39. package/src/components/astro/RLAModal.astro +48 -18
  40. package/src/components/astro/RLANavigationMenu.astro +381 -326
  41. package/src/components/astro/{RLADocsPackageManagers.astro → RLAPackageManagers.astro} +81 -87
  42. package/src/components/astro/RLAPageSection.astro +4 -2
  43. package/src/components/astro/RLAPopover.astro +4 -3
  44. package/src/components/astro/RLAPost.astro +11 -2
  45. package/src/components/astro/RLAScrollArea.astro +4 -1
  46. package/src/components/astro/RLAScrollToTop.astro +200 -172
  47. package/src/components/astro/RLASearch.astro +282 -0
  48. package/src/components/astro/RLASelect.astro +78 -55
  49. package/src/components/astro/RLASeparator.astro +11 -6
  50. package/src/components/astro/RLAShare.astro +60 -0
  51. package/src/components/astro/RLAShortcuts.astro +429 -0
  52. package/src/components/astro/RLASidebar.astro +158 -212
  53. package/src/components/astro/RLASlideover.astro +47 -17
  54. package/src/components/astro/RLASplitter.astro +194 -141
  55. package/src/components/astro/RLASteps.astro +118 -71
  56. package/src/components/astro/RLASurround.astro +39 -0
  57. package/src/components/astro/RLATable.astro +4 -1
  58. package/src/components/astro/RLATableOfContents.astro +109 -23
  59. package/src/components/astro/RLATabs.astro +147 -133
  60. package/src/components/astro/RLATextarea.astro +4 -1
  61. package/src/components/astro/RLAThemeSelector.astro +105 -76
  62. package/src/components/astro/RLAToast.astro +35 -21
  63. package/src/components/vue/RLVBanner.vue +230 -0
  64. package/src/components/vue/RLVButton.vue +3 -2
  65. package/src/components/vue/RLVCheckbox.vue +4 -2
  66. package/src/components/vue/RLVFooter.vue +3 -2
  67. package/src/components/vue/RLVHeader.vue +3 -2
  68. package/src/components/vue/RLVIcon.vue +4 -2
  69. package/src/components/vue/RLVInput.vue +5 -4
  70. package/src/components/vue/RLVLogo.vue +25 -3
  71. package/src/components/vue/RLVPlaceholder.vue +3 -2
  72. package/src/components/vue/RLVPost.vue +8 -2
  73. package/src/components/vue/RLVProgress.vue +60 -0
  74. package/src/components/vue/RLVScrollToTop.vue +65 -52
  75. package/src/components/vue/RLVSection.vue +14 -13
  76. package/src/components/vue/RLVToast.vue +17 -4
  77. package/src/composables/index.ts +1 -0
  78. package/src/composables/useHeaderStore.ts +1 -0
  79. package/src/composables/useShortcuts.ts +11 -0
  80. package/src/config/security.ts +43 -36
  81. package/src/config/site.config.ts +1 -0
  82. package/src/docs/components/ApiChangelog.astro +147 -0
  83. package/src/{plugins/starlightDocsApi → docs}/components/ApiEmits.astro +4 -4
  84. package/src/{plugins/starlightDocsApi → docs}/components/ApiProps.astro +4 -4
  85. package/src/{plugins/starlightDocsApi → docs}/components/ApiSlots.astro +4 -4
  86. package/src/docs/components/ApiTheme.astro +100 -0
  87. package/src/docs/extractAll.ts +94 -0
  88. package/src/docs/extractors/extractAstro.ts +69 -0
  89. package/src/docs/extractors/extractTheme.ts +56 -0
  90. package/src/{plugins/starlightDocsApi/extract-vue.ts → docs/extractors/extractVue.ts} +164 -15
  91. package/src/{plugins/starlightDocsApi → docs}/utils.ts +1 -2
  92. package/src/env.d.ts +0 -12
  93. package/src/integrations/ui.ts +35 -19
  94. package/src/middleware/security.ts +75 -19
  95. package/src/presets/index.ts +118 -293
  96. package/src/styles/index.css +0 -2
  97. package/src/themes/accordion.theme.ts +29 -3
  98. package/src/themes/aspect-ratio.theme.ts +1 -1
  99. package/src/themes/avatar-group.theme.ts +1 -1
  100. package/src/themes/avatar.theme.ts +2 -2
  101. package/src/themes/badge.theme.ts +2 -3
  102. package/src/themes/banner.theme.ts +40 -4
  103. package/src/themes/breadcrumb.theme.ts +1 -1
  104. package/src/themes/button.theme.ts +21 -22
  105. package/src/themes/{alert.theme.ts → callout.theme.ts} +12 -39
  106. package/src/themes/card.theme.ts +17 -9
  107. package/src/themes/carousel.theme.ts +1 -1
  108. package/src/themes/checkbox.theme.ts +1 -2
  109. package/src/themes/chip.theme.ts +2 -3
  110. package/src/themes/color-area.theme.ts +33 -0
  111. package/src/themes/color-field.theme.ts +32 -0
  112. package/src/themes/color-slider.theme.ts +34 -0
  113. package/src/themes/confirm.theme.ts +1 -1
  114. package/src/themes/container.theme.ts +1 -1
  115. package/src/themes/drawer.theme.ts +1 -1
  116. package/src/themes/footer.theme.ts +2 -2
  117. package/src/themes/grid.theme.ts +38 -0
  118. package/src/themes/header.theme.ts +4 -3
  119. package/src/themes/hover-card.theme.ts +1 -1
  120. package/src/themes/image.theme.ts +11 -1
  121. package/src/themes/input.theme.ts +7 -1
  122. package/src/themes/kbd.theme.ts +2 -3
  123. package/src/themes/label.theme.ts +1 -1
  124. package/src/themes/layout-grid.theme.ts +16 -0
  125. package/src/themes/link-group.theme.ts +0 -4
  126. package/src/themes/marquee.theme.ts +1 -1
  127. package/src/themes/modal.theme.ts +1 -1
  128. package/src/themes/navigation-menu.theme.ts +2 -2
  129. package/src/themes/page-section.theme.ts +4 -2
  130. package/src/themes/pagination.theme.ts +1 -1
  131. package/src/themes/pin-input.theme.ts +1 -1
  132. package/src/themes/popover.theme.ts +1 -1
  133. package/src/themes/post.theme.ts +23 -3
  134. package/src/themes/progress.theme.ts +1 -2
  135. package/src/themes/scroll-to-top.theme.ts +2 -3
  136. package/src/themes/search.theme.ts +64 -0
  137. package/src/themes/separator.theme.ts +1 -1
  138. package/src/themes/slideover.theme.ts +1 -1
  139. package/src/themes/slider.theme.ts +1 -2
  140. package/src/themes/spinner.theme.ts +1 -2
  141. package/src/themes/{megamenu.theme.ts → steps.theme.ts} +3 -3
  142. package/src/themes/sticky-surface.theme.ts +1 -1
  143. package/src/themes/switch.theme.ts +1 -2
  144. package/src/themes/table-of-contents.theme.ts +21 -2
  145. package/src/themes/timeline.theme.ts +7 -2
  146. package/src/themes/toast.theme.ts +1 -1
  147. package/src/types/components/accordion.ts +35 -2
  148. package/src/types/components/avatar.ts +16 -0
  149. package/src/types/components/banner.ts +42 -0
  150. package/src/types/components/button.ts +3 -4
  151. package/src/types/components/{alert.ts → callout.ts} +6 -6
  152. package/src/types/components/color-area.ts +19 -0
  153. package/src/types/components/color-field.ts +21 -0
  154. package/src/types/components/color-slider.ts +20 -0
  155. package/src/types/components/grid.ts +23 -0
  156. package/src/types/components/input.ts +4 -0
  157. package/src/types/components/layout-grid.ts +145 -0
  158. package/src/types/components/link-group.ts +4 -2
  159. package/src/types/components/link.ts +15 -0
  160. package/src/types/components/logo.ts +3 -3
  161. package/src/types/components/post.ts +12 -0
  162. package/src/types/components/search.ts +29 -0
  163. package/src/types/components/sidebar.ts +2 -6
  164. package/src/types/components/{megamenu.ts → steps.ts} +7 -1
  165. package/src/types/components/table-of-contents.ts +22 -0
  166. package/src/types/components/toast.ts +4 -0
  167. package/src/types/docs.ts +40 -0
  168. package/src/types/index.ts +151 -1
  169. package/src/types/theme.ts +6 -6
  170. package/src/utils/color.ts +165 -0
  171. package/src/utils/docs.ts +260 -110
  172. package/src/utils/headerStack.ts +24 -4
  173. package/src/utils/index.ts +2 -0
  174. package/src/utils/shortcuts.ts +322 -0
  175. package/src/components/TableOfContents.astro +0 -107
  176. package/src/components/astro/RLAAlert.astro +0 -93
  177. package/src/components/astro/RLAChangelog.astro +0 -23
  178. package/src/components/astro/RLAMegamenu.astro +0 -23
  179. package/src/plugins/index.ts +0 -2
  180. package/src/plugins/starlightDocsApi/components/ApiTheme.astro +0 -27
  181. package/src/plugins/starlightDocsApi/index.ts +0 -272
  182. package/src/styles/prism.css +0 -42
  183. package/src/themes/changelog.theme.ts +0 -13
  184. package/src/types/components/index.ts +0 -134
  185. /package/src/{plugins/starlightDocsApi → docs}/types.ts +0 -0
@@ -4,8 +4,9 @@ import { useUi, resolveClasses } from "../../utils"
4
4
  import type { ScrollToTopProps } from "../../types/components/scroll-to-top"
5
5
  import scrollToTopTheme from "../../themes/scroll-to-top.theme"
6
6
  import { useScrollToTop } from "../../composables/useScrollToTop"
7
- import { computed } from "vue"
7
+ import { computed, onMounted, onUnmounted, watch, useAttrs } from "vue"
8
8
 
9
+ const attrs = useAttrs()
9
10
  const scrollToTop = scv(scrollToTopTheme)
10
11
 
11
12
  const {
@@ -28,49 +29,49 @@ const {
28
29
  threshold
29
30
  })
30
31
 
31
- const circumference = 2 * Math.PI * 45
32
- const percentPx = circumference / 100
32
+ const instanceId = `rla-vue-${Math.random().toString(36).slice(2, 8)}`
33
+ const gaugeBorderWidth = progressWidth * 0.5
33
34
 
34
- const currentPercent = computed(() => scrollPercentage.value)
35
- const percentageInPx = computed(() => `${percentPx}px`)
36
- const durationInSeconds = computed(() => `${duration}s`)
35
+ let styleEl: HTMLStyleElement | null = null
36
+
37
+ function updateCssVars() {
38
+ if (!styleEl) return
39
+ styleEl.textContent = `
40
+ [data-rla-instance="${instanceId}"] {
41
+ --gauge-track-border-color: var(--color-${color}-900);
42
+ --gauge-track-bg: var(--color-${color}-950);
43
+ --gauge-stroke-width: ${gaugeBorderWidth}px;
44
+ --gauge-color: var(--color-${color}-500);
45
+ --gauge-progress: ${scrollPercentage.value};
46
+ }
47
+ `
48
+ }
49
+
50
+ watch(scrollPercentage, updateCssVars)
51
+
52
+ onMounted(() => {
53
+ const nonce = document.querySelector('meta[name="csp-nonce"]')?.getAttribute("content") ?? ""
54
+ styleEl = document.createElement("style")
55
+ if (nonce) styleEl.setAttribute("nonce", nonce)
56
+ document.head.appendChild(styleEl)
57
+ updateCssVars()
58
+ })
59
+
60
+ onUnmounted(() => {
61
+ if (styleEl) styleEl.remove()
62
+ })
37
63
  </script>
38
64
 
39
65
  <template>
40
66
  <div
67
+ :data-rla-instance="instanceId"
41
68
  :class="['rla-scroll-to-top fixed bottom-6 right-6 z-50', { visible: isVisible }, classes.root]"
42
- v-bind="$attrs"
69
+ v-bind="attrs"
43
70
  >
44
71
  <button type="button" :class="classes.button" aria-label="Scroll to top" @click="scrollToTopFn">
45
72
  <div :class="classes.progressBase">
46
- <svg :class="classes.svg" viewBox="0 0 100 100" shape-rendering="geometricPrecision">
47
- <circle
48
- cx="50"
49
- cy="50"
50
- r="45"
51
- fill="var(--color-primary-950)"
52
- :stroke-width="progressWidth"
53
- stroke-dashoffset="0"
54
- stroke-linecap="round"
55
- class="gauge-secondary-stroke opacity-100"
56
- />
57
- <circle
58
- v-if="showProgress"
59
- cx="50"
60
- cy="50"
61
- r="45"
62
- fill="transparent"
63
- :stroke-width="progressWidth"
64
- stroke-linecap="round"
65
- class="gauge-primary-stroke opacity-100"
66
- :style="{
67
- '--stroke-percent': currentPercent,
68
- '--percent-to-px': percentageInPx,
69
- '--circumference': circumference,
70
- '--duration': durationInSeconds
71
- }"
72
- />
73
- </svg>
73
+ <div class="gauge-track"></div>
74
+ <div v-if="showProgress" class="gauge-progress"></div>
74
75
  <div :class="classes.iconContainer">
75
76
  <svg
76
77
  xmlns="http://www.w3.org/2000/svg"
@@ -106,27 +107,39 @@ const durationInSeconds = computed(() => `${duration}s`)
106
107
  }
107
108
 
108
109
  .progress-circle-base {
110
+ position: relative;
109
111
  transform: translateZ(0);
110
112
  }
111
113
 
112
- .gauge-primary-stroke {
113
- stroke: var(--color-primary-500);
114
- --stroke-percent: v-bind(currentPercent);
115
- --percent-to-px: v-bind(percentageInPx);
116
- --circumference: v-bind(circumference);
117
- stroke-dasharray: calc(var(--stroke-percent) * var(--percent-to-px)) var(--circumference);
118
- transition:
119
- stroke-dasharray v-bind(durationInSeconds) ease,
120
- stroke v-bind(durationInSeconds) ease;
121
- transform: rotate(-90deg);
122
- transform-origin: center;
123
- shape-rendering: geometricPrecision;
114
+ .gauge-track {
115
+ position: absolute;
116
+ inset: 0;
117
+ border-radius: 50%;
118
+ border-style: solid;
119
+ border-width: var(--gauge-stroke-width, 3px);
120
+ border-color: var(--gauge-track-border-color, var(--color-primary-900));
121
+ background-color: var(--gauge-track-bg, var(--color-primary-950));
124
122
  }
125
123
 
126
- .gauge-secondary-stroke {
127
- stroke: var(--color-primary-900);
128
- stroke-dasharray: 282.6;
129
- transform: rotate(-90deg);
130
- transform-origin: center;
124
+ .gauge-progress {
125
+ position: absolute;
126
+ inset: 0;
127
+ border-radius: 50%;
128
+ background: conic-gradient(
129
+ from 0deg,
130
+ var(--gauge-color, var(--color-primary-500)) 0deg,
131
+ var(--gauge-color, var(--color-primary-500)) calc(var(--gauge-progress, 0) * 3.6deg),
132
+ transparent calc(var(--gauge-progress, 0) * 3.6deg)
133
+ );
134
+ -webkit-mask: radial-gradient(
135
+ circle closest-side,
136
+ transparent calc(100% - var(--gauge-stroke-width, 3px)),
137
+ #000 calc(100% - var(--gauge-stroke-width, 3px))
138
+ );
139
+ mask: radial-gradient(
140
+ circle closest-side,
141
+ transparent calc(100% - var(--gauge-stroke-width, 3px)),
142
+ #000 calc(100% - var(--gauge-stroke-width, 3px))
143
+ );
131
144
  }
132
145
  </style>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed, useSlots } from "vue"
2
+ import { computed, useSlots, useAttrs } from "vue"
3
3
  import { scv } from "css-variants"
4
4
  import { useUi, resolveClasses } from "../../utils"
5
5
  import type { PageSectionProps } from "../../types/components/page-section"
@@ -24,6 +24,7 @@ const {
24
24
  links
25
25
  } = defineProps<PageSectionProps>()
26
26
 
27
+ const attrs = useAttrs()
27
28
  const slots = useSlots()
28
29
 
29
30
  const resolvedClasses = computed(() =>
@@ -51,9 +52,9 @@ const resolvedClasses = computed(() =>
51
52
  data-slot="root"
52
53
  :data-orientation="orientation"
53
54
  :class="[resolvedClasses.root]"
54
- v-bind="$attrs"
55
+ v-bind="attrs"
55
56
  >
56
- <slot v-if="$slots.background" name="background">
57
+ <slot v-if="slots.background" name="background">
57
58
  <div :class="resolvedClasses.background" data-slot="background">
58
59
  <slot name="background" />
59
60
  </div>
@@ -64,26 +65,26 @@ const resolvedClasses = computed(() =>
64
65
  <div data-slot="container" :class="resolvedClasses.container">
65
66
  <div
66
67
  v-if="
67
- $slots.header ||
68
+ slots.header ||
68
69
  icon ||
69
70
  headline ||
70
71
  title ||
71
72
  description ||
72
- $slots.body ||
73
- $slots.footer ||
74
- $slots.links ||
73
+ slots.body ||
74
+ slots.footer ||
75
+ slots.links ||
75
76
  links?.length
76
77
  "
77
78
  data-slot="wrapper"
78
79
  :class="resolvedClasses.wrapper"
79
80
  >
80
81
  <div
81
- v-if="$slots.header || icon || headline || title || description"
82
+ v-if="slots.header || icon || headline || title || description"
82
83
  data-slot="header"
83
84
  :class="resolvedClasses.header"
84
85
  >
85
86
  <slot name="header">
86
- <div v-if="icon || $slots.leading" data-slot="leading" :class="resolvedClasses.leading">
87
+ <div v-if="icon || slots.leading" data-slot="leading" :class="resolvedClasses.leading">
87
88
  <slot name="leading" :ui="resolvedClasses">
88
89
  <RLVIcon
89
90
  v-if="icon"
@@ -119,18 +120,18 @@ const resolvedClasses = computed(() =>
119
120
  </slot>
120
121
  </div>
121
122
 
122
- <div v-if="$slots.body" data-slot="body" :class="resolvedClasses.body">
123
+ <div v-if="slots.body" data-slot="body" :class="resolvedClasses.body">
123
124
  <slot name="body" />
124
125
  </div>
125
126
 
126
127
  <div
127
- v-if="$slots.footer || $slots.links || links?.length"
128
+ v-if="slots.footer || slots.links || links?.length"
128
129
  data-slot="footer"
129
130
  :class="resolvedClasses.footer"
130
131
  >
131
132
  <slot name="footer">
132
133
  <div
133
- v-if="links?.length || $slots.links"
134
+ v-if="links?.length || slots.links"
134
135
  data-slot="links"
135
136
  :class="resolvedClasses.links"
136
137
  >
@@ -141,7 +142,7 @@ const resolvedClasses = computed(() =>
141
142
  </div>
142
143
  </div>
143
144
 
144
- <slot v-if="$slots.default" />
145
+ <slot v-if="slots.default" />
145
146
  <div v-else-if="orientation === 'horizontal'" class="hidden lg:block" />
146
147
  </div>
147
148
 
@@ -49,11 +49,24 @@ const icon = computed(() => variantIcons[props.variant] ?? variantIcons.default!
49
49
  {{ description }}
50
50
  </div>
51
51
  <slot />
52
- </div>
53
52
 
54
- <button v-if="actionLabel" type="button" :class="resolvedClasses.action" data-slot="action">
55
- {{ actionLabel }}
56
- </button>
53
+ <div
54
+ v-if="$slots.actions || actionLabel"
55
+ class="flex items-center justify-end gap-2 mt-3"
56
+ data-slot="actions"
57
+ >
58
+ <slot name="actions">
59
+ <button
60
+ v-if="actionLabel"
61
+ type="button"
62
+ :class="resolvedClasses.action"
63
+ data-slot="action"
64
+ >
65
+ {{ actionLabel }}
66
+ </button>
67
+ </slot>
68
+ </div>
69
+ </div>
57
70
 
58
71
  <button
59
72
  type="button"
@@ -1,3 +1,4 @@
1
1
  export { useScrollToTop } from "./useScrollToTop"
2
2
  export { useToast } from "./useToast"
3
3
  export { headerLayersStore } from "./useHeaderStore"
4
+ export { useShortcuts } from "./useShortcuts"
@@ -1,6 +1,7 @@
1
1
  import { map } from "nanostores"
2
2
 
3
3
  export interface HeaderLayerInfo {
4
+ type: "banner" | "header"
4
5
  order: number
5
6
  isVisible: boolean
6
7
  height: number
@@ -0,0 +1,11 @@
1
+ import { onUnmounted } from "vue"
2
+ import { defineShortcuts } from "../utils/shortcuts"
3
+ import type { ShortcutsConfig, ShortcutsOptions } from "../utils/shortcuts"
4
+
5
+ export function useShortcuts(config: ShortcutsConfig, options?: ShortcutsOptions) {
6
+ const cleanup = defineShortcuts(config, options)
7
+ onUnmounted(() => {
8
+ cleanup()
9
+ })
10
+ return cleanup
11
+ }
@@ -33,6 +33,10 @@ export interface SecurityConfigOptions {
33
33
  * Additional style resources
34
34
  */
35
35
  styleResources?: string[]
36
+ /**
37
+ * Enables the 'require-trusted-types-for' directive
38
+ */
39
+ trustedTypes?: boolean
36
40
  /**
37
41
  * Direct overrides for any security config fields
38
42
  */
@@ -46,46 +50,46 @@ export interface SecurityConfigOptions {
46
50
  * support for easy customization and additions per project.
47
51
  *
48
52
  * @example
49
- * ```ts
50
- * // Default returned shape:
51
- * {
53
+ * ;```ts
54
+ * // Default returned shape:
55
+ * {
52
56
  * checkOrigin: true,
53
57
  * allowedDomains: [
54
- * {
55
- * hostname: "**.domain.com",
56
- * protocol: "https"
57
- * }
58
+ * {
59
+ * hostname: "**.domain.com",
60
+ * protocol: "https"
61
+ * }
58
62
  * ],
59
63
  * csp: {
60
- * algorithm: "SHA-384",
61
- * directives: [
62
- * "default-src 'none'",
63
- * "img-src 'self' data: https://cdn.domain.com https://i3.ytimg.com https://www.youtube.com https://www.youtube-nocookie.com",
64
- * "font-src 'self'",
65
- * "connect-src 'self' https://cloudflareinsights.com",
66
- * "frame-ancestors 'none'",
67
- * "frame-src https://www.youtube.com https://www.youtube-nocookie.com",
68
- * "upgrade-insecure-requests",
69
- * "base-uri 'self'",
70
- * "form-action 'self'"
71
- * ],
72
- * scriptDirective: {
73
- * resources: [
74
- * "'self'",
75
- * "https://static.cloudflareinsights.com",
76
- * "https://betterlytics.io/analytics.js",
77
- * "'inline-speculation-rules'"
78
- * ]
79
- * },
80
- * styleDirective: {
81
- * resources: [
82
- * "'self'",
83
- * "'sha256-SKuaOGnks7NAUq37nvw1PfGEE0lOAs5ERbiYRbGFIbw='"
84
- * ]
85
- * }
64
+ * algorithm: "SHA-384",
65
+ * directives: [
66
+ * "default-src 'none'",
67
+ * "img-src 'self' data: https://cdn.domain.com https://i3.ytimg.com https://www.youtube.com https://www.youtube-nocookie.com",
68
+ * "font-src 'self'",
69
+ * "connect-src 'self' https://cloudflareinsights.com",
70
+ * "frame-ancestors 'none'",
71
+ * "frame-src https://www.youtube.com https://www.youtube-nocookie.com",
72
+ * "upgrade-insecure-requests",
73
+ * "base-uri 'self'",
74
+ * "form-action 'self'"
75
+ * ],
76
+ * scriptDirective: {
77
+ * resources: [
78
+ * "'self'",
79
+ * "https://static.cloudflareinsights.com",
80
+ * "https://betterlytics.io/analytics.js",
81
+ * "'inline-speculation-rules'"
82
+ * ]
83
+ * },
84
+ * styleDirective: {
85
+ * resources: [
86
+ * "'self'",
87
+ * "'sha256-SKuaOGnks7NAUq37nvw1PfGEE0lOAs5ERbiYRbGFIbw='"
88
+ * ]
89
+ * }
90
+ * }
86
91
  * }
87
- * }
88
- * ```
92
+ * ```
89
93
  */
90
94
  export function defineSecurity(options: SecurityConfigOptions): AstroSecurityConfig {
91
95
  const { domain, overrides } = options
@@ -134,7 +138,10 @@ export function defineSecurity(options: SecurityConfigOptions): AstroSecurityCon
134
138
  `frame-src ${frameSources.join(" ")}`,
135
139
  "upgrade-insecure-requests",
136
140
  "base-uri 'self'",
137
- "form-action 'self'"
141
+ "form-action 'self'",
142
+ "object-src 'none'",
143
+ "manifest-src 'self'",
144
+ ...(options.trustedTypes ? ["require-trusted-types-for 'script'" as const] : [])
138
145
  ]
139
146
 
140
147
  // 3. Build script & style directives
@@ -24,4 +24,5 @@ export interface SiteConfig {
24
24
  maxDescriptionLength: number
25
25
  authorHandle?: string
26
26
  }
27
+ ui?: Record<string, any>
27
28
  }
@@ -0,0 +1,147 @@
1
+ ---
2
+ import RLATimeline from "../../components/astro/RLATimeline.astro"
3
+ import { scv } from "css-variants"
4
+ import { useUi, resolveClasses } from "../../utils"
5
+ import timelineThemeDefault, { createTimelineTheme } from "../../themes/timeline.theme"
6
+ import { getUIConfig } from "virtual:rimelight-ui"
7
+
8
+ interface Props {
9
+ component: string
10
+ ui?: any
11
+ class?: any
12
+ }
13
+
14
+ const { component, ui: uiProp, class: className } = Astro.props
15
+
16
+ const uiConfig = getUIConfig()
17
+ const timelineTheme = uiConfig.colors ? createTimelineTheme() : timelineThemeDefault
18
+ const timeline = scv(timelineTheme)
19
+ const classes = resolveClasses(timeline, {}, useUi("timeline", uiProp), className);
20
+ ---
21
+
22
+ <div class="api-changelog-container group" data-component={component} data-theme-classes={JSON.stringify(classes)}>
23
+ <!-- Loading skeleton -->
24
+ <div class="api-changelog-loading py-6 flex flex-col gap-4">
25
+ <div class="animate-pulse h-4 bg-gray-800 rounded w-1/4"></div>
26
+ <div class="animate-pulse h-3 bg-gray-850 rounded w-full"></div>
27
+ <div class="animate-pulse h-3 bg-gray-850 rounded w-5/6"></div>
28
+ </div>
29
+
30
+ <!-- Timeline Content -->
31
+ <div class="api-changelog-content hidden">
32
+ <RLATimeline ui={uiProp} class={className}>
33
+ <!-- Dynamic timeline items insert here -->
34
+ <div class="api-changelog-list flex flex-col space-y-8"></div>
35
+ </RLATimeline>
36
+ </div>
37
+
38
+ <div class="api-changelog-empty hidden text-sm italic text-gray-500 py-2">
39
+ No recent changes found.
40
+ </div>
41
+ </div>
42
+
43
+ <script>
44
+ function escapeHtml(str: string): string {
45
+ return str
46
+ .replace(/&/g, "&amp;")
47
+ .replace(/</g, "&lt;")
48
+ .replace(/>/g, "&gt;")
49
+ .replace(/"/g, "&quot;")
50
+ .replace(/'/g, "&#039;");
51
+ }
52
+
53
+ async function loadChangelog() {
54
+ if (typeof document === "undefined") return;
55
+ const containers = document.querySelectorAll('.api-changelog-container');
56
+
57
+ await Promise.all(
58
+ Array.from(containers).map(async (container) => {
59
+ if (container.getAttribute('data-changelog-ready') === 'true') return;
60
+ container.setAttribute('data-changelog-ready', 'true');
61
+
62
+ const component = container.getAttribute('data-component');
63
+ const themeClassesJson = container.getAttribute('data-theme-classes');
64
+ const themeClasses = themeClassesJson ? JSON.parse(themeClassesJson) : {};
65
+
66
+ const loadingEl = container.querySelector('.api-changelog-loading') as HTMLElement;
67
+ const contentEl = container.querySelector('.api-changelog-content') as HTMLElement;
68
+ const listEl = container.querySelector('.api-changelog-list') as HTMLElement;
69
+ const emptyEl = container.querySelector('.api-changelog-empty') as HTMLElement;
70
+
71
+ if (!component || !loadingEl || !contentEl || !listEl || !emptyEl) return;
72
+
73
+ try {
74
+ const res = await fetch(`/api/github/changelog?component=${component}`);
75
+ if (!res.ok) throw new Error();
76
+ const data = await res.json();
77
+
78
+ loadingEl.classList.add('hidden');
79
+
80
+ if (!data || data.length === 0) {
81
+ emptyEl.classList.remove('hidden');
82
+ return;
83
+ }
84
+
85
+ let html = '';
86
+ for (const item of data) {
87
+ const dateStr = item.published_at
88
+ ? new Date(item.published_at).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })
89
+ : 'Unreleased';
90
+
91
+ html += `
92
+ <div class="${themeClasses.node || 'relative timeline-node'}">
93
+ <!-- Dot Indicator -->
94
+ <span class="${themeClasses.dot || 'absolute -left-[33px] top-[2px] flex h-4 w-4 items-center justify-center rounded-full bg-gray-950 ring-2 ring-gray-800'}">
95
+ <span class="${themeClasses.dotInner || 'h-2 w-2 rounded-full bg-primary-500'}"></span>
96
+ </span>
97
+
98
+ <div class="${themeClasses.header || 'flex items-center justify-between gap-4 mb-3'}">
99
+ <div class="flex items-center gap-2">
100
+ <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-semibold bg-primary-500/10 text-primary-400 border border-primary-500/20">
101
+ ${escapeHtml(item.tag === 'unreleased' ? 'Soon' : item.tag)}
102
+ </span>
103
+ </div>
104
+
105
+ <time class="text-xs text-gray-500 font-normal">
106
+ ${escapeHtml(dateStr)}
107
+ </time>
108
+ </div>
109
+
110
+ <ul class="${themeClasses.content || 'space-y-2 text-sm text-gray-400'}">
111
+ ${item.commits.map((commit: any) => {
112
+ const cleanedMessage = commit.message
113
+ .replace(/#(\d+)/g, '<a href="https://github.com/Rimelight-Entertainment/rimelight/issues/$1" target="_blank" class="text-primary-400 hover:underline">#$1</a>')
114
+ .replace(/`(.*?)`/g, '<code class="text-xs px-1 bg-gray-900 border border-gray-800 rounded font-mono text-gray-300">$1</code>');
115
+
116
+ return `
117
+ <li class="flex items-start gap-2">
118
+ <a href="https://github.com/Rimelight-Entertainment/rimelight/commit/${commit.sha}" target="_blank" class="font-mono text-xs text-gray-500 hover:text-gray-300 hover:underline shrink-0 pt-0.5">
119
+ ${commit.sha.slice(0, 7)}
120
+ </a>
121
+ <span class="text-gray-300">
122
+ ${cleanedMessage}
123
+ </span>
124
+ </li>
125
+ `;
126
+ }).join('')}
127
+ </ul>
128
+ </div>
129
+ `;
130
+ }
131
+
132
+ listEl.innerHTML = html;
133
+ contentEl.classList.remove('hidden');
134
+ } catch (err) {
135
+ loadingEl.classList.add('hidden');
136
+ emptyEl.textContent = 'Failed to load changelog.';
137
+ emptyEl.classList.remove('hidden');
138
+ }
139
+ })
140
+ );
141
+ }
142
+
143
+ if (typeof document !== 'undefined') {
144
+ loadChangelog();
145
+ document.addEventListener('astro:after-swap', loadChangelog);
146
+ }
147
+ </script>
@@ -29,14 +29,14 @@ const emits = compMeta?.emits ?? []
29
29
  {emits.map((emit: EmitMeta) => (
30
30
  <tr class="border-b border-gray-100 dark:border-gray-800">
31
31
  <td class="py-2 px-3">
32
- <code class="text-sm bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
32
+ <span class="font-mono text-sm">
33
33
  {emit.name}
34
- </code>
34
+ </span>
35
35
  </td>
36
36
  <td class="py-2 px-3">
37
- <code class="text-xs bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded font-mono">
37
+ <span class="font-mono text-xs text-gray-600 dark:text-gray-400">
38
38
  {emit.type}
39
- </code>
39
+ </span>
40
40
  </td>
41
41
  </tr>
42
42
  ))}
@@ -33,15 +33,15 @@ const props = filterProps(compMeta?.props ?? [], filterOpts)
33
33
  <tbody>
34
34
  {props.map((prop: PropMeta) => (
35
35
  <tr>
36
- <td><code>{prop.name}</code></td>
37
- <td><code>{prop.type}</code></td>
36
+ <td><span class="font-mono text-sm">{prop.name}</span></td>
37
+ <td><span class="font-mono text-xs text-gray-600 dark:text-gray-400">{prop.type}</span></td>
38
38
  <td>{prop.required ? "Yes" : "No"}</td>
39
- <td>{prop.default !== undefined ? <code>{prop.default}</code> : "—"}</td>
39
+ <td>{prop.default !== undefined ? <span class="font-mono text-xs text-gray-600 dark:text-gray-400">{prop.default}</span> : "—"}</td>
40
40
  <td>{prop.description || "—"}</td>
41
41
  </tr>
42
42
  ))}
43
43
  </tbody>
44
44
  </table>
45
45
  ) : (
46
- <p>No props to display.</p>
46
+ <p class="text-sm italic text-gray-500">No props to display.</p>
47
47
  )}
@@ -29,15 +29,15 @@ const slots = compMeta?.slots ?? []
29
29
  {slots.map((slot: SlotMeta) => (
30
30
  <tr class="border-b border-gray-100 dark:border-gray-800">
31
31
  <td class="py-2 px-3">
32
- <code class="text-sm bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded">
32
+ <span class="font-mono text-sm">
33
33
  {slot.name}
34
- </code>
34
+ </span>
35
35
  </td>
36
36
  <td class="py-2 px-3">
37
37
  {Object.keys(slot.bindings).length > 0 ? (
38
- <code class="text-xs bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 rounded font-mono">
38
+ <span class="font-mono text-xs text-gray-600 dark:text-gray-400">
39
39
  {Object.values(slot.bindings)[0]}
40
- </code>
40
+ </span>
41
41
  ) : (
42
42
  <span class="text-gray-400">-</span>
43
43
  )}