@rimelight/ui 0.0.20 → 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.
- package/package.json +9 -3
- package/src/components/Head.astro +3 -0
- package/src/components/astro/RLAFooter.astro +69 -0
- package/src/components/astro/RLAHeader.astro +178 -75
- package/src/components/astro/RLAScrollToTop.astro +221 -0
- package/src/components/vue/RLVFooter.vue +81 -0
- package/src/components/vue/RLVHeader.vue +140 -18
- package/src/components/vue/RLVScrollToTop.vue +214 -0
- package/src/composables/index.ts +1 -0
- package/src/composables/useScrollToTop.ts +62 -0
- package/src/integrations/ui.ts +3 -2
- package/src/plugins/starlightAddons/PackageManagers.astro +4 -4
- 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 +1 -7
- package/src/styles/index.css +153 -0
- package/src/themes/footer.theme.ts +29 -0
- package/src/themes/header.theme.ts +32 -2
- package/src/themes/index.ts +7 -3
- package/src/themes/scroll-to-top.theme.ts +48 -0
- package/src/utils/headerStack.ts +88 -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
|
+
|
|
@@ -59,13 +59,7 @@ export default function starlightAddons(userConfig?: StarlightAddonsConfig): Sta
|
|
|
59
59
|
return {
|
|
60
60
|
name: "rimelight-ui-starlight-addons",
|
|
61
61
|
hooks: {
|
|
62
|
-
"config:setup"({
|
|
63
|
-
addIntegration,
|
|
64
|
-
updateConfig,
|
|
65
|
-
logger,
|
|
66
|
-
command,
|
|
67
|
-
config: starlightConfig
|
|
68
|
-
}) {
|
|
62
|
+
"config:setup"({ addIntegration, updateConfig, logger, command, config: starlightConfig }) {
|
|
69
63
|
// --- Sidebar Topics Logic ---
|
|
70
64
|
if ((command === "dev" || command === "build") && topicsData.length > 0) {
|
|
71
65
|
if (starlightConfig.sidebar) {
|
package/src/styles/index.css
CHANGED
|
@@ -1,3 +1,156 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
@import "@nuxt/ui";
|
|
3
3
|
@import "./prism.css";
|
|
4
|
+
|
|
5
|
+
/* Keyboard Component Styles */
|
|
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;
|
|
16
|
+
}
|
|
17
|
+
.keyboard-container {
|
|
18
|
+
width: 100%;
|
|
19
|
+
display: flex;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
.keyboard-svg {
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: auto;
|
|
25
|
+
max-width: 900px;
|
|
26
|
+
}
|
|
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;
|
|
33
|
+
}
|
|
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%;
|
|
41
|
+
}
|
|
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;
|
|
55
|
+
}
|
|
56
|
+
.legend-color-svg {
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
display: block;
|
|
59
|
+
}
|
|
60
|
+
.keyboard-wrapper .legend-label {
|
|
61
|
+
margin: 0 !important;
|
|
62
|
+
padding: 0 !important;
|
|
63
|
+
line-height: 1 !important;
|
|
64
|
+
display: inline-block !important;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Gamepad Component Styles */
|
|
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;
|
|
80
|
+
}
|
|
81
|
+
.gamepad-container {
|
|
82
|
+
width: 100%;
|
|
83
|
+
max-width: 440px;
|
|
84
|
+
}
|
|
85
|
+
.gamepad-svg {
|
|
86
|
+
width: 100%;
|
|
87
|
+
height: auto;
|
|
88
|
+
}
|
|
89
|
+
.gp-btn rect,
|
|
90
|
+
.gp-btn circle {
|
|
91
|
+
fill: #1e1e30;
|
|
92
|
+
stroke: #33334d;
|
|
93
|
+
stroke-width: 1;
|
|
94
|
+
transition: all 0.2s ease;
|
|
95
|
+
}
|
|
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;
|
|
103
|
+
}
|
|
104
|
+
.gp-btn.active rect,
|
|
105
|
+
.gp-btn.active circle {
|
|
106
|
+
fill: var(--hl-color, #00c16a);
|
|
107
|
+
stroke: #ffffff33;
|
|
108
|
+
stroke-width: 1.5;
|
|
109
|
+
}
|
|
110
|
+
.gp-btn.active .btn-label {
|
|
111
|
+
fill: #fff;
|
|
112
|
+
}
|
|
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%;
|
|
120
|
+
}
|
|
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;
|
|
134
|
+
}
|
|
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;
|
|
141
|
+
}
|
|
142
|
+
|
|
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;
|
|
149
|
+
}
|
|
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
|
+
}
|
|
@@ -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
|
package/src/themes/index.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { buttonTheme } from "./button.theme"
|
|
2
1
|
import { headerTheme } from "./header.theme"
|
|
2
|
+
import { footerTheme } from "./footer.theme"
|
|
3
|
+
import { scrollToTopTheme } from "./scroll-to-top.theme"
|
|
4
|
+
import { buttonTheme } from "./button.theme"
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Default UI configuration for all components. This is the baseline that gets merged with
|
|
6
8
|
* user-provided overrides via the Astro integration options.
|
|
7
9
|
*/
|
|
8
10
|
export const defaultUIConfig = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
"header": headerTheme,
|
|
12
|
+
"footer": footerTheme,
|
|
13
|
+
"scroll-to-top": scrollToTopTheme,
|
|
14
|
+
"button": buttonTheme
|
|
11
15
|
} as const
|
|
12
16
|
|
|
13
17
|
export type DefaultUIConfig = typeof defaultUIConfig
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme configuration for the ScrollToTop component. Defines the slots, base styles, and variants
|
|
3
|
+
* for the scroll-to-top button with progress indicator.
|
|
4
|
+
*/
|
|
5
|
+
export const scrollToTopTheme = {
|
|
6
|
+
slots: ["root", "button", "progressBase", "svg", "iconContainer", "icon"],
|
|
7
|
+
base: {
|
|
8
|
+
root: "fixed bottom-6 right-6 z-50",
|
|
9
|
+
button: "size-14 lg:size-12 p-0 rounded-full",
|
|
10
|
+
progressBase: "progress-circle-base size-full",
|
|
11
|
+
svg: "size-full",
|
|
12
|
+
iconContainer: "absolute inset-0 flex items-center justify-center text-center",
|
|
13
|
+
icon: "size-6 text-white"
|
|
14
|
+
},
|
|
15
|
+
variants: {
|
|
16
|
+
color: {
|
|
17
|
+
primary: {
|
|
18
|
+
progressBase: "",
|
|
19
|
+
svg: "",
|
|
20
|
+
iconContainer: "",
|
|
21
|
+
icon: ""
|
|
22
|
+
},
|
|
23
|
+
secondary: {
|
|
24
|
+
progressBase: "",
|
|
25
|
+
svg: "",
|
|
26
|
+
iconContainer: "",
|
|
27
|
+
icon: ""
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
compoundVariants: [
|
|
32
|
+
{
|
|
33
|
+
color: "primary",
|
|
34
|
+
classNames: {
|
|
35
|
+
icon: "text-primary-foreground"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
color: "secondary",
|
|
40
|
+
classNames: {
|
|
41
|
+
icon: "text-secondary-foreground"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
defaultVariants: {
|
|
46
|
+
color: "primary"
|
|
47
|
+
}
|
|
48
|
+
} as const
|