@coffic/cosy-ui 0.8.22 → 0.8.24
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/dist/app.css +1 -1
- package/dist/index-astro.ts +3 -0
- package/dist/src-astro/code-panel/CodePanel.astro +109 -0
- package/dist/src-astro/code-panel/index.ts +2 -0
- package/dist/src-astro/code-panel/types.ts +17 -0
- package/dist/src-astro/grid/Grid.astro +274 -20
- package/dist/src-astro/install-tabs/ButtonCopyInstall.astro +102 -0
- package/dist/src-astro/install-tabs/InstallTabs.astro +174 -0
- package/dist/src-astro/install-tabs/index.ts +2 -0
- package/dist/src-astro/install-tabs/types.ts +8 -0
- package/dist/src-astro/layout-app/AppLayout.astro +1 -10
- package/dist/src-astro/layout-basic/BaseLayout.astro +20 -4
- package/dist/src-astro/list/ListItem.astro +45 -0
- package/dist/src-astro/list/index.ts +1 -0
- package/dist/src-astro/types/meta.ts +65 -53
- package/dist/src-vue/list/ListItem.vue +47 -2
- package/package.json +3 -2
package/dist/index-astro.ts
CHANGED
@@ -7,6 +7,8 @@ export * from './src-astro/card';
|
|
7
7
|
export * from './src-astro/code-block';
|
8
8
|
export * from './src-astro/code-container';
|
9
9
|
export * from './src-astro/code-example';
|
10
|
+
export * from './src-astro/code-panel';
|
11
|
+
export * from './src-astro/install-tabs';
|
10
12
|
export * from './src-astro/contact';
|
11
13
|
export * from './src-astro/container';
|
12
14
|
export * from './src-astro/errors';
|
@@ -44,6 +46,7 @@ export * from './src-astro/theme-switcher';
|
|
44
46
|
export * from './src-astro/toc';
|
45
47
|
export * from './src-astro/toast';
|
46
48
|
export * from './src-astro/confirm-dialog';
|
49
|
+
export * from './src-astro/list';
|
47
50
|
|
48
51
|
// 类型定义 (按字母顺序)
|
49
52
|
export * from './src-astro/types/article';
|
@@ -0,0 +1,109 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component CodePanel
|
4
|
+
*
|
5
|
+
* @description
|
6
|
+
* 代码展示面板组件,支持语法高亮、多主题切换和行号显示。
|
7
|
+
* 基于 Shiki 提供高质量的代码高亮效果,与 VS Code 保持一致的渲染效果。
|
8
|
+
* 支持服务端渲染,无需客户端 JavaScript 即可展示代码高亮。
|
9
|
+
*
|
10
|
+
* @features
|
11
|
+
* - 🎨 语法高亮:基于 Shiki,支持 180+ 编程语言
|
12
|
+
* - 🎭 多主题:支持 VS Code 主题(dark-plus、light-plus、github-dark 等)
|
13
|
+
* - 📏 行号显示:可选的行号显示功能
|
14
|
+
* - 🚀 性能优化:服务端渲染,避免客户端重复计算
|
15
|
+
* - 📱 响应式:自适应不同屏幕尺寸
|
16
|
+
* - 🎯 可访问性:语义化 HTML 结构,支持键盘导航
|
17
|
+
*
|
18
|
+
* @usage
|
19
|
+
* 基本用法:
|
20
|
+
* ```astro
|
21
|
+
* <CodePanel code="console.log('Hello, world!');" />
|
22
|
+
* ```
|
23
|
+
*
|
24
|
+
* 指定语言和主题:
|
25
|
+
* ```astro
|
26
|
+
* <CodePanel
|
27
|
+
* code="const name = 'TypeScript';"
|
28
|
+
* language="typescript"
|
29
|
+
* theme="github-dark"
|
30
|
+
* showLineNumbers={true}
|
31
|
+
* />
|
32
|
+
* ```
|
33
|
+
*
|
34
|
+
* 在切换面板中使用:
|
35
|
+
* ```astro
|
36
|
+
* <CodePanel
|
37
|
+
* code={sourceCode}
|
38
|
+
* language="astro"
|
39
|
+
* visible={false}
|
40
|
+
* />
|
41
|
+
* ```
|
42
|
+
*
|
43
|
+
* @props
|
44
|
+
* @param {string} code - 要显示的代码字符串
|
45
|
+
* @param {string} [language="typescript"] - 代码语言,用于语法高亮
|
46
|
+
* @param {string} [theme="dark-plus"] - 主题名称
|
47
|
+
* @param {boolean} [showLineNumbers=false] - 是否显示行号
|
48
|
+
* @param {boolean} [visible=true] - 是否显示在面板中
|
49
|
+
* @param {string} [class] - 自定义类名
|
50
|
+
*
|
51
|
+
* @slots
|
52
|
+
* 此组件不包含插槽,通过 props 传入代码内容
|
53
|
+
*
|
54
|
+
* @accessibility
|
55
|
+
* - 使用语义化的 HTML 结构
|
56
|
+
* - 代码区域具有适当的 aria 标签
|
57
|
+
* - 支持键盘导航和屏幕阅读器
|
58
|
+
* - 高对比度主题支持
|
59
|
+
*/
|
60
|
+
import type { CodePanelProps } from './types';
|
61
|
+
import '../../style.ts';
|
62
|
+
|
63
|
+
interface Props extends CodePanelProps {}
|
64
|
+
|
65
|
+
const {
|
66
|
+
code = '',
|
67
|
+
language = 'typescript',
|
68
|
+
theme = 'dark-plus',
|
69
|
+
showLineNumbers = false,
|
70
|
+
visible = true,
|
71
|
+
class: className = '',
|
72
|
+
} = Astro.props;
|
73
|
+
|
74
|
+
// 优化 Shiki 加载 - 使用动态导入避免构建时创建多个实例
|
75
|
+
let highlightedCode = code;
|
76
|
+
try {
|
77
|
+
// 使用动态导入避免构建时的性能问题
|
78
|
+
const { createHighlighter } = await import('shiki');
|
79
|
+
|
80
|
+
const highlighter = await createHighlighter({
|
81
|
+
themes: [theme],
|
82
|
+
langs: [language],
|
83
|
+
});
|
84
|
+
|
85
|
+
highlightedCode = highlighter.codeToHtml(code, {
|
86
|
+
lang: language,
|
87
|
+
theme: theme,
|
88
|
+
transformers: [],
|
89
|
+
});
|
90
|
+
|
91
|
+
// 释放资源
|
92
|
+
highlighter.dispose?.();
|
93
|
+
} catch (error) {
|
94
|
+
console.warn(
|
95
|
+
'CodePanel: Shiki highlighting failed, falling back to plain text:',
|
96
|
+
error
|
97
|
+
);
|
98
|
+
// 降级到普通代码块
|
99
|
+
highlightedCode = `<pre><code>${code}</code></pre>`;
|
100
|
+
}
|
101
|
+
---
|
102
|
+
|
103
|
+
<div data-panel="code" role="region" aria-label="代码展示面板">
|
104
|
+
<!-- Shiki 渲染的代码 -->
|
105
|
+
<div
|
106
|
+
class="cosy:w-full not-prose cosy-shiki-container"
|
107
|
+
set:html={highlightedCode}
|
108
|
+
/>
|
109
|
+
</div>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
/**
|
2
|
+
* CodePanel 组件的属性接口
|
3
|
+
*/
|
4
|
+
export interface CodePanelProps {
|
5
|
+
/** 要显示的代码字符串 */
|
6
|
+
code: string;
|
7
|
+
/** 代码语言,用于语法高亮,默认 'typescript' */
|
8
|
+
language?: string;
|
9
|
+
/** 主题名称,默认 'dark-plus' */
|
10
|
+
theme?: 'dark-plus' | 'light-plus' | 'github-dark' | 'github-light' | 'nord' | 'dracula';
|
11
|
+
/** 是否显示行号,默认 false */
|
12
|
+
showLineNumbers?: boolean;
|
13
|
+
/** 是否显示在面板中(用于切换),默认 true */
|
14
|
+
visible?: boolean;
|
15
|
+
/** 自定义类名 */
|
16
|
+
class?: string;
|
17
|
+
}
|
@@ -114,29 +114,283 @@ const {
|
|
114
114
|
...rest
|
115
115
|
} = Astro.props;
|
116
116
|
|
117
|
-
//
|
117
|
+
// 处理响应式列数(所有 class 必须静态字符串,不能拼接)
|
118
118
|
const getColsClasses = (cols: ResponsiveValue<number>) => {
|
119
|
+
// 单一数字
|
119
120
|
if (typeof cols === 'number') {
|
120
|
-
|
121
|
+
switch (cols) {
|
122
|
+
case 1:
|
123
|
+
return 'cosy:grid-cols-1';
|
124
|
+
case 2:
|
125
|
+
return 'cosy:grid-cols-2';
|
126
|
+
case 3:
|
127
|
+
return 'cosy:grid-cols-3';
|
128
|
+
case 4:
|
129
|
+
return 'cosy:grid-cols-4';
|
130
|
+
case 5:
|
131
|
+
return 'cosy:grid-cols-5';
|
132
|
+
case 6:
|
133
|
+
return 'cosy:grid-cols-6';
|
134
|
+
case 7:
|
135
|
+
return 'cosy:grid-cols-7';
|
136
|
+
case 8:
|
137
|
+
return 'cosy:grid-cols-8';
|
138
|
+
case 9:
|
139
|
+
return 'cosy:grid-cols-9';
|
140
|
+
case 10:
|
141
|
+
return 'cosy:grid-cols-10';
|
142
|
+
case 11:
|
143
|
+
return 'cosy:grid-cols-11';
|
144
|
+
case 12:
|
145
|
+
return 'cosy:grid-cols-12';
|
146
|
+
default:
|
147
|
+
return '';
|
148
|
+
}
|
121
149
|
}
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
150
|
+
// 响应式对象
|
151
|
+
let result: string[] = [];
|
152
|
+
const responsive = cols as any;
|
153
|
+
if (responsive.base) {
|
154
|
+
switch (responsive.base) {
|
155
|
+
case 1:
|
156
|
+
result.push('cosy:grid-cols-1');
|
157
|
+
break;
|
158
|
+
case 2:
|
159
|
+
result.push('cosy:grid-cols-2');
|
160
|
+
break;
|
161
|
+
case 3:
|
162
|
+
result.push('cosy:grid-cols-3');
|
163
|
+
break;
|
164
|
+
case 4:
|
165
|
+
result.push('cosy:grid-cols-4');
|
166
|
+
break;
|
167
|
+
case 5:
|
168
|
+
result.push('cosy:grid-cols-5');
|
169
|
+
break;
|
170
|
+
case 6:
|
171
|
+
result.push('cosy:grid-cols-6');
|
172
|
+
break;
|
173
|
+
case 7:
|
174
|
+
result.push('cosy:grid-cols-7');
|
175
|
+
break;
|
176
|
+
case 8:
|
177
|
+
result.push('cosy:grid-cols-8');
|
178
|
+
break;
|
179
|
+
case 9:
|
180
|
+
result.push('cosy:grid-cols-9');
|
181
|
+
break;
|
182
|
+
case 10:
|
183
|
+
result.push('cosy:grid-cols-10');
|
184
|
+
break;
|
185
|
+
case 11:
|
186
|
+
result.push('cosy:grid-cols-11');
|
187
|
+
break;
|
188
|
+
case 12:
|
189
|
+
result.push('cosy:grid-cols-12');
|
190
|
+
break;
|
191
|
+
}
|
192
|
+
}
|
193
|
+
if (responsive.sm) {
|
194
|
+
switch (responsive.sm) {
|
195
|
+
case 1:
|
196
|
+
result.push('cosy:sm:grid-cols-1');
|
197
|
+
break;
|
198
|
+
case 2:
|
199
|
+
result.push('cosy:sm:grid-cols-2');
|
200
|
+
break;
|
201
|
+
case 3:
|
202
|
+
result.push('cosy:sm:grid-cols-3');
|
203
|
+
break;
|
204
|
+
case 4:
|
205
|
+
result.push('cosy:sm:grid-cols-4');
|
206
|
+
break;
|
207
|
+
case 5:
|
208
|
+
result.push('cosy:sm:grid-cols-5');
|
209
|
+
break;
|
210
|
+
case 6:
|
211
|
+
result.push('cosy:sm:grid-cols-6');
|
212
|
+
break;
|
213
|
+
case 7:
|
214
|
+
result.push('cosy:sm:grid-cols-7');
|
215
|
+
break;
|
216
|
+
case 8:
|
217
|
+
result.push('cosy:sm:grid-cols-8');
|
218
|
+
break;
|
219
|
+
case 9:
|
220
|
+
result.push('cosy:sm:grid-cols-9');
|
221
|
+
break;
|
222
|
+
case 10:
|
223
|
+
result.push('cosy:sm:grid-cols-10');
|
224
|
+
break;
|
225
|
+
case 11:
|
226
|
+
result.push('cosy:sm:grid-cols-11');
|
227
|
+
break;
|
228
|
+
case 12:
|
229
|
+
result.push('cosy:sm:grid-cols-12');
|
230
|
+
break;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
if (responsive.md) {
|
234
|
+
switch (responsive.md) {
|
235
|
+
case 1:
|
236
|
+
result.push('cosy:md:grid-cols-1');
|
237
|
+
break;
|
238
|
+
case 2:
|
239
|
+
result.push('cosy:md:grid-cols-2');
|
240
|
+
break;
|
241
|
+
case 3:
|
242
|
+
result.push('cosy:md:grid-cols-3');
|
243
|
+
break;
|
244
|
+
case 4:
|
245
|
+
result.push('cosy:md:grid-cols-4');
|
246
|
+
break;
|
247
|
+
case 5:
|
248
|
+
result.push('cosy:md:grid-cols-5');
|
249
|
+
break;
|
250
|
+
case 6:
|
251
|
+
result.push('cosy:md:grid-cols-6');
|
252
|
+
break;
|
253
|
+
case 7:
|
254
|
+
result.push('cosy:md:grid-cols-7');
|
255
|
+
break;
|
256
|
+
case 8:
|
257
|
+
result.push('cosy:md:grid-cols-8');
|
258
|
+
break;
|
259
|
+
case 9:
|
260
|
+
result.push('cosy:md:grid-cols-9');
|
261
|
+
break;
|
262
|
+
case 10:
|
263
|
+
result.push('cosy:md:grid-cols-10');
|
264
|
+
break;
|
265
|
+
case 11:
|
266
|
+
result.push('cosy:md:grid-cols-11');
|
267
|
+
break;
|
268
|
+
case 12:
|
269
|
+
result.push('cosy:md:grid-cols-12');
|
270
|
+
break;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
if (responsive.lg) {
|
274
|
+
switch (responsive.lg) {
|
275
|
+
case 1:
|
276
|
+
result.push('cosy:lg:grid-cols-1');
|
277
|
+
break;
|
278
|
+
case 2:
|
279
|
+
result.push('cosy:lg:grid-cols-2');
|
280
|
+
break;
|
281
|
+
case 3:
|
282
|
+
result.push('cosy:lg:grid-cols-3');
|
283
|
+
break;
|
284
|
+
case 4:
|
285
|
+
result.push('cosy:lg:grid-cols-4');
|
286
|
+
break;
|
287
|
+
case 5:
|
288
|
+
result.push('cosy:lg:grid-cols-5');
|
289
|
+
break;
|
290
|
+
case 6:
|
291
|
+
result.push('cosy:lg:grid-cols-6');
|
292
|
+
break;
|
293
|
+
case 7:
|
294
|
+
result.push('cosy:lg:grid-cols-7');
|
295
|
+
break;
|
296
|
+
case 8:
|
297
|
+
result.push('cosy:lg:grid-cols-8');
|
298
|
+
break;
|
299
|
+
case 9:
|
300
|
+
result.push('cosy:lg:grid-cols-9');
|
301
|
+
break;
|
302
|
+
case 10:
|
303
|
+
result.push('cosy:lg:grid-cols-10');
|
304
|
+
break;
|
305
|
+
case 11:
|
306
|
+
result.push('cosy:lg:grid-cols-11');
|
307
|
+
break;
|
308
|
+
case 12:
|
309
|
+
result.push('cosy:lg:grid-cols-12');
|
310
|
+
break;
|
311
|
+
}
|
312
|
+
}
|
313
|
+
if (responsive.xl) {
|
314
|
+
switch (responsive.xl) {
|
315
|
+
case 1:
|
316
|
+
result.push('cosy:xl:grid-cols-1');
|
317
|
+
break;
|
318
|
+
case 2:
|
319
|
+
result.push('cosy:xl:grid-cols-2');
|
320
|
+
break;
|
321
|
+
case 3:
|
322
|
+
result.push('cosy:xl:grid-cols-3');
|
323
|
+
break;
|
324
|
+
case 4:
|
325
|
+
result.push('cosy:xl:grid-cols-4');
|
326
|
+
break;
|
327
|
+
case 5:
|
328
|
+
result.push('cosy:xl:grid-cols-5');
|
329
|
+
break;
|
330
|
+
case 6:
|
331
|
+
result.push('cosy:xl:grid-cols-6');
|
332
|
+
break;
|
333
|
+
case 7:
|
334
|
+
result.push('cosy:xl:grid-cols-7');
|
335
|
+
break;
|
336
|
+
case 8:
|
337
|
+
result.push('cosy:xl:grid-cols-8');
|
338
|
+
break;
|
339
|
+
case 9:
|
340
|
+
result.push('cosy:xl:grid-cols-9');
|
341
|
+
break;
|
342
|
+
case 10:
|
343
|
+
result.push('cosy:xl:grid-cols-10');
|
344
|
+
break;
|
345
|
+
case 11:
|
346
|
+
result.push('cosy:xl:grid-cols-11');
|
347
|
+
break;
|
348
|
+
case 12:
|
349
|
+
result.push('cosy:xl:grid-cols-12');
|
350
|
+
break;
|
351
|
+
}
|
352
|
+
}
|
353
|
+
if (responsive['2xl']) {
|
354
|
+
switch (responsive['2xl']) {
|
355
|
+
case 1:
|
356
|
+
result.push('cosy:2xl:grid-cols-1');
|
357
|
+
break;
|
358
|
+
case 2:
|
359
|
+
result.push('cosy:2xl:grid-cols-2');
|
360
|
+
break;
|
361
|
+
case 3:
|
362
|
+
result.push('cosy:2xl:grid-cols-3');
|
363
|
+
break;
|
364
|
+
case 4:
|
365
|
+
result.push('cosy:2xl:grid-cols-4');
|
366
|
+
break;
|
367
|
+
case 5:
|
368
|
+
result.push('cosy:2xl:grid-cols-5');
|
369
|
+
break;
|
370
|
+
case 6:
|
371
|
+
result.push('cosy:2xl:grid-cols-6');
|
372
|
+
break;
|
373
|
+
case 7:
|
374
|
+
result.push('cosy:2xl:grid-cols-7');
|
375
|
+
break;
|
376
|
+
case 8:
|
377
|
+
result.push('cosy:2xl:grid-cols-8');
|
378
|
+
break;
|
379
|
+
case 9:
|
380
|
+
result.push('cosy:2xl:grid-cols-9');
|
381
|
+
break;
|
382
|
+
case 10:
|
383
|
+
result.push('cosy:2xl:grid-cols-10');
|
384
|
+
break;
|
385
|
+
case 11:
|
386
|
+
result.push('cosy:2xl:grid-cols-11');
|
387
|
+
break;
|
388
|
+
case 12:
|
389
|
+
result.push('cosy:2xl:grid-cols-12');
|
390
|
+
break;
|
391
|
+
}
|
392
|
+
}
|
393
|
+
return result.join(' ');
|
140
394
|
};
|
141
395
|
|
142
396
|
// 间距映射
|
@@ -0,0 +1,102 @@
|
|
1
|
+
---
|
2
|
+
import { ClipboardIcon } from '../icons';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* 安装命令复制按钮,带气泡toast反馈
|
6
|
+
* @param code 要复制的命令字符串
|
7
|
+
*/
|
8
|
+
interface Props {
|
9
|
+
code: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
const { code } = Astro.props;
|
13
|
+
const uniqueId = `copy-install-${Math.random().toString(36).substr(2, 9)}`;
|
14
|
+
const toastId = `toast-${uniqueId}`;
|
15
|
+
---
|
16
|
+
|
17
|
+
<button
|
18
|
+
class="cosy:gap-2 cosy:btn cosy:btn-ghost cosy:btn-sm"
|
19
|
+
aria-label="复制安装命令"
|
20
|
+
type="button"
|
21
|
+
style="position: relative;"
|
22
|
+
id={uniqueId}
|
23
|
+
data-code={code}>
|
24
|
+
<span class="cosy:copy-icon"><ClipboardIcon /></span>
|
25
|
+
<span
|
26
|
+
id={toastId}
|
27
|
+
style="
|
28
|
+
display: none;
|
29
|
+
position: fixed;
|
30
|
+
top: 50%;
|
31
|
+
left: 50%;
|
32
|
+
transform: translate(-50%, -50%);
|
33
|
+
background: #22c55e;
|
34
|
+
color: #fff;
|
35
|
+
padding: 0.75rem 1.25rem;
|
36
|
+
border-radius: 0.5rem;
|
37
|
+
font-size: 0.875rem;
|
38
|
+
font-weight: 500;
|
39
|
+
white-space: nowrap;
|
40
|
+
z-index: 99999;
|
41
|
+
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
|
42
|
+
pointer-events: none;
|
43
|
+
transition: all 0.2s ease;
|
44
|
+
">
|
45
|
+
复制成功 ✓
|
46
|
+
</span>
|
47
|
+
</button>
|
48
|
+
|
49
|
+
<script>
|
50
|
+
function initializeCopyInstall() {
|
51
|
+
console.log('InstallTabs: 初始化复制按钮');
|
52
|
+
|
53
|
+
document.querySelectorAll('[id^="copy-install-"]').forEach((btn) => {
|
54
|
+
// 防止重复绑定事件
|
55
|
+
if (btn.hasAttribute('data-initialized')) return;
|
56
|
+
btn.setAttribute('data-initialized', 'true');
|
57
|
+
|
58
|
+
btn.addEventListener('click', () => {
|
59
|
+
const code = btn.getAttribute('data-code') || '';
|
60
|
+
const toastId = `toast-${btn.id}`;
|
61
|
+
const toast = document.getElementById(toastId);
|
62
|
+
|
63
|
+
console.log('InstallTabs: 点击复制按钮', { code, toastId, toast });
|
64
|
+
|
65
|
+
navigator.clipboard
|
66
|
+
.writeText(code)
|
67
|
+
.then(() => {
|
68
|
+
console.log('InstallTabs: 复制成功', code);
|
69
|
+
if (toast && toast instanceof HTMLElement) {
|
70
|
+
console.log('InstallTabs: 显示toast');
|
71
|
+
// 确保toast在最前面
|
72
|
+
document.body.appendChild(toast);
|
73
|
+
|
74
|
+
toast.style.display = 'block';
|
75
|
+
toast.style.opacity = '1';
|
76
|
+
toast.style.visibility = 'visible';
|
77
|
+
|
78
|
+
setTimeout(() => {
|
79
|
+
toast.style.opacity = '0';
|
80
|
+
setTimeout(() => {
|
81
|
+
toast.style.display = 'none';
|
82
|
+
toast.style.visibility = 'hidden';
|
83
|
+
// 移回原位
|
84
|
+
btn.appendChild(toast);
|
85
|
+
}, 200);
|
86
|
+
}, 2000);
|
87
|
+
} else {
|
88
|
+
console.error('InstallTabs: 无法找到toast元素', toastId);
|
89
|
+
}
|
90
|
+
})
|
91
|
+
.catch((err) => {
|
92
|
+
console.error('InstallTabs: 复制失败', err);
|
93
|
+
});
|
94
|
+
});
|
95
|
+
});
|
96
|
+
}
|
97
|
+
|
98
|
+
document.addEventListener('astro:page-load', () => {
|
99
|
+
console.log('InstallTabs: 页面加载完成,初始化复制按钮');
|
100
|
+
initializeCopyInstall();
|
101
|
+
});
|
102
|
+
</script>
|