@rimelight/ui 0.0.19 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -9
- package/src/components/Head.astro +3 -0
- package/src/components/astro/RLAScrollToTop.astro +175 -0
- package/src/components/vue/RLVScrollToTop.vue +154 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/useScrollToTop.ts +62 -0
- package/src/integrations/ui.ts +1 -0
- package/src/plugins/index.ts +2 -2
- package/src/plugins/starlightAddons/PackageManagers.astro +73 -73
- package/src/plugins/starlightAddons/PageTitle.astro +112 -112
- package/src/plugins/starlightAddons/components/Gamepad.astro +180 -0
- package/src/plugins/starlightAddons/components/Keyboard.astro +321 -0
- package/src/plugins/starlightAddons/index.ts +125 -126
- package/src/styles/index.css +154 -0
- package/src/styles/prism.css +42 -0
- package/src/themes/index.ts +3 -1
- package/src/themes/scroll-to-top.theme.ts +48 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface HighlightGroup {
|
|
3
|
+
keys: string[]
|
|
4
|
+
color?: string
|
|
5
|
+
label?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
highlightedKeys?: string[]
|
|
10
|
+
groups?: HighlightGroup[]
|
|
11
|
+
keyGap?: number
|
|
12
|
+
sectionGap?: number
|
|
13
|
+
keySize?: number
|
|
14
|
+
variant?: 'full' | '60'
|
|
15
|
+
os?: 'windows' | 'mac'
|
|
16
|
+
highlightColor?: string
|
|
17
|
+
backgroundColor?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
highlightedKeys = [],
|
|
22
|
+
groups = [],
|
|
23
|
+
keyGap = 8,
|
|
24
|
+
sectionGap = 40,
|
|
25
|
+
keySize = 40,
|
|
26
|
+
variant = 'full',
|
|
27
|
+
os = 'windows',
|
|
28
|
+
highlightColor = 'var(--ui-color-primary-500, #00c16a)',
|
|
29
|
+
backgroundColor = 'transparent'
|
|
30
|
+
} = Astro.props
|
|
31
|
+
|
|
32
|
+
interface Key {
|
|
33
|
+
label?: string
|
|
34
|
+
width: number
|
|
35
|
+
height?: number
|
|
36
|
+
code?: string
|
|
37
|
+
type?: 'spacer' | 'key'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const mainRows: Key[][] = [
|
|
41
|
+
[
|
|
42
|
+
{ label: 'Esc', width: 1, code: 'Escape' },
|
|
43
|
+
{ type: 'spacer', width: 1 },
|
|
44
|
+
{ label: 'F1', width: 1 }, { label: 'F2', width: 1 }, { label: 'F3', width: 1 }, { label: 'F4', width: 1 },
|
|
45
|
+
{ type: 'spacer', width: 0.5 },
|
|
46
|
+
{ label: 'F5', width: 1 }, { label: 'F6', width: 1 }, { label: 'F7', width: 1 }, { label: 'F8', width: 1 },
|
|
47
|
+
{ type: 'spacer', width: 0.5 },
|
|
48
|
+
{ label: 'F9', width: 1 }, { label: 'F10', width: 1 }, { label: 'F11', width: 1 }, { label: 'F12', width: 1 },
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
{ label: '`', width: 1 }, { label: '1', width: 1, code: 'Digit1' }, { label: '2', width: 1, code: 'Digit2' }, { label: '3', width: 1, code: 'Digit3' },
|
|
52
|
+
{ label: '4', width: 1, code: 'Digit4' }, { label: '5', width: 1, code: 'Digit5' }, { label: '6', width: 1, code: 'Digit6' }, { label: '7', width: 1, code: 'Digit7' },
|
|
53
|
+
{ label: '8', width: 1, code: 'Digit8' }, { label: '9', width: 1, code: 'Digit9' }, { label: '0', width: 1, code: 'Digit0' }, { label: '-', width: 1, code: 'Minus' },
|
|
54
|
+
{ label: '=', width: 1, code: 'Equal' }, { label: 'Bkspc', width: 2, code: 'Backspace' },
|
|
55
|
+
],
|
|
56
|
+
[
|
|
57
|
+
{ label: 'Tab', width: 1.5 }, { label: 'Q', width: 1 }, { label: 'W', width: 1 }, { label: 'E', width: 1 },
|
|
58
|
+
{ label: 'R', width: 1 }, { label: 'T', width: 1 }, { label: 'Y', width: 1 }, { label: 'U', width: 1 },
|
|
59
|
+
{ label: 'I', width: 1 }, { label: 'O', width: 1 }, { label: 'P', width: 1 }, { label: '[', width: 1 },
|
|
60
|
+
{ label: ']', width: 1 }, { label: '\\', width: 1.5 },
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
{ label: 'Caps', width: 1.75, code: 'CapsLock' }, { label: 'A', width: 1 }, { label: 'S', width: 1 }, { label: 'D', width: 1 },
|
|
64
|
+
{ label: 'F', width: 1 }, { label: 'G', width: 1 }, { label: 'H', width: 1 }, { label: 'J', width: 1 },
|
|
65
|
+
{ label: 'K', width: 1 }, { label: 'L', width: 1 }, { label: ';', width: 1 }, { label: "'", width: 1 },
|
|
66
|
+
{ label: 'Enter', width: 2.25, code: 'Enter' },
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
{ label: 'Shift', width: 2.5, code: 'ShiftLeft' }, { label: 'Z', width: 1 }, { label: 'X', width: 1 }, { label: 'C', width: 1 },
|
|
70
|
+
{ label: 'V', width: 1 }, { label: 'B', width: 1 }, { label: 'N', width: 1 }, { label: 'M', width: 1 },
|
|
71
|
+
{ label: ',', width: 1 }, { label: '.', width: 1, code: 'Period' }, { label: '/', width: 1, code: 'Slash' }, { label: 'Shift', width: 2.5, code: 'ShiftRight' },
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
{ label: 'Ctrl', width: 1.25, code: 'ControlLeft' }, { label: 'Win', width: 1.25, code: 'MetaLeft' }, { label: 'Alt', width: 1.25, code: 'AltLeft' },
|
|
75
|
+
{ label: 'Space', width: 6.25, code: 'Space' },
|
|
76
|
+
{ label: 'Alt', width: 1.25, code: 'AltRight' }, { label: 'Fn', width: 1.25 }, { label: 'Menu', width: 1.25, code: 'ContextMenu' }, { label: 'Ctrl', width: 1.25, code: 'ControlRight' },
|
|
77
|
+
]
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
const functionRows: Key[][] = [
|
|
81
|
+
[{ label: 'PrtSc', width: 1, code: 'PrintScreen' }, { label: 'ScrLk', width: 1, code: 'ScrollLock' }, { label: 'Pause', width: 1, code: 'Pause' }],
|
|
82
|
+
[{ label: 'Ins', width: 1 }, { label: 'Home', width: 1 }, { label: 'PgUp', width: 1 }],
|
|
83
|
+
[{ label: 'Del', width: 1 }, { label: 'End', width: 1 }, { label: 'PgDn', width: 1 }],
|
|
84
|
+
[{ type: 'spacer', width: 3 }],
|
|
85
|
+
[{ type: 'spacer', width: 1 }, { label: '↑', width: 1, code: 'ArrowUp' }, { type: 'spacer', width: 1 }],
|
|
86
|
+
[{ label: '←', width: 1, code: 'ArrowLeft' }, { label: '↓', width: 1, code: 'ArrowDown' }, { label: '→', width: 1, code: 'ArrowRight' }],
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
const numpadKeys: Key[] = [
|
|
90
|
+
{ label: 'Num', width: 1, code: 'NumLock' }, { label: '/', width: 1, code: 'NumpadDivide' }, { label: '*', width: 1, code: 'NumpadMultiply' }, { label: '-', width: 1, code: 'NumpadSubtract' },
|
|
91
|
+
{ label: '7', width: 1, code: 'Numpad7' }, { label: '8', width: 1, code: 'Numpad8' }, { label: '9', width: 1, code: 'Numpad9' }, { label: '+', width: 1, height: 2, code: 'NumpadAdd' },
|
|
92
|
+
{ label: '4', width: 1, code: 'Numpad4' }, { label: '5', width: 1, code: 'Numpad5' }, { label: '6', width: 1, code: 'Numpad6' },
|
|
93
|
+
{ label: '1', width: 1, code: 'Numpad1' }, { label: '2', width: 1, code: 'Numpad2' }, { label: '3', width: 1, code: 'Numpad3' }, { label: 'Enter', width: 1, height: 2, code: 'NumpadEnter' },
|
|
94
|
+
{ label: '0', width: 2, code: 'Numpad0' }, { label: '.', width: 1, code: 'NumpadDecimal' },
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
function getKeyLabel(key: Key) {
|
|
98
|
+
if (os !== 'mac') return key.label;
|
|
99
|
+
|
|
100
|
+
const macMap: Record<string, string> = {
|
|
101
|
+
'Ctrl': 'control',
|
|
102
|
+
'Win': 'option',
|
|
103
|
+
'Alt': 'command',
|
|
104
|
+
'Bkspc': 'delete',
|
|
105
|
+
'Enter': 'return',
|
|
106
|
+
'Caps': 'caps lock',
|
|
107
|
+
'Shift': 'shift',
|
|
108
|
+
'Menu': 'option',
|
|
109
|
+
'Tab': 'tab',
|
|
110
|
+
'Esc': 'esc',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const label = (key.label && macMap[key.label]) || key.label;
|
|
114
|
+
|
|
115
|
+
if (label === 'command') return 'cmd ⌘';
|
|
116
|
+
if (label === 'option') return 'opt ⌥';
|
|
117
|
+
if (label === 'control') return 'ctrl ⌃';
|
|
118
|
+
if (label === 'shift') return 'shift ⇧';
|
|
119
|
+
if (label === 'return') return 'return ↩';
|
|
120
|
+
if (label === 'delete') return 'delete ⌫';
|
|
121
|
+
|
|
122
|
+
return label;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function getKeyHighlight(key: Key) {
|
|
126
|
+
if (!key) return { active: false };
|
|
127
|
+
|
|
128
|
+
const code = key.code || key.label || '';
|
|
129
|
+
const label = key.label || '';
|
|
130
|
+
const macLabel = getKeyLabel(key) || '';
|
|
131
|
+
|
|
132
|
+
const isMatch = (list: string[]) => {
|
|
133
|
+
if (!list || !Array.isArray(list)) return false;
|
|
134
|
+
if (list.includes(code)) return true;
|
|
135
|
+
if (list.includes(label)) {
|
|
136
|
+
if (code.startsWith('Numpad') && /^\d$/.test(label)) return false;
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
return os === 'mac' && list.includes(macLabel);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (isMatch(highlightedKeys)) {
|
|
143
|
+
return { active: true, color: highlightColor };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const group of groups) {
|
|
147
|
+
if (isMatch(group.keys)) {
|
|
148
|
+
return { active: true, color: group.color };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { active: false };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const displayMainRows = variant === '60'
|
|
156
|
+
? [
|
|
157
|
+
[{ label: 'Esc', width: 1, code: 'Escape' }, ...(mainRows[1] || []).slice(1)],
|
|
158
|
+
...mainRows.slice(2)
|
|
159
|
+
]
|
|
160
|
+
: mainRows;
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
interface RenderKey extends Key {
|
|
164
|
+
x: number;
|
|
165
|
+
y: number;
|
|
166
|
+
w: number;
|
|
167
|
+
h: number;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function processRows(rows: Key[][], startX: number, startY: number): RenderKey[] {
|
|
171
|
+
const result: RenderKey[] = [];
|
|
172
|
+
let currentY = startY;
|
|
173
|
+
|
|
174
|
+
for (const row of rows) {
|
|
175
|
+
let currentX = startX;
|
|
176
|
+
for (const key of row) {
|
|
177
|
+
const w = key.width || 1;
|
|
178
|
+
const h = key.height || 1;
|
|
179
|
+
const widthPx = w * keySize + (w - 1) * keyGap;
|
|
180
|
+
const heightPx = h * keySize + (h - 1) * keyGap;
|
|
181
|
+
|
|
182
|
+
if (key.type !== 'spacer') {
|
|
183
|
+
result.push({
|
|
184
|
+
...key,
|
|
185
|
+
x: currentX,
|
|
186
|
+
y: currentY,
|
|
187
|
+
w: widthPx,
|
|
188
|
+
h: heightPx
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
currentX += widthPx + keyGap;
|
|
192
|
+
}
|
|
193
|
+
currentY += keySize + keyGap;
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const mainRenderKeys = processRows(displayMainRows, 0, 0);
|
|
199
|
+
const mainHeight = displayMainRows.length * (keySize + keyGap);
|
|
200
|
+
|
|
201
|
+
let secondaryKeys: RenderKey[] = [];
|
|
202
|
+
let numpadRenderKeys: RenderKey[] = [];
|
|
203
|
+
let totalWidth = (15 * keySize) + (14 * keyGap);
|
|
204
|
+
let finalHeight = mainHeight;
|
|
205
|
+
|
|
206
|
+
if (variant === 'full') {
|
|
207
|
+
const secondaryY = mainHeight + sectionGap;
|
|
208
|
+
secondaryKeys = processRows(functionRows, 0, secondaryY);
|
|
209
|
+
|
|
210
|
+
const numpadX = (3 * keySize) + (3 * keyGap) + sectionGap;
|
|
211
|
+
|
|
212
|
+
numpadKeys.forEach((key, i) => {
|
|
213
|
+
const k = key as Key;
|
|
214
|
+
const w = k.width || 1;
|
|
215
|
+
const h = k.height || 1;
|
|
216
|
+
|
|
217
|
+
const positions = [
|
|
218
|
+
[0,0], [1,0], [2,0], [3,0],
|
|
219
|
+
[0,1], [1,1], [2,1], [3,1],
|
|
220
|
+
[0,2], [1,2], [2,2],
|
|
221
|
+
[0,3], [1,3], [2,3], [3,3],
|
|
222
|
+
[0,4], [2,4]
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const pxOffset = positions[i]?.[0] ?? 0;
|
|
226
|
+
const pyOffset = positions[i]?.[1] ?? 0;
|
|
227
|
+
|
|
228
|
+
const px = numpadX + pxOffset * (keySize + keyGap);
|
|
229
|
+
const py = secondaryY + (pyOffset + 1) * (keySize + keyGap);
|
|
230
|
+
|
|
231
|
+
numpadRenderKeys.push({
|
|
232
|
+
...k,
|
|
233
|
+
x: px,
|
|
234
|
+
y: py,
|
|
235
|
+
w: w * keySize + (w - 1) * keyGap,
|
|
236
|
+
h: h * keySize + (h - 1) * keyGap
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
totalWidth = Math.max(totalWidth, numpadX + (4 * keySize) + (3 * keyGap));
|
|
241
|
+
finalHeight = secondaryY + (6 * keySize) + (5 * keyGap);
|
|
242
|
+
}
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
<div class="keyboard-wrapper">
|
|
246
|
+
<div class="keyboard-container">
|
|
247
|
+
<svg
|
|
248
|
+
viewBox={`-16 -16 ${totalWidth + 32} ${finalHeight + 64}`}
|
|
249
|
+
class="keyboard-svg"
|
|
250
|
+
preserveAspectRatio="xMidYMid meet"
|
|
251
|
+
>
|
|
252
|
+
{backgroundColor && backgroundColor !== 'transparent' && (
|
|
253
|
+
<rect
|
|
254
|
+
x="-16" y="-16"
|
|
255
|
+
width={totalWidth + 32}
|
|
256
|
+
height={finalHeight + 64}
|
|
257
|
+
fill={backgroundColor}
|
|
258
|
+
rx="12"
|
|
259
|
+
/>
|
|
260
|
+
)}
|
|
261
|
+
|
|
262
|
+
{[...mainRenderKeys, ...secondaryKeys, ...numpadRenderKeys].map((key) => {
|
|
263
|
+
const highlight = getKeyHighlight(key);
|
|
264
|
+
const color = highlight.active ? (highlight.color || highlightColor) : '#2a2a3e';
|
|
265
|
+
const label = getKeyLabel(key);
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<g class={`kb-key-group ${highlight.active ? 'highlighted' : ''}`}>
|
|
269
|
+
{/* Key Base / Shadow */}
|
|
270
|
+
<rect
|
|
271
|
+
x={key.x}
|
|
272
|
+
y={key.y + 1}
|
|
273
|
+
width={key.w}
|
|
274
|
+
height={key.h}
|
|
275
|
+
rx="4"
|
|
276
|
+
fill="#1a1a2e"
|
|
277
|
+
/>
|
|
278
|
+
{/* Key Face */}
|
|
279
|
+
<rect
|
|
280
|
+
x={key.x}
|
|
281
|
+
y={key.y}
|
|
282
|
+
width={key.w}
|
|
283
|
+
height={key.h}
|
|
284
|
+
rx="4"
|
|
285
|
+
fill={color}
|
|
286
|
+
stroke={highlight.active ? "#ffffff66" : "#3a3a4e"}
|
|
287
|
+
stroke-width="1"
|
|
288
|
+
/>
|
|
289
|
+
{label && (
|
|
290
|
+
<text
|
|
291
|
+
x={key.x + key.w / 2}
|
|
292
|
+
y={key.y + key.h / 2}
|
|
293
|
+
text-anchor="middle"
|
|
294
|
+
dominant-baseline="central"
|
|
295
|
+
class="kb-key-label"
|
|
296
|
+
fill={highlight.active ? "#fff" : "#e0e0e0"}
|
|
297
|
+
>
|
|
298
|
+
{label}
|
|
299
|
+
</text>
|
|
300
|
+
)}
|
|
301
|
+
</g>
|
|
302
|
+
);
|
|
303
|
+
})}
|
|
304
|
+
|
|
305
|
+
</svg>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
{groups.some(g => g.label) && (
|
|
309
|
+
<div class="flex flex-wrap justify-center gap-x-6 gap-y-3 mt-6 w-full">
|
|
310
|
+
{groups.filter(g => g.label).map(group => (
|
|
311
|
+
<div class="flex items-center gap-2 m-0 p-0 text-[10px] text-[#a0a0b0] uppercase tracking-[0.05em] font-medium whitespace-nowrap leading-none">
|
|
312
|
+
<svg width="8" height="8" class="flex-shrink-0 block">
|
|
313
|
+
<rect width="8" height="8" rx="2" fill={group.color || '#6366f1'} />
|
|
314
|
+
</svg>
|
|
315
|
+
<span class="m-0 p-0 leading-none inline-block">{group.label}</span>
|
|
316
|
+
</div>
|
|
317
|
+
))}
|
|
318
|
+
</div>
|
|
319
|
+
)}
|
|
320
|
+
</div>
|
|
321
|
+
|
|
@@ -1,126 +1,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"({
|
|
63
|
-
addIntegration,
|
|
64
|
-
updateConfig,
|
|
65
|
-
logger,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
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
|
+
}
|