@coffic/cosy-ui 0.2.3 → 0.3.0-beta.1

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.
@@ -28,7 +28,8 @@
28
28
  */
29
29
 
30
30
  // 导入图标
31
- import { ClipboardIcon, CheckIcon } from '../../index';
31
+ import { ClipboardIcon, CheckIcon } from '../../index'
32
+ import "../../app.css"
32
33
 
33
34
  interface Props {
34
35
  title?: string;
@@ -39,138 +40,40 @@ interface Props {
39
40
  const { title, description, code } = Astro.props;
40
41
  ---
41
42
 
42
- <div class="code-example">
43
- {title && <h3 class="code-example-title">{title}</h3>}
44
- {description && <p class="code-example-description">{description}</p>}
45
-
46
- <div class="code-example-tabs">
47
- <button class="code-example-tab active" data-tab="preview">预览</button>
48
- <button class="code-example-tab" data-tab="code">代码</button>
43
+ <div class="card bg-base-100 shadow-lg mb-8 rounded-none code-example">
44
+ {title && <h3 class="card-title p-4 border-b border-base-300">{title}</h3>}
45
+ {description && <p class="px-4 pt-4 text-base-content/70">{description}</p>}
46
+
47
+ <div class="flex justify-between items-center px-4 bg-base-200 rounded-none">
48
+ <div role="tablist" class="tabs tabs-box">
49
+ <button role="tab" class="tab tab-active" data-tab="preview">预览</button>
50
+ <button role="tab" class="tab" data-tab="code">代码</button>
51
+ </div>
52
+ <div class="flex items-center gap-2">
53
+ <button class="btn btn-ghost btn-sm gap-2" aria-label="复制代码">
54
+ <span class="copy-icon"><ClipboardIcon /></span>
55
+ <span class="check-icon hidden text-success"><CheckIcon /></span>
56
+ <span class="copy-text">复制代码</span>
57
+ </button>
58
+ </div>
49
59
  </div>
50
60
 
51
- <div class="code-example-content">
61
+ <div class="relative p-0">
52
62
  <div class="code-example-panel active" data-panel="preview">
53
- <div class="code-example-preview">
63
+ <div class="p-6 bg-base-100">
54
64
  <slot />
55
65
  </div>
56
66
  </div>
57
67
 
58
68
  <div class="code-example-panel" data-panel="code">
59
- <div class="code-example-code">
60
- <div class="code-example-header">
61
- <button class="copy-button" aria-label="复制代码">
62
- <span class="copy-icon"><ClipboardIcon /></span>
63
- <span class="check-icon"><CheckIcon /></span>
64
- <span class="copy-text">复制代码</span>
65
- </button>
66
- </div>
67
- <pre><code class="language-astro">{code}</code></pre>
69
+ <div class="bg-base-300 px-6 py-2">
70
+ <pre class="overflow-x-auto"><code class="language-astro">{code}</code></pre>
68
71
  </div>
69
72
  </div>
70
73
  </div>
71
74
  </div>
72
75
 
73
76
  <style>
74
- /* 颜色变量 - 支持亮色和暗黑模式 */
75
- :root {
76
- /* 亮色模式变量 */
77
- --code-example-border: #e2e8f0;
78
- --code-example-bg: #ffffff;
79
- --code-example-text: #333333;
80
- --code-example-title-border: #e2e8f0;
81
- --code-example-description-color: #64748b;
82
- --code-example-tabs-bg: #f8fafc;
83
- --code-example-tab-color: #64748b;
84
- --code-example-tab-hover-color: #0f172a;
85
- --code-example-tab-active-color: #3b82f6;
86
- --code-example-tab-active-border: #3b82f6;
87
- --code-example-preview-bg: #ffffff;
88
- --code-example-code-bg: #1e293b;
89
- --code-example-code-text: #e2e8f0;
90
- --code-example-header-bg: #334155;
91
- --code-example-copy-button-color: #e2e8f0;
92
- --code-example-copy-button-hover-bg: rgba(255, 255, 255, 0.1);
93
- --code-example-copy-success-color: #4ade80;
94
- }
95
-
96
- @media (prefers-color-scheme: dark) {
97
- :root {
98
- /* 暗黑模式变量 */
99
- --code-example-border: #2d3748;
100
- --code-example-bg: #1a202c;
101
- --code-example-text: #e2e8f0;
102
- --code-example-title-border: #2d3748;
103
- --code-example-description-color: #a0aec0;
104
- --code-example-tabs-bg: #171e2e;
105
- --code-example-tab-color: #a0aec0;
106
- --code-example-tab-hover-color: #e2e8f0;
107
- --code-example-tab-active-color: #60a5fa;
108
- --code-example-tab-active-border: #60a5fa;
109
- --code-example-preview-bg: #1a202c;
110
- --code-example-code-bg: #0f172a;
111
- --code-example-code-text: #e2e8f0;
112
- --code-example-header-bg: #1e293b;
113
- --code-example-copy-button-color: #e2e8f0;
114
- --code-example-copy-button-hover-bg: rgba(255, 255, 255, 0.1);
115
- --code-example-copy-success-color: #4ade80;
116
- }
117
- }
118
-
119
- .code-example {
120
- margin-bottom: 2rem;
121
- border: 1px solid var(--code-example-border);
122
- border-radius: 0.5rem;
123
- overflow: hidden;
124
- background-color: var(--code-example-bg);
125
- color: var(--code-example-text);
126
- box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
127
- }
128
-
129
- .code-example-title {
130
- font-size: 1.25rem;
131
- font-weight: bold;
132
- margin: 0;
133
- padding: 1rem;
134
- border-bottom: 1px solid var(--code-example-title-border);
135
- }
136
-
137
- .code-example-description {
138
- padding: 0 1rem;
139
- color: var(--code-example-description-color);
140
- margin-top: 0.5rem;
141
- margin-bottom: 1rem;
142
- }
143
-
144
- .code-example-tabs {
145
- display: flex;
146
- border-bottom: 1px solid var(--code-example-title-border);
147
- background-color: var(--code-example-tabs-bg);
148
- }
149
-
150
- .code-example-tab {
151
- padding: 0.75rem 1.5rem;
152
- background: transparent;
153
- border: none;
154
- border-bottom: 2px solid transparent;
155
- cursor: pointer;
156
- font-weight: 500;
157
- color: var(--code-example-tab-color);
158
- transition: all 0.2s ease;
159
- }
160
-
161
- .code-example-tab:hover {
162
- color: var(--code-example-tab-hover-color);
163
- }
164
-
165
- .code-example-tab.active {
166
- color: var(--code-example-tab-active-color);
167
- border-bottom-color: var(--code-example-tab-active-border);
168
- }
169
-
170
- .code-example-content {
171
- position: relative;
172
- }
173
-
174
77
  .code-example-panel {
175
78
  display: none;
176
79
  }
@@ -178,99 +81,23 @@ const { title, description, code } = Astro.props;
178
81
  .code-example-panel.active {
179
82
  display: block;
180
83
  }
181
-
182
- .code-example-preview {
183
- padding: 1.5rem;
184
- background-color: var(--code-example-preview-bg);
185
- }
186
-
187
- .code-example-code {
188
- position: relative;
189
- background-color: var(--code-example-code-bg);
190
- color: var(--code-example-code-text);
191
- overflow: auto;
192
- }
193
-
194
- .code-example-header {
195
- display: flex;
196
- justify-content: flex-end;
197
- align-items: center;
198
- padding: 0.5rem 1rem;
199
- background-color: var(--code-example-header-bg);
200
- }
201
-
202
- .copy-button {
203
- background: transparent;
204
- border: none;
205
- color: var(--code-example-copy-button-color);
206
- cursor: pointer;
207
- display: flex;
208
- align-items: center;
209
- gap: 0.5rem;
210
- padding: 0.5rem 0.75rem;
211
- border-radius: 0.25rem;
212
- transition: background-color 0.2s;
213
- font-size: 0.875rem;
214
- }
215
-
216
- .copy-button:hover {
217
- background-color: var(--code-example-copy-button-hover-bg);
218
- }
219
-
220
- .copy-icon, .check-icon {
221
- display: flex;
222
- align-items: center;
223
- justify-content: center;
224
- width: 1rem;
225
- height: 1rem;
226
- }
227
-
228
- .check-icon {
229
- display: none;
230
- color: var(--code-example-copy-success-color);
231
- }
232
-
233
- .copy-button.copied .copy-icon {
234
- display: none;
235
- }
236
-
237
- .copy-button.copied .check-icon {
238
- display: flex;
239
- }
240
-
241
- .copy-button.copied .copy-text {
242
- color: var(--code-example-copy-success-color);
243
- }
244
-
245
- pre {
246
- margin: 0;
247
- padding: 1rem;
248
- overflow-x: auto;
249
- }
250
-
251
- code {
252
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
253
- white-space: pre;
254
- font-size: 0.9rem;
255
- line-height: 1.5;
256
- }
257
84
  </style>
258
85
 
259
86
  <script>
260
87
  function initializeCodeExample() {
261
88
  // 标签切换功能
262
- const tabs = document.querySelectorAll('.code-example-tab');
89
+ const tabs = document.querySelectorAll('.tab');
263
90
 
264
91
  tabs.forEach(tab => {
265
92
  tab.addEventListener('click', () => {
266
93
  // 获取当前标签组
267
- const tabGroup = tab.closest('.code-example-tabs');
94
+ const tabGroup = tab.closest('.tabs');
268
95
  if (!tabGroup) return;
269
96
 
270
- const codeExample = tab.closest('.code-example');
97
+ const codeExample = tab.closest('.card');
271
98
  if (!codeExample) return;
272
99
 
273
- const contentContainer = codeExample.querySelector('.code-example-content');
100
+ const contentContainer = codeExample.querySelector('.relative');
274
101
  if (!contentContainer) return;
275
102
 
276
103
  // 获取目标面板
@@ -278,10 +105,10 @@ const { title, description, code } = Astro.props;
278
105
  if (!targetTab) return;
279
106
 
280
107
  // 更新标签状态
281
- tabGroup.querySelectorAll('.code-example-tab').forEach(t => {
282
- t.classList.remove('active');
108
+ tabGroup.querySelectorAll('.tab').forEach(t => {
109
+ t.classList.remove('tab-active');
283
110
  });
284
- tab.classList.add('active');
111
+ tab.classList.add('tab-active');
285
112
 
286
113
  // 更新面板状态
287
114
  contentContainer.querySelectorAll('.code-example-panel').forEach(panel => {
@@ -296,11 +123,11 @@ const { title, description, code } = Astro.props;
296
123
  });
297
124
 
298
125
  // 复制代码功能
299
- const copyButtons = document.querySelectorAll('.copy-button');
126
+ const copyButtons = document.querySelectorAll('.btn-ghost');
300
127
 
301
128
  copyButtons.forEach(button => {
302
129
  button.addEventListener('click', () => {
303
- const codeBlock = button.closest('.code-example-code');
130
+ const codeBlock = button.closest('.bg-base-300');
304
131
  if (!codeBlock) return;
305
132
 
306
133
  const codeElement = codeBlock.querySelector('code');
@@ -311,11 +138,17 @@ const { title, description, code } = Astro.props;
311
138
  // 复制到剪贴板
312
139
  navigator.clipboard.writeText(code).then(() => {
313
140
  // 显示复制成功状态
314
- button.classList.add('copied');
141
+ button.classList.add('btn-success');
142
+ button.querySelector('.copy-icon')?.classList.add('hidden');
143
+ button.querySelector('.check-icon')?.classList.remove('hidden');
144
+ button.querySelector('.copy-text')?.classList.add('text-success');
315
145
 
316
146
  // 3秒后恢复原状
317
147
  setTimeout(() => {
318
- button.classList.remove('copied');
148
+ button.classList.remove('btn-success');
149
+ button.querySelector('.copy-icon')?.classList.remove('hidden');
150
+ button.querySelector('.check-icon')?.classList.add('hidden');
151
+ button.querySelector('.copy-text')?.classList.remove('text-success');
319
152
  }, 3000);
320
153
  });
321
154
  });
@@ -79,6 +79,7 @@ import Article from '../typography/Article.astro';
79
79
  import Footer from './Footer.astro';
80
80
  import Main from '../containers/Main.astro';
81
81
  import Header from './Header.astro';
82
+ import Sidebar from './Sidebar.astro';
82
83
  import DefaultLogo from '../../assets/logo.png';
83
84
 
84
85
  export interface SidebarItem {
@@ -120,6 +121,12 @@ export interface Props {
120
121
  */
121
122
  logo?: ImageMetadata;
122
123
 
124
+ /**
125
+ * Logo链接地址
126
+ * @default "/"
127
+ */
128
+ logoHref?: string;
129
+
123
130
  /**
124
131
  * 侧边栏项目
125
132
  */
@@ -182,6 +189,12 @@ export interface Props {
182
189
  * 当前语言
183
190
  */
184
191
  currentLocale?: string;
192
+
193
+ /**
194
+ * 基础路径,用于处理网站部署在二级目录的情况
195
+ * @default ""
196
+ */
197
+ basePath?: string;
185
198
 
186
199
  /**
187
200
  * 页脚相关配置
@@ -297,6 +310,11 @@ export interface Props {
297
310
  * 技术栈链接
298
311
  */
299
312
  footerTechStackLink?: string;
313
+
314
+ /**
315
+ * 首页链接
316
+ */
317
+ footerHomeLink?: string;
300
318
  }
301
319
 
302
320
  const {
@@ -305,6 +323,7 @@ const {
305
323
  keywords,
306
324
  siteName = "文档中心",
307
325
  logo = DefaultLogo,
326
+ logoHref = "/",
308
327
  sidebarItems,
309
328
  showTableOfContents = true,
310
329
  showHeader = true,
@@ -312,10 +331,12 @@ const {
312
331
  head,
313
332
  class: className,
314
333
  'class:list': classList,
315
- debug = false,
334
+ debug = true,
316
335
  navItems,
317
336
  languages,
318
337
  currentLocale,
338
+ basePath = "",
339
+ footerHomeLink,
319
340
  footerSlogan,
320
341
  footerCompany,
321
342
  footerCopyright,
@@ -354,10 +375,12 @@ const currentPath = Astro.url.pathname;
354
375
  {showHeader && (
355
376
  <Header
356
377
  logo={logo}
378
+ logoHref={logoHref}
357
379
  navItems={navItems}
358
380
  languages={languages}
359
381
  currentLocale={currentLocale}
360
382
  sticky={true}
383
+ basePath={basePath}
361
384
  />
362
385
  )}
363
386
 
@@ -368,61 +391,13 @@ const currentPath = Astro.url.pathname;
368
391
  )}
369
392
 
370
393
  <div class="flex-1 flex">
371
- <aside class="w-64 border-r border-base-300 shrink-0">
372
- <nav class="p-4 sticky top-16">
373
- {sidebarItems.map((section: SidebarSection) => (
374
- <div class="mb-6">
375
- <h3 class="font-bold mb-2 text-base-content/70">{section.title}</h3>
376
- <ul class="menu menu-sm">
377
- {section.items.map((item: SidebarItem) => {
378
- const isActive = currentPath === item.href;
379
- return (
380
- <li>
381
- <a
382
- href={item.href}
383
- class:list={[
384
- "hover:bg-base-200",
385
- { "active": isActive }
386
- ]}
387
- >
388
- {item.text}
389
- </a>
390
- {item.items && (
391
- <ul class="menu menu-sm pl-4">
392
- {item.items.map((subitem: SidebarItem) => {
393
- const isSubActive = currentPath === subitem.href;
394
- return (
395
- <li>
396
- <a
397
- href={subitem.href}
398
- class:list={[
399
- "hover:bg-base-200",
400
- { "active": isSubActive }
401
- ]}
402
- >
403
- {subitem.text}
404
- </a>
405
- </li>
406
- );
407
- })}
408
- </ul>
409
- )}
410
- </li>
411
- );
412
- })}
413
- </ul>
414
- </div>
415
- ))}
416
- </nav>
417
- </aside>
394
+ <Sidebar sidebarItems={sidebarItems} currentPath={currentPath} />
418
395
 
419
396
  <Main
420
- class="flex-1 py-8"
397
+ class="flex-1 py-8 min-h-screen"
421
398
  >
422
399
  <div class="container mx-auto px-4 flex gap-8">
423
- <Article
424
- class="prose max-w-3xl"
425
- >
400
+ <Article >
426
401
  <slot />
427
402
  </Article>
428
403
 
@@ -442,7 +417,7 @@ const currentPath = Astro.url.pathname;
442
417
  {showFooter && (
443
418
  <Footer
444
419
  siteName={siteName}
445
- homeLink="/"
420
+ homeLink={footerHomeLink || "/"}
446
421
  slogan={footerSlogan || "优雅、高效的组件库"}
447
422
  company={footerCompany || siteName}
448
423
  copyright={footerCopyright || "保留所有权利"}
@@ -178,7 +178,7 @@ const debugClasses = debug ? {
178
178
  ---
179
179
 
180
180
  <footer class:list={["footer sm:footer-horizontal bg-base-200 text-base-content p-10", debugClasses.footer]}>
181
- <div class:list={["flex container mx-auto sm:flex-col md:flex-row justify-between gap-8", debugClasses.section]}>
181
+ <div class:list={["flex flex-col gap-8 container mx-auto items-center w-full md:flex-row md:justify-between", debugClasses.section]}>
182
182
  {/* 品牌区域 */}
183
183
  <aside class:list={["max-w-xs", debugClasses.aside]}>
184
184
  <a href={homeLink} class="flex items-center gap-2 mb-4">
@@ -215,13 +215,14 @@ const debugClasses = debug ? {
215
215
  )}
216
216
  </aside>
217
217
 
218
- <div class:list={["grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-10 sm:justify-end", debugClasses.section]}>
218
+ {/* 导航区域 */}
219
+ <div class:list={["flex flex-col gap-8 mx-auto max-w-xl text-center items-center w-full md:flex-row md:justify-between md:items-start", debugClasses.section]}>
219
220
  {/* 产品导航 */}
220
221
  {products.length > 0 && (
221
- <nav class:list={["", debugClasses.nav]}>
222
+ <nav class:list={["h-full", debugClasses.nav]}>
222
223
  <h6 class="footer-title">产品</h6>
223
224
  {products.map((product) => (
224
- <Link href={product.href} external={product.external} block animation='hover-lift'>
225
+ <Link href={product.href} external={product.external} size='sm' block animation='hover-lift'>
225
226
  {product.name}
226
227
  </Link>
227
228
  ))}
@@ -230,12 +231,12 @@ const debugClasses = debug ? {
230
231
 
231
232
  {/* 关于导航 */}
232
233
  {(aboutLink || contactLink || teamLink || careersLink) && (
233
- <nav class:list={["", debugClasses.nav]}>
234
- <h6 class="footer-title">关于我们</h6>
235
- {aboutLink && <Link href={aboutLink} block animation='hover-lift'>关于我们</Link>}
236
- {teamLink && <Link href={teamLink} block animation='hover-lift'>团队介绍</Link>}
237
- {careersLink && <Link href={careersLink} block animation='hover-lift'>加入我们</Link>}
238
- {contactLink && <Link href={contactLink} block animation='hover-lift'>联系我们</Link>}
234
+ <nav class:list={["h-full", debugClasses.nav]}>
235
+ <h6 class="footer-title">关于</h6>
236
+ {aboutLink && <Link href={aboutLink} block animation='hover-lift' size='sm'>关于我们</Link>}
237
+ {teamLink && <Link href={teamLink} block animation='hover-lift' size='sm'>团队介绍</Link>}
238
+ {careersLink && <Link href={careersLink} block animation='hover-lift' size='sm'>加入我们</Link>}
239
+ {contactLink && <Link href={contactLink} block animation='hover-lift' size='sm'>联系我们</Link>}
239
240
  </nav>
240
241
  )}
241
242
 
@@ -243,10 +244,11 @@ const debugClasses = debug ? {
243
244
  {(newsLink || historyLink || partnersLink || blogLink || faqLink || mediaLink || techStackLink) && (
244
245
  <nav class:list={["", debugClasses.nav]}>
245
246
  <h6 class="footer-title">资源</h6>
246
- {newsLink && <Link href={newsLink} block animation='hover-lift'>新闻动态</Link>}
247
- {blogLink && <Link href={blogLink} block animation='hover-lift'>技术博客</Link>}
248
- {faqLink && <Link href={faqLink} block animation='hover-lift'>常见问题</Link>}
249
- {techStackLink && <Link href={techStackLink} block animation='hover-lift'>技术栈</Link>}
247
+ {newsLink && <Link href={newsLink} block animation='hover-lift' size='sm'>新闻动态</Link>}
248
+ {blogLink && <Link href={blogLink} block animation='hover-lift' size='sm'>技术博客</Link>}
249
+ {faqLink && <Link href={faqLink} block animation='hover-lift' size='sm'>常见问题</Link>}
250
+ {historyLink && <Link href={historyLink} block animation='hover-lift' size='sm'>发展历程</Link>}
251
+ {techStackLink && <Link href={techStackLink} block animation='hover-lift' size='sm'>技术栈</Link>}
250
252
  </nav>
251
253
  )}
252
254
 
@@ -254,8 +256,8 @@ const debugClasses = debug ? {
254
256
  {(termsLink || privacyLink) && (
255
257
  <nav class:list={["", debugClasses.nav]}>
256
258
  <h6 class="footer-title">法律</h6>
257
- {termsLink && <Link href={termsLink} block animation='hover-lift'>服务条款</Link>}
258
- {privacyLink && <Link href={privacyLink} block animation='hover-lift'>隐私政策</Link>}
259
+ {termsLink && <Link href={termsLink} block animation='hover-lift' size='sm'>服务条款</Link>}
260
+ {privacyLink && <Link href={privacyLink} block animation='hover-lift' size='sm'>隐私政策</Link>}
259
261
  </nav>
260
262
  )}
261
263
  </div>
@@ -282,3 +284,10 @@ const debugClasses = debug ? {
282
284
  )}
283
285
  </aside>
284
286
  </div>
287
+
288
+ <style scoped>
289
+ @reference '../../app.css';
290
+ nav {
291
+ @apply flex flex-col gap-3;
292
+ }
293
+ </style>
@@ -15,6 +15,11 @@ interface ImageMetadata {
15
15
 
16
16
  interface Props {
17
17
  logo: ImageMetadata;
18
+ /**
19
+ * Logo 链接地址
20
+ * @default "/"
21
+ */
22
+ logoHref?: string;
18
23
  languages?: Array<{ code: string; name: string }>;
19
24
  currentLocale?: string;
20
25
  navItems?: Array<{
@@ -32,19 +37,47 @@ interface Props {
32
37
  * @default true
33
38
  */
34
39
  sticky?: boolean;
40
+ /**
41
+ * 基础路径,用于处理网站部署在二级目录的情况
42
+ * @default ""
43
+ */
44
+ basePath?: string;
35
45
  }
36
46
 
37
47
  const {
38
48
  logo,
49
+ logoHref = "/",
39
50
  navItems = [],
40
51
  sticky = true,
52
+ languages = [
53
+ { code: 'zh-cn', name: '中文' },
54
+ { code: 'en', name: 'English' }
55
+ ],
56
+ currentLocale = 'zh-cn',
57
+ basePath = ''
41
58
  } = Astro.props;
42
59
 
43
60
  type NavItem = { href: string; label: string; match: (path: string) => boolean };
61
+
62
+ // 获取当前路径
63
+ const currentPath = Astro.url.pathname;
64
+
65
+ // 处理基础路径
66
+ const basePathPattern = basePath ? new RegExp(`^${basePath}`) : null;
67
+ const pathWithoutBase = basePathPattern ? currentPath.replace(basePathPattern, '') : currentPath;
68
+
69
+ // 提取路径部分,排除语言代码
70
+ const pathWithoutLocale = pathWithoutBase.replace(/^\/(zh-cn|en)/, '');
71
+
72
+ // 生成语言切换链接
73
+ function getLanguageUrl(langCode: string) {
74
+ // 如果有基础路径,需要加上
75
+ return `${basePath}/${langCode}${pathWithoutLocale}`;
76
+ }
44
77
  ---
45
78
 
46
79
  <header class:list={[
47
- "navbar bg-accent/80 backdrop-blur border-b border-base-200 z-50 w-full",
80
+ "navbar bg-accent/30 backdrop-blur border-base-200 z-50 w-full",
48
81
  { "fixed top-0": sticky }
49
82
  ]}>
50
83
  <div class="navbar-start">
@@ -66,7 +99,7 @@ type NavItem = { href: string; label: string; match: (path: string) => boolean }
66
99
  </ul>
67
100
  </div>
68
101
 
69
- <Link href="/" class="btn btn-ghost">
102
+ <Link href={logoHref} class="btn btn-ghost">
70
103
  <Image src={logo} alt="logo" class="w-10 h-10" />
71
104
  </Link>
72
105
  </div>
@@ -85,14 +118,24 @@ type NavItem = { href: string; label: string; match: (path: string) => boolean }
85
118
  </div>
86
119
 
87
120
  <div class="navbar-end">
88
- <Button
89
- variant="ghost"
90
- size="sm"
91
- class="btn-circle"
92
- onClick="my_modal_1.showModal()"
93
- >
94
- <SearchIcon class="w-5 h-5" />
95
- </Button>
121
+ <!-- 语言切换按钮 -->
122
+ <div class="dropdown dropdown-end">
123
+ <div tabindex="0" role="button" class="btn btn-ghost btn-sm">
124
+ <span class="mr-1">{currentLocale === 'zh-cn' ? '中文' : 'English'}</span>
125
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
126
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
127
+ </svg>
128
+ </div>
129
+ <ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-32">
130
+ {languages.map((lang) => (
131
+ <li class={currentLocale === lang.code ? "disabled" : ""}>
132
+ <a href={getLanguageUrl(lang.code)} class={currentLocale === lang.code ? "active" : ""}>
133
+ {lang.name}
134
+ </a>
135
+ </li>
136
+ ))}
137
+ </ul>
138
+ </div>
96
139
  </div>
97
140
  </header>
98
141