@rimelight/ui 0.0.21 → 0.0.22

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.
@@ -1,125 +1,119 @@
1
- /// <reference path="./locals.d.ts" />
2
- import type { StarlightPlugin, StarlightUserConfig } from "@astrojs/starlight/types"
3
- import virtual from "vite-plugin-virtual"
4
- import path from "node:path"
5
- import { fileURLToPath } from "node:url"
6
- import { z } from "astro/zod"
7
-
8
- import {
9
- StarlightSidebarTopicsConfigSchema,
10
- StarlightSidebarTopicsOptionsSchema,
11
- type StarlightSidebarTopicsUserConfig,
12
- type StarlightSidebarTopicsUserOptions
13
- } from "./libs/config"
14
- import { throwPluginError } from "./libs/plugin"
15
- import { vitePluginStarlightSidebarTopics } from "./libs/vite"
16
-
17
- export type {
18
- StarlightSidebarTopicsConfig,
19
- StarlightSidebarTopicsUserConfig,
20
- StarlightSidebarTopicsOptions,
21
- StarlightSidebarTopicsUserOptions
22
- } from "./libs/config"
23
-
24
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
25
-
26
- export interface StarlightAddonsConfig {
27
- copy?: boolean
28
- share?: boolean
29
- // Merged config for Topics
30
- topics?: StarlightSidebarTopicsUserConfig
31
- topicOptions?: StarlightSidebarTopicsUserOptions
32
- }
33
-
34
- export default function starlightAddons(userConfig?: StarlightAddonsConfig): StarlightPlugin {
35
- // 1. Setup default and merged config for Addons
36
- const defaultAddonConfig: StarlightAddonsConfig = {
37
- copy: true,
38
- share: true
39
- }
40
-
41
- const addonConfig = { ...defaultAddonConfig, ...userConfig }
42
-
43
- // 2. Validate Sidebar Topics configurations if they exist
44
- const parsedTopics = StarlightSidebarTopicsConfigSchema.safeParse(addonConfig.topics || [])
45
- if (!parsedTopics.success) {
46
- throwPluginError(
47
- `Invalid topics config: ${parsedTopics.error.issues.map((i) => i.message).join("\n")}`
48
- )
49
- }
50
-
51
- const parsedOptions = StarlightSidebarTopicsOptionsSchema.safeParse(addonConfig.topicOptions)
52
- if (!parsedOptions.success) {
53
- throwPluginError(`Invalid topic options: ${z.prettifyError(parsedOptions.error)}`)
54
- }
55
-
56
- const topicsData = parsedTopics.data
57
- const optionsData = parsedOptions.data
58
-
59
- return {
60
- name: "rimelight-ui-starlight-addons",
61
- hooks: {
62
- "config:setup"({
63
- addIntegration,
64
- updateConfig,
65
- logger,
66
- command,
67
- config: starlightConfig
68
- }) {
69
- // --- Sidebar Topics Logic ---
70
- if ((command === "dev" || command === "build") && topicsData.length > 0) {
71
- if (starlightConfig.sidebar) {
72
- throwPluginError(
73
- "Sidebar detected. To use topics, move your sidebar items into the topics config."
74
- )
75
- }
76
-
77
- const topicSidebar: StarlightUserConfig["sidebar"] = topicsData.map((topic, index) => {
78
- return "items" in topic
79
- ? { label: String(index), items: topic.items }
80
- : { label: String(index), items: [] }
81
- })
82
-
83
- updateConfig({
84
- sidebar: topicSidebar,
85
- components: {
86
- Sidebar: `starlight-sidebar-topics/overrides/Sidebar.astro`
87
- }
88
- })
89
- }
90
-
91
- // --- Addons Logic ---
92
- if (!addonConfig.copy && !addonConfig.share) {
93
- logger.warn("StarlightAddons: No features enabled.")
94
- }
95
-
96
- updateConfig({
97
- components: {
98
- // This will merge with the Sidebar override if both exist
99
- PageTitle: path.join(__dirname, "PageTitle.astro")
100
- }
101
- })
102
-
103
- // --- Unified Integration ---
104
- addIntegration({
105
- name: "rimelight-ui-starlight-addons-integration",
106
- hooks: {
107
- "astro:config:setup": ({ updateConfig: updateAstroConfig }) => {
108
- updateAstroConfig({
109
- vite: {
110
- plugins: [
111
- virtual({
112
- "virtual:rimelight-starlight-addons-config": `export default ${JSON.stringify(addonConfig)}`
113
- }),
114
- // Pass the validated topic data to the vite plugin
115
- vitePluginStarlightSidebarTopics(topicsData, optionsData)
116
- ]
117
- }
118
- } as Record<string, unknown>)
119
- }
120
- }
121
- })
122
- }
123
- }
124
- }
125
- }
1
+ /// <reference path="./locals.d.ts" />
2
+ import type { StarlightPlugin, StarlightUserConfig } from "@astrojs/starlight/types"
3
+ import virtual from "vite-plugin-virtual"
4
+ import path from "node:path"
5
+ import { fileURLToPath } from "node:url"
6
+ import { z } from "astro/zod"
7
+
8
+ import {
9
+ StarlightSidebarTopicsConfigSchema,
10
+ StarlightSidebarTopicsOptionsSchema,
11
+ type StarlightSidebarTopicsUserConfig,
12
+ type StarlightSidebarTopicsUserOptions
13
+ } from "./libs/config"
14
+ import { throwPluginError } from "./libs/plugin"
15
+ import { vitePluginStarlightSidebarTopics } from "./libs/vite"
16
+
17
+ export type {
18
+ StarlightSidebarTopicsConfig,
19
+ StarlightSidebarTopicsUserConfig,
20
+ StarlightSidebarTopicsOptions,
21
+ StarlightSidebarTopicsUserOptions
22
+ } from "./libs/config"
23
+
24
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
25
+
26
+ export interface StarlightAddonsConfig {
27
+ copy?: boolean
28
+ share?: boolean
29
+ // Merged config for Topics
30
+ topics?: StarlightSidebarTopicsUserConfig
31
+ topicOptions?: StarlightSidebarTopicsUserOptions
32
+ }
33
+
34
+ export default function starlightAddons(userConfig?: StarlightAddonsConfig): StarlightPlugin {
35
+ // 1. Setup default and merged config for Addons
36
+ const defaultAddonConfig: StarlightAddonsConfig = {
37
+ copy: true,
38
+ share: true
39
+ }
40
+
41
+ const addonConfig = { ...defaultAddonConfig, ...userConfig }
42
+
43
+ // 2. Validate Sidebar Topics configurations if they exist
44
+ const parsedTopics = StarlightSidebarTopicsConfigSchema.safeParse(addonConfig.topics || [])
45
+ if (!parsedTopics.success) {
46
+ throwPluginError(
47
+ `Invalid topics config: ${parsedTopics.error.issues.map((i) => i.message).join("\n")}`
48
+ )
49
+ }
50
+
51
+ const parsedOptions = StarlightSidebarTopicsOptionsSchema.safeParse(addonConfig.topicOptions)
52
+ if (!parsedOptions.success) {
53
+ throwPluginError(`Invalid topic options: ${z.prettifyError(parsedOptions.error)}`)
54
+ }
55
+
56
+ const topicsData = parsedTopics.data
57
+ const optionsData = parsedOptions.data
58
+
59
+ return {
60
+ name: "rimelight-ui-starlight-addons",
61
+ hooks: {
62
+ "config:setup"({ addIntegration, updateConfig, logger, command, config: starlightConfig }) {
63
+ // --- Sidebar Topics Logic ---
64
+ if ((command === "dev" || command === "build") && topicsData.length > 0) {
65
+ if (starlightConfig.sidebar) {
66
+ throwPluginError(
67
+ "Sidebar detected. To use topics, move your sidebar items into the topics config."
68
+ )
69
+ }
70
+
71
+ const topicSidebar: StarlightUserConfig["sidebar"] = topicsData.map((topic, index) => {
72
+ return "items" in topic
73
+ ? { label: String(index), items: topic.items }
74
+ : { label: String(index), items: [] }
75
+ })
76
+
77
+ updateConfig({
78
+ sidebar: topicSidebar,
79
+ components: {
80
+ Sidebar: `starlight-sidebar-topics/overrides/Sidebar.astro`
81
+ }
82
+ })
83
+ }
84
+
85
+ // --- Addons Logic ---
86
+ if (!addonConfig.copy && !addonConfig.share) {
87
+ logger.warn("StarlightAddons: No features enabled.")
88
+ }
89
+
90
+ updateConfig({
91
+ components: {
92
+ // This will merge with the Sidebar override if both exist
93
+ PageTitle: path.join(__dirname, "PageTitle.astro")
94
+ }
95
+ })
96
+
97
+ // --- Unified Integration ---
98
+ addIntegration({
99
+ name: "rimelight-ui-starlight-addons-integration",
100
+ hooks: {
101
+ "astro:config:setup": ({ updateConfig: updateAstroConfig }) => {
102
+ updateAstroConfig({
103
+ vite: {
104
+ plugins: [
105
+ virtual({
106
+ "virtual:rimelight-starlight-addons-config": `export default ${JSON.stringify(addonConfig)}`
107
+ }),
108
+ // Pass the validated topic data to the vite plugin
109
+ vitePluginStarlightSidebarTopics(topicsData, optionsData)
110
+ ]
111
+ }
112
+ } as Record<string, unknown>)
113
+ }
114
+ }
115
+ })
116
+ }
117
+ }
118
+ }
119
+ }
@@ -4,153 +4,153 @@
4
4
 
5
5
  /* Keyboard Component Styles */
6
6
  .keyboard-wrapper {
7
- display: flex;
8
- flex-direction: column;
9
- align-items: center;
10
- padding: 16px;
11
- border-radius: 12px;
12
- width: 100%;
13
- max-width: 100%;
14
- margin: 16px 0;
15
- box-sizing: border-box;
7
+ display: flex;
8
+ flex-direction: column;
9
+ align-items: center;
10
+ padding: 16px;
11
+ border-radius: 12px;
12
+ width: 100%;
13
+ max-width: 100%;
14
+ margin: 16px 0;
15
+ box-sizing: border-box;
16
16
  }
17
17
  .keyboard-container {
18
- width: 100%;
19
- display: flex;
20
- justify-content: center;
18
+ width: 100%;
19
+ display: flex;
20
+ justify-content: center;
21
21
  }
22
22
  .keyboard-svg {
23
- width: 100%;
24
- height: auto;
25
- max-width: 900px;
23
+ width: 100%;
24
+ height: auto;
25
+ max-width: 900px;
26
26
  }
27
27
  .kb-key-label {
28
- font-family: var(--font-mono, monospace);
29
- font-size: 10px;
30
- font-weight: 600;
31
- pointer-events: none;
32
- user-select: none;
28
+ font-family: var(--font-mono, monospace);
29
+ font-size: 10px;
30
+ font-weight: 600;
31
+ pointer-events: none;
32
+ user-select: none;
33
33
  }
34
34
  .kb-legend {
35
- display: flex;
36
- flex-wrap: wrap;
37
- justify-content: center;
38
- gap: 12px 24px;
39
- margin-top: 24px;
40
- width: 100%;
35
+ display: flex;
36
+ flex-wrap: wrap;
37
+ justify-content: center;
38
+ gap: 12px 24px;
39
+ margin-top: 24px;
40
+ width: 100%;
41
41
  }
42
42
  .keyboard-wrapper .legend-item {
43
- display: flex !important;
44
- align-items: center !important;
45
- gap: 8px;
46
- margin: 0 !important;
47
- padding: 0 !important;
48
- font-size: 10px;
49
- color: #a0a0b0;
50
- text-transform: uppercase;
51
- letter-spacing: 0.05em;
52
- font-weight: 500;
53
- white-space: nowrap;
54
- line-height: 1 !important;
43
+ display: flex !important;
44
+ align-items: center !important;
45
+ gap: 8px;
46
+ margin: 0 !important;
47
+ padding: 0 !important;
48
+ font-size: 10px;
49
+ color: #a0a0b0;
50
+ text-transform: uppercase;
51
+ letter-spacing: 0.05em;
52
+ font-weight: 500;
53
+ white-space: nowrap;
54
+ line-height: 1 !important;
55
55
  }
56
56
  .legend-color-svg {
57
- flex-shrink: 0;
58
- display: block;
57
+ flex-shrink: 0;
58
+ display: block;
59
59
  }
60
60
  .keyboard-wrapper .legend-label {
61
- margin: 0 !important;
62
- padding: 0 !important;
63
- line-height: 1 !important;
64
- display: inline-block !important;
61
+ margin: 0 !important;
62
+ padding: 0 !important;
63
+ line-height: 1 !important;
64
+ display: inline-block !important;
65
65
  }
66
66
 
67
67
  /* Gamepad Component Styles */
68
68
  .gamepad-wrapper {
69
- display: flex;
70
- flex-direction: column;
71
- align-items: center;
72
- padding: 48px;
73
- border-radius: 24px;
74
- width: 100%;
75
- max-width: 100%;
76
- margin: 24px 0;
77
- box-sizing: border-box;
78
- container-type: inline-size;
79
- border: 1px solid #1a1a2e;
69
+ display: flex;
70
+ flex-direction: column;
71
+ align-items: center;
72
+ padding: 48px;
73
+ border-radius: 24px;
74
+ width: 100%;
75
+ max-width: 100%;
76
+ margin: 24px 0;
77
+ box-sizing: border-box;
78
+ container-type: inline-size;
79
+ border: 1px solid #1a1a2e;
80
80
  }
81
81
  .gamepad-container {
82
- width: 100%;
83
- max-width: 440px;
82
+ width: 100%;
83
+ max-width: 440px;
84
84
  }
85
85
  .gamepad-svg {
86
- width: 100%;
87
- height: auto;
86
+ width: 100%;
87
+ height: auto;
88
88
  }
89
89
  .gp-btn rect,
90
90
  .gp-btn circle {
91
- fill: #1e1e30;
92
- stroke: #33334d;
93
- stroke-width: 1;
94
- transition: all 0.2s ease;
91
+ fill: #1e1e30;
92
+ stroke: #33334d;
93
+ stroke-width: 1;
94
+ transition: all 0.2s ease;
95
95
  }
96
96
  .btn-label {
97
- fill: #555570;
98
- font-size: 10px;
99
- font-family: var(--font-sans, system-ui);
100
- font-weight: 800;
101
- pointer-events: none;
102
- line-height: 1;
97
+ fill: #555570;
98
+ font-size: 10px;
99
+ font-family: var(--font-sans, system-ui);
100
+ font-weight: 800;
101
+ pointer-events: none;
102
+ line-height: 1;
103
103
  }
104
104
  .gp-btn.active rect,
105
105
  .gp-btn.active circle {
106
- fill: var(--hl-color, #00c16a);
107
- stroke: #ffffff33;
108
- stroke-width: 1.5;
106
+ fill: var(--hl-color, #00c16a);
107
+ stroke: #ffffff33;
108
+ stroke-width: 1.5;
109
109
  }
110
110
  .gp-btn.active .btn-label {
111
- fill: #fff;
111
+ fill: #fff;
112
112
  }
113
113
  .gamepad-legend {
114
- display: flex;
115
- flex-wrap: wrap;
116
- justify-content: center;
117
- gap: 12px 24px;
118
- margin-top: 40px;
119
- width: 100%;
114
+ display: flex;
115
+ flex-wrap: wrap;
116
+ justify-content: center;
117
+ gap: 12px 24px;
118
+ margin-top: 40px;
119
+ width: 100%;
120
120
  }
121
121
  .gamepad-wrapper .legend-item {
122
- display: flex !important;
123
- align-items: center !important;
124
- gap: 8px;
125
- height: 10px !important;
126
- font-size: 10px;
127
- color: #777790;
128
- text-transform: uppercase;
129
- letter-spacing: 0.1em;
130
- font-weight: 700;
131
- line-height: 10px !important;
132
- padding: 0 !important;
133
- margin: 0 !important;
122
+ display: flex !important;
123
+ align-items: center !important;
124
+ gap: 8px;
125
+ height: 10px !important;
126
+ font-size: 10px;
127
+ color: #777790;
128
+ text-transform: uppercase;
129
+ letter-spacing: 0.1em;
130
+ font-weight: 700;
131
+ line-height: 10px !important;
132
+ padding: 0 !important;
133
+ margin: 0 !important;
134
134
  }
135
135
  .gamepad-wrapper .legend-label {
136
- display: inline-block !important;
137
- height: 10px !important;
138
- line-height: 10px !important;
139
- margin: 0 !important;
140
- padding: 0 !important;
136
+ display: inline-block !important;
137
+ height: 10px !important;
138
+ line-height: 10px !important;
139
+ margin: 0 !important;
140
+ padding: 0 !important;
141
141
  }
142
142
 
143
143
  .kb-legend-label {
144
- font-family: var(--font-sans, system-ui);
145
- font-size: 10px;
146
- font-weight: 700;
147
- text-transform: uppercase;
148
- letter-spacing: 0.05em;
144
+ font-family: var(--font-sans, system-ui);
145
+ font-size: 10px;
146
+ font-weight: 700;
147
+ text-transform: uppercase;
148
+ letter-spacing: 0.05em;
149
149
  }
150
150
  .gp-legend-label {
151
- font-family: var(--font-sans, system-ui);
152
- font-size: 10px;
153
- font-weight: 700;
154
- text-transform: uppercase;
155
- letter-spacing: 0.1em;
156
- }
151
+ font-family: var(--font-sans, system-ui);
152
+ font-size: 10px;
153
+ font-weight: 700;
154
+ text-transform: uppercase;
155
+ letter-spacing: 0.1em;
156
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Theme configuration for the Footer component. Defines the slots, base styles, and variants for
3
+ * layout management.
4
+ */
5
+ export const footerTheme = {
6
+ slots: ["root", "container", "left", "center", "right"],
7
+ base: {
8
+ root: "py-8 lg:py-12",
9
+ container:
10
+ "flex flex-col justify-between gap-xl lg:flex-row lg:items-stretch px-4 md:px-6 lg:px-8",
11
+ left: "order-last flex flex-col items-center justify-between gap-xl lg:order-1 lg:flex-none lg:items-start",
12
+ center: "flex flex-col items-center justify-start lg:order-2 lg:flex-1",
13
+ right:
14
+ "order-first flex flex-col items-center justify-between gap-xl lg:order-3 lg:flex-none lg:items-end"
15
+ },
16
+ variants: {
17
+ contain: {
18
+ true: {
19
+ container: "mx-auto max-w-7xl"
20
+ },
21
+ false: {
22
+ container: "w-full"
23
+ }
24
+ }
25
+ },
26
+ defaultVariants: {
27
+ contain: false
28
+ }
29
+ } as const
@@ -1,11 +1,29 @@
1
1
  /**
2
2
  * Theme configuration for the Header component. Defines the slots, base styles, and variants for
3
3
  * layout management.
4
+ *
5
+ * Slots:
6
+ *
7
+ * - `root` — the `<header>` element
8
+ * - `content` — inner wrapper div used for height measurement (fixed/hideOnScroll mode)
9
+ * - `container` — max-width flex row
10
+ * - `left` — left region
11
+ * - `center` — center region
12
+ * - `right` — right region
13
+ *
14
+ * Variants:
15
+ *
16
+ * - `contain` — constrains container to max-w-7xl
17
+ * - `sticky` — sticky positioning (ignored when `fixed` variant is true)
18
+ * - `fixed` — position: fixed with left/right 0 and smooth top/opacity transition; used for stacked
19
+ * header layers. Top offset and z-index are applied via inline style (top prop + stackIndex prop)
20
+ * rather than theme classes.
4
21
  */
5
22
  export const headerTheme = {
6
- slots: ["root", "container", "left", "center", "right"],
23
+ slots: ["root", "content", "container", "left", "center", "right"],
7
24
  base: {
8
25
  root: "w-full",
26
+ content: "w-full",
9
27
  container:
10
28
  "flex flex-row items-center justify-between gap-4xs h-16 md:h-18 px-4 md:px-6 lg:px-8",
11
29
  left: "flex flex-none items-center justify-start",
@@ -28,10 +46,22 @@ export const headerTheme = {
28
46
  false: {
29
47
  root: "relative"
30
48
  }
49
+ },
50
+ /**
51
+ * Fixed-layer mode. When true, overrides sticky/relative positioning with `position: fixed` and
52
+ * enables smooth transitions. `top` and `z-index` are applied via inline `:style` on the
53
+ * component.
54
+ */
55
+ fixed: {
56
+ true: {
57
+ root: "fixed left-0 right-0 transition-[top,opacity] duration-200 ease-in-out"
58
+ },
59
+ false: {}
31
60
  }
32
61
  },
33
62
  defaultVariants: {
34
63
  contain: true,
35
- sticky: true
64
+ sticky: true,
65
+ fixed: false
36
66
  }
37
67
  } as const
@@ -1,15 +1,17 @@
1
- import { buttonTheme } from "./button.theme"
2
1
  import { headerTheme } from "./header.theme"
2
+ import { footerTheme } from "./footer.theme"
3
3
  import { scrollToTopTheme } from "./scroll-to-top.theme"
4
+ import { buttonTheme } from "./button.theme"
4
5
 
5
6
  /**
6
7
  * Default UI configuration for all components. This is the baseline that gets merged with
7
8
  * user-provided overrides via the Astro integration options.
8
9
  */
9
10
  export const defaultUIConfig = {
10
- button: buttonTheme,
11
- header: headerTheme,
12
- "scroll-to-top": scrollToTopTheme
11
+ "header": headerTheme,
12
+ "footer": footerTheme,
13
+ "scroll-to-top": scrollToTopTheme,
14
+ "button": buttonTheme
13
15
  } as const
14
16
 
15
17
  export type DefaultUIConfig = typeof defaultUIConfig