@coffic/cosy-ui 0.8.26 → 0.8.28

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 (31) hide show
  1. package/dist/app.css +1 -1
  2. package/dist/index-astro.ts +1 -0
  3. package/dist/index-vue.ts +18 -0
  4. package/dist/src-astro/badge/Badge.astro +67 -0
  5. package/dist/src-astro/badge/index.ts +1 -0
  6. package/dist/src-astro/code-container/CodeContainer.astro +4 -2
  7. package/dist/src-astro/team-member/TeamMember.astro +36 -4
  8. package/dist/src-astro/team-member/index.ts +3 -26
  9. package/dist/src-vue/badge/Badge.vue +66 -0
  10. package/dist/src-vue/badge/index.ts +1 -0
  11. package/dist/src-vue/buttons/ButtonFolder.vue +98 -0
  12. package/dist/src-vue/buttons/ButtonRefresh.vue +163 -0
  13. package/dist/src-vue/buttons/index.ts +2 -0
  14. package/dist/src-vue/card/Card.vue +52 -0
  15. package/dist/src-vue/card/CardCourse.vue +34 -0
  16. package/dist/src-vue/card/index.ts +2 -0
  17. package/dist/src-vue/key-catcher/KeyCatcher.vue +121 -0
  18. package/dist/src-vue/key-catcher/index.ts +1 -0
  19. package/dist/src-vue/progress/Progress.vue +68 -0
  20. package/dist/src-vue/progress/index.ts +1 -0
  21. package/dist/src-vue/status-bar/StatusBar.vue +101 -0
  22. package/dist/src-vue/status-bar/StatusBarItem.vue +97 -0
  23. package/dist/src-vue/status-bar/index.ts +2 -0
  24. package/dist/src-vue/tool-bar/ToolBar.vue +95 -0
  25. package/dist/src-vue/tool-bar/index.ts +1 -0
  26. package/package.json +1 -1
  27. package/dist/src-astro/code-container/CodePanel.astro +0 -14
  28. package/dist/src-astro/team-member/TeamMemberBasic.astro +0 -25
  29. package/dist/src-astro/team-member/TeamMemberCustomStyle.astro +0 -26
  30. package/dist/src-astro/team-member/TeamMemberGroup.astro +0 -60
  31. package/dist/src-astro/team-member/TeamMemberWithSocial.astro +0 -30
@@ -0,0 +1,121 @@
1
+ <!--
2
+ @component KeyCatcher
3
+
4
+ @description
5
+ KeyCatcher 组件用于全局捕获键盘按键事件,并可通过自定义事件通知外部。支持可选的按键展示浮窗。
6
+
7
+ @usage
8
+ 基本用法:
9
+ ```vue
10
+ <KeyCatcher />
11
+ ```
12
+
13
+ 显示最近按下的按键:
14
+ ```vue
15
+ <KeyCatcher :showKey="true" />
16
+ ```
17
+
18
+ 监听全局按键事件:
19
+ ```vue
20
+ <KeyCatcher @globalKey="onGlobalKey" />
21
+ ```
22
+
23
+ @props
24
+ @prop {boolean} [showKey=false] - 是否显示最近按下的按键浮窗
25
+
26
+ @events
27
+ @event globalKey - 当捕获到全局按键时触发,参数为按键字符串
28
+
29
+ @slots
30
+
31
+ -->
32
+
33
+ <script setup lang="ts">
34
+ import { ref, onMounted, onUnmounted } from 'vue';
35
+ import '../../style';
36
+
37
+ const lastKey = ref<string | null>(null);
38
+ let timer: ReturnType<typeof setTimeout> | null = null;
39
+
40
+ const props = defineProps<{ showKey?: boolean }>();
41
+ const emit = defineEmits<{ (e: 'globalKey', key: string): void }>();
42
+
43
+ const handleKeydown = (event: KeyboardEvent) => {
44
+ const tag = (event.target as HTMLElement)?.tagName?.toLowerCase();
45
+ const isEditable =
46
+ tag === 'input' ||
47
+ tag === 'textarea' ||
48
+ (event.target as HTMLElement)?.isContentEditable;
49
+ if (
50
+ event.key.length === 1 ||
51
+ [
52
+ 'Enter',
53
+ 'Escape',
54
+ 'Backspace',
55
+ 'Tab',
56
+ 'Shift',
57
+ 'Control',
58
+ 'Alt',
59
+ 'Meta',
60
+ 'ArrowUp',
61
+ 'ArrowDown',
62
+ 'ArrowLeft',
63
+ 'ArrowRight',
64
+ 'CapsLock',
65
+ 'Delete',
66
+ 'Home',
67
+ 'End',
68
+ 'PageUp',
69
+ 'PageDown',
70
+ ].includes(event.key)
71
+ ) {
72
+ // 只在不是输入框、textarea、contenteditable 时发事件
73
+ if (/^[a-zA-Z]$/.test(event.key) && !isEditable) {
74
+ emit('globalKey', event.key);
75
+ }
76
+ // 展示按键(如果允许)
77
+ if (props.showKey) {
78
+ let key = event.key;
79
+ if (key === ' ') key = 'Space';
80
+ lastKey.value = key;
81
+ if (timer) clearTimeout(timer);
82
+ timer = setTimeout(() => {
83
+ lastKey.value = null;
84
+ }, 3000);
85
+ }
86
+ }
87
+ };
88
+
89
+ onMounted(() => {
90
+ window.addEventListener('keydown', handleKeydown);
91
+ });
92
+
93
+ onUnmounted(() => {
94
+ window.removeEventListener('keydown', handleKeydown);
95
+ if (timer) clearTimeout(timer);
96
+ });
97
+ </script>
98
+
99
+ <template>
100
+ <Transition name="key-fade">
101
+ <div v-if="props.showKey && lastKey"
102
+ class="cosy:fixed cosy:bottom-4 cosy:right-4 cosy:bg-accent cosy:shadow-lg cosy:rounded cosy:px-6 cosy:py-3 cosy:z-50 cosy:text-xl cosy:font-bold cosy:text-gray-800 cosy:select-none cosy:pointer-events-none cosy:border cosy:border-gray-200 cosy:backdrop-blur-sm">
103
+ <span class="cosy:text-blue-600">{{ lastKey }}</span>
104
+ </div>
105
+ </Transition>
106
+ </template>
107
+
108
+ <style scoped>
109
+ .key-fade-enter-active,
110
+ .key-fade-leave-active {
111
+ transition:
112
+ opacity 0.2s,
113
+ transform 0.2s;
114
+ }
115
+
116
+ .key-fade-enter-from,
117
+ .key-fade-leave-to {
118
+ opacity: 0;
119
+ transform: translateY(20px);
120
+ }
121
+ </style>
@@ -0,0 +1 @@
1
+ export { default as KeyCatcher } from './KeyCatcher.vue';
@@ -0,0 +1,68 @@
1
+ <!--
2
+ Progress 组件
3
+
4
+ 基于DaisyUI的进度条组件,支持多种颜色主题和进度值动态更新。
5
+ 当不传入value时,将显示不确定进度的动画效果。
6
+
7
+ 使用示例:
8
+ ```vue
9
+ <Progress />
10
+ <Progress :value="40" />
11
+ <Progress :value="70" color="primary" />
12
+ <Progress :value="30" color="secondary" />
13
+ <Progress :value="50" color="accent" />
14
+ <Progress :value="60" color="info" />
15
+ <Progress :value="80" color="success" />
16
+ <Progress :value="90" color="warning" />
17
+ <Progress :value="20" color="error" />
18
+ ```
19
+
20
+ 属性说明:
21
+ - value: 当前进度值
22
+ - 类型: number
23
+ - 默认值: undefined (不传入时显示不确定进度动画)
24
+ - max: 最大值
25
+ - 类型: number
26
+ - 默认值: 100
27
+ - color: 颜色主题
28
+ - 可选值: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'
29
+ - 默认值: 'neutral'
30
+ -->
31
+
32
+ <script setup lang="ts">
33
+ import { computed } from 'vue'
34
+ import '../../style';
35
+
36
+ interface Props {
37
+ // 当前进度值
38
+ value?: number
39
+ // 最大值
40
+ max?: number
41
+ // 颜色主题
42
+ color?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'
43
+ }
44
+
45
+ const props = withDefaults(defineProps<Props>(), {
46
+ value: undefined,
47
+ max: 100,
48
+ color: 'neutral'
49
+ })
50
+
51
+ // 计算进度条的类名(不能用字符串拼接,需枚举所有可能)
52
+ const progressClass = computed(() => {
53
+ const classes = ['cosy:progress', 'cosy:w-56']
54
+ if (props.color === 'primary') classes.push('cosy:progress-primary')
55
+ else if (props.color === 'secondary') classes.push('cosy:progress-secondary')
56
+ else if (props.color === 'accent') classes.push('cosy:progress-accent')
57
+ else if (props.color === 'info') classes.push('cosy:progress-info')
58
+ else if (props.color === 'success') classes.push('cosy:progress-success')
59
+ else if (props.color === 'warning') classes.push('cosy:progress-warning')
60
+ else if (props.color === 'error') classes.push('cosy:progress-error')
61
+ // neutral 不加变体类
62
+ return classes.join(' ')
63
+ })
64
+ </script>
65
+
66
+ <template>
67
+ <progress :class="progressClass" :value="value" :max="max"></progress>
68
+ </template>
@@ -0,0 +1 @@
1
+ export { default as Progress } from './Progress.vue';
@@ -0,0 +1,101 @@
1
+ <!--
2
+ StatusBar 组件
3
+
4
+ 一个基于DaisyUI的底部状态栏组件,类似VSCode底部状态栏,采用Raycast风格设计。
5
+ 支持左右两侧内容布局,可以放置按钮、图标和文字等元素。
6
+
7
+ 使用示例:
8
+ ```vue
9
+ <StatusBar>
10
+ <template #left>
11
+ <span>左侧状态信息</span>
12
+ </template>
13
+ <template #right>
14
+ <span>右侧状态信息</span>
15
+ </template>
16
+ </StatusBar>
17
+
18
+ <StatusBar>
19
+ <template #left>
20
+ <StatusBar.Item>
21
+ <i class="i-carbon-information"></i>
22
+ <span>分支: main</span>
23
+ </StatusBar.Item>
24
+ <StatusBar.Item>
25
+ <i class="i-carbon-git-commit"></i>
26
+ <span>3 changes</span>
27
+ </StatusBar.Item>
28
+ </template>
29
+ <template #right>
30
+ <StatusBar.Item clickable @click="handleClick">
31
+ <i class="i-carbon-checkmark"></i>
32
+ <span>Ready</span>
33
+ </StatusBar.Item>
34
+ </template>
35
+ </StatusBar>
36
+ ```
37
+
38
+ 属性说明:
39
+ - variant: 状态栏变体
40
+ - 可选值: 'default' | 'compact'
41
+ - 默认值: 'default'
42
+ - bordered: 是否显示上边框
43
+ - 类型: boolean
44
+ - 默认值: true
45
+
46
+ 插槽:
47
+ - left: 左侧内容区域
48
+ - right: 右侧内容区域
49
+ -->
50
+
51
+ <script setup lang="ts">
52
+ import { computed } from 'vue';
53
+ import '../../style';
54
+
55
+ interface Props {
56
+ // 状态栏变体
57
+ variant?: 'default' | 'compact'
58
+ // 是否显示上边框
59
+ bordered?: boolean
60
+ }
61
+
62
+ const props = withDefaults(defineProps<Props>(), {
63
+ variant: 'default',
64
+ bordered: true
65
+ })
66
+
67
+ // 计算状态栏类名
68
+ const statusBarClass = computed(() => {
69
+ return [
70
+ 'cosy:w-full cosy:h-full',
71
+ 'cosy:flex',
72
+ 'cosy:justify-between',
73
+ 'cosy:items-center',
74
+ 'cosy:bg-base-200',
75
+ 'cosy:text-base-content/70',
76
+ 'cosy:text-xs',
77
+ 'no-drag-region',
78
+ 'cosy:transition-all',
79
+ 'cosy:duration-200',
80
+ {
81
+ 'cosy:h-8': props.variant === 'default',
82
+ 'cosy:h-6': props.variant === 'compact',
83
+ 'cosy:border-t cosy:border-base-300': props.bordered
84
+ }
85
+ ]
86
+ })
87
+ </script>
88
+
89
+ <template>
90
+ <div :class="statusBarClass">
91
+ <!-- 左侧内容区域 -->
92
+ <div class="cosy:flex cosy:items-center cosy:h-full cosy:overflow-hidden">
93
+ <slot name="left"></slot>
94
+ </div>
95
+
96
+ <!-- 右侧内容区域 -->
97
+ <div class="cosy:flex cosy:items-center cosy:h-full cosy:overflow-hidden">
98
+ <slot name="right"></slot>
99
+ </div>
100
+ </div>
101
+ </template>
@@ -0,0 +1,97 @@
1
+ <!--
2
+ StatusBarItem 组件
3
+
4
+ 一个用于在StatusBar中显示单个状态项的组件,支持可点击和不可点击两种状态。
5
+
6
+ 使用示例:
7
+ ```vue
8
+ <StatusBarItem>
9
+ <i class="i-carbon-information"></i>
10
+ <span>状态信息</span>
11
+ </StatusBarItem>
12
+
13
+ <StatusBarItem clickable @click="handleClick">
14
+ <i class="i-carbon-git-branch"></i>
15
+ <span>main</span>
16
+ </StatusBarItem>
17
+
18
+ <StatusBarItem variant="primary">主要状态</StatusBarItem>
19
+ <StatusBarItem variant="success">成功状态</StatusBarItem>
20
+ <StatusBarItem variant="warning">警告状态</StatusBarItem>
21
+ <StatusBarItem variant="error">错误状态</StatusBarItem>
22
+ ```
23
+
24
+ 属性说明:
25
+ - clickable: 是否可点击
26
+ - 类型: boolean
27
+ - 默认值: false
28
+ - variant: 状态项变体
29
+ - 可选值: 'default' | 'primary' | 'success' | 'warning' | 'error'
30
+ - 默认值: 'default'
31
+ - active: 是否激活状态
32
+ - 类型: boolean
33
+ - 默认值: false
34
+
35
+ 事件:
36
+ - click: 点击状态项时触发(仅在clickable为true时有效)
37
+ -->
38
+
39
+ <script setup lang="ts">
40
+ import { computed } from 'vue';
41
+ import '../../style';
42
+
43
+ interface Props {
44
+ // 是否可点击
45
+ clickable?: boolean
46
+ // 状态项变体
47
+ variant?: 'default' | 'primary' | 'success' | 'warning' | 'error'
48
+ // 是否激活状态
49
+ active?: boolean
50
+ }
51
+
52
+ const props = withDefaults(defineProps<Props>(), {
53
+ clickable: false,
54
+ variant: 'default',
55
+ active: false
56
+ })
57
+
58
+ const emit = defineEmits<{
59
+ (e: 'click', event: MouseEvent): void
60
+ }>()
61
+
62
+ const handleClick = (event: MouseEvent) => {
63
+ if (props.clickable) {
64
+ emit('click', event)
65
+ }
66
+ }
67
+
68
+ // 计算状态项类名
69
+ const itemClass = computed(() => {
70
+ return [
71
+ 'cosy:status-bar-item',
72
+ 'cosy:h-full',
73
+ 'cosy:flex',
74
+ 'cosy:items-center',
75
+ 'cosy:gap-1',
76
+ 'cosy:px-2',
77
+ 'cosy:text-xs',
78
+ 'cosy:transition-all',
79
+ 'cosy:duration-200',
80
+ {
81
+ 'cosy:cursor-pointer cosy:hover:bg-base-300': props.clickable,
82
+ 'cosy:cursor-default': !props.clickable,
83
+ 'cosy:bg-base-300': props.active,
84
+ 'cosy:text-primary': props.variant === 'primary',
85
+ 'cosy:text-success': props.variant === 'success',
86
+ 'cosy:text-warning': props.variant === 'warning',
87
+ 'cosy:text-error': props.variant === 'error'
88
+ }
89
+ ]
90
+ })
91
+ </script>
92
+
93
+ <template>
94
+ <div :class="itemClass" @click="handleClick">
95
+ <slot></slot>
96
+ </div>
97
+ </template>
@@ -0,0 +1,2 @@
1
+ export { default as StatusBar } from './StatusBar.vue';
2
+ export { default as StatusBarItem } from './StatusBarItem.vue';
@@ -0,0 +1,95 @@
1
+ <!--
2
+ ToolBar 组件
3
+
4
+ 一个基于 DaisyUI navbar 的顶部工具栏组件,采用现代化设计。
5
+ 支持左中右三段式布局,可以放置按钮、输入框、图标和文字等元素。
6
+
7
+ 使用示例:
8
+ ```vue
9
+ <ToolBar>
10
+ <template #left>
11
+ <button class="btn btn-ghost btn-sm">
12
+ <i class="i-carbon-menu"></i>
13
+ </button>
14
+ </template>
15
+ <template #center>
16
+ <div class="w-full max-w-2xl">
17
+ <input type="text" class="input input-sm w-full bg-base-300" placeholder="输入URL地址" />
18
+ </div>
19
+ </template>
20
+ <template #right>
21
+ <button class="btn btn-ghost btn-sm" @click="handleRefresh">
22
+ <i class="i-carbon-refresh"></i>
23
+ </button>
24
+ </template>
25
+ </ToolBar>
26
+ ```
27
+
28
+ 属性说明:
29
+ - variant: 工具栏变体
30
+ - 可选值: 'default' | 'compact'
31
+ - 默认值: 'default'
32
+ - bordered: 是否显示下边框
33
+ - 类型: boolean
34
+ - 默认值: true
35
+
36
+ 插槽:
37
+ - left: 左侧内容区域
38
+ - center: 中间内容区域
39
+ - right: 右侧内容区域
40
+ -->
41
+
42
+ <script setup lang="ts">
43
+ import { computed } from 'vue';
44
+ import '../../style';
45
+
46
+ interface Props {
47
+ // 工具栏变体
48
+ variant?: 'default' | 'compact'
49
+ // 是否显示下边框
50
+ bordered?: boolean
51
+ }
52
+
53
+ const props = withDefaults(defineProps<Props>(), {
54
+ variant: 'default',
55
+ bordered: true
56
+ })
57
+
58
+ // 计算工具栏类名
59
+ const toolBarClass = computed(() => {
60
+ return [
61
+ 'cosy:navbar',
62
+ 'cosy:bg-base-200',
63
+ 'cosy:text-base-content/70',
64
+ 'cosy:text-sm',
65
+ 'no-drag-region',
66
+ 'cosy:transition-all',
67
+ 'cosy:duration-200',
68
+ 'cosy:rounded-box',
69
+ {
70
+ 'cosy:min-h-12': props.variant === 'default',
71
+ 'cosy:min-h-10': props.variant === 'compact',
72
+ 'cosy:border-b cosy:border-base-300': props.bordered
73
+ }
74
+ ]
75
+ })
76
+ </script>
77
+
78
+ <template>
79
+ <div :class="toolBarClass">
80
+ <!-- 左侧内容区域 -->
81
+ <div class="cosy:navbar-start">
82
+ <slot name="left"></slot>
83
+ </div>
84
+
85
+ <!-- 中间内容区域 -->
86
+ <div class="cosy:navbar-center">
87
+ <slot name="center"></slot>
88
+ </div>
89
+
90
+ <!-- 右侧内容区域 -->
91
+ <div class="cosy:navbar-end">
92
+ <slot name="right"></slot>
93
+ </div>
94
+ </div>
95
+ </template>
@@ -0,0 +1 @@
1
+ export { default as ToolBar } from './ToolBar.vue';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffic/cosy-ui",
3
- "version": "0.8.26",
3
+ "version": "0.8.28",
4
4
  "description": "An astro component library",
5
5
  "author": {
6
6
  "name": "nookery",
@@ -1,14 +0,0 @@
1
- ---
2
- interface Props {
3
- code: string;
4
- }
5
- const { code = '' } = Astro.props;
6
- ---
7
-
8
- <div class="cosy:hidden cosy:code-example-panel" data-panel="code">
9
- <pre
10
- class="cosy:overflow-x-auto"
11
- style="margin:0">
12
- <code class="cosy:language-astro">{code}</code>
13
- </pre>
14
- </div>
@@ -1,25 +0,0 @@
1
- ---
2
- /**
3
- * @component TeamMember.Basic
4
- *
5
- * @description
6
- * 基础的团队成员展示组件示例,展示最简单的成员信息展示方式。
7
- */
8
- import '../../style.ts';
9
- import TeamMember from './TeamMember.astro';
10
- import { getExampleImage } from '../../src/utils/image.ts';
11
-
12
- const avatar = getExampleImage({
13
- width: 200,
14
- height: 200,
15
- provider: 'robohash',
16
- randomSeed: 'member1',
17
- });
18
- ---
19
-
20
- <TeamMember
21
- name="张三"
22
- role="产品设计师"
23
- avatar={avatar}
24
- bio="专注于用户体验设计,拥有5年产品设计经验"
25
- />
@@ -1,26 +0,0 @@
1
- ---
2
- /**
3
- * @component TeamMember.CustomStyle
4
- *
5
- * @description
6
- * 自定义样式的团队成员展示组件示例,展示如何使用class属性自定义样式。
7
- */
8
- import '../../style.ts';
9
- import TeamMember from './TeamMember.astro';
10
- import { getExampleImage } from '../../src/utils/image.ts';
11
-
12
- const avatar = getExampleImage({
13
- width: 200,
14
- height: 200,
15
- provider: 'robohash',
16
- randomSeed: 'member3',
17
- });
18
- ---
19
-
20
- <TeamMember
21
- name="王五"
22
- role="市场总监"
23
- avatar={avatar}
24
- bio="负责产品市场策略和品牌推广,拥有丰富的市场营销经验"
25
- class="cosy:bg-gradient-to-r cosy:from-purple-100 cosy:to-pink-100 cosy:shadow-lg cosy:w-72"
26
- />
@@ -1,60 +0,0 @@
1
- ---
2
- /**
3
- * @component TeamMember.Group
4
- *
5
- * @description
6
- * 团队成员组展示组件示例,展示如何组合多个团队成员。
7
- */
8
- import '../../style.ts';
9
- import TeamMember from './TeamMember.astro';
10
- import { getExampleImage } from '../../src/utils/image.ts';
11
-
12
- const members = [
13
- {
14
- name: '张三',
15
- title: '产品设计师',
16
- avatar: getExampleImage({
17
- width: 200,
18
- height: 200,
19
- provider: 'robohash',
20
- randomSeed: 'member1',
21
- }),
22
- description: '专注于用户体验设计,拥有5年产品设计经验',
23
- },
24
- {
25
- name: '李四',
26
- title: '高级开发工程师',
27
- avatar: getExampleImage({
28
- width: 200,
29
- height: 200,
30
- provider: 'robohash',
31
- randomSeed: 'member2',
32
- }),
33
- description: '全栈开发专家,热爱开源项目,专注于前端框架和性能优化',
34
- },
35
- {
36
- name: '王五',
37
- title: '市场总监',
38
- avatar: getExampleImage({
39
- width: 200,
40
- height: 200,
41
- provider: 'robohash',
42
- randomSeed: 'member3',
43
- }),
44
- description: '负责产品市场策略和品牌推广,拥有丰富的市场营销经验',
45
- },
46
- ];
47
- ---
48
-
49
- <div class="cosy:grid cosy:grid-cols-3 md:cosy:grid-cols-3 cosy:gap-8">
50
- {
51
- members.map((member) => (
52
- <TeamMember
53
- name={member.name}
54
- role={member.title}
55
- avatar={member.avatar}
56
- bio={member.description}
57
- />
58
- ))
59
- }
60
- </div>
@@ -1,30 +0,0 @@
1
- ---
2
- /**
3
- * @component TeamMember.WithSocial
4
- *
5
- * @description
6
- * 带社交媒体链接的团队成员展示组件示例,展示如何添加社交媒体链接。
7
- */
8
- import '../../style.ts';
9
- import TeamMember from './TeamMember.astro';
10
- import { getExampleImage } from '../../src/utils/image.ts';
11
-
12
- const avatar = getExampleImage({
13
- width: 200,
14
- height: 200,
15
- provider: 'robohash',
16
- randomSeed: 'member2',
17
- });
18
- ---
19
-
20
- <TeamMember
21
- name="李四"
22
- role="高级开发工程师"
23
- avatar={avatar}
24
- bio="全栈开发专家,热爱开源项目,专注于前端框架和性能优化"
25
- socialLinks={[
26
- { platform: 'github', url: 'https://github.com' },
27
- { platform: 'twitter', url: 'https://twitter.com' },
28
- { platform: 'linkedin', url: 'https://linkedin.com' },
29
- ]}
30
- />