rune-lab 0.0.4 → 0.0.6-rc0

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 (45) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +40 -40
  3. package/dist/components/UIShowcase.svelte +184 -338
  4. package/dist/components/layout/ThemeSelector.svelte +25 -7
  5. package/dist/components/layout/URLDisplay.svelte +11 -17
  6. package/dist/core/theme/constants.d.ts +29 -0
  7. package/dist/core/theme/constants.js +61 -0
  8. package/dist/index.d.ts +4 -13
  9. package/dist/index.js +8 -42
  10. package/dist/mod.d.ts +5 -0
  11. package/dist/mod.js +12 -0
  12. package/dist/stores/api.svelte.d.ts +11 -0
  13. package/dist/stores/api.svelte.js +30 -0
  14. package/dist/stores/app.svelte.js +6 -6
  15. package/dist/stores/auth.svelte.d.ts +9 -10
  16. package/dist/stores/auth.svelte.js +21 -14
  17. package/dist/stores/{layout/footer.svelte.d.ts → footer.svelte.d.ts} +10 -10
  18. package/dist/stores/{layout/footer.svelte.js → footer.svelte.js} +4 -4
  19. package/dist/stores/theme.svelte.d.ts +4 -6
  20. package/dist/stores/theme.svelte.js +25 -33
  21. package/package.json +36 -64
  22. package/dist/components/dt/Altharun.svelte +0 -74
  23. package/dist/components/dt/Altharun.svelte.d.ts +0 -3
  24. package/dist/components/dt/Kyntharil.svelte +0 -97
  25. package/dist/components/dt/Kyntharil.svelte.d.ts +0 -3
  26. package/dist/components/dt/MetadataTable.svelte +0 -70
  27. package/dist/components/dt/MetadataTable.svelte.d.ts +0 -9
  28. package/dist/components/dt/SchemaDisplay.svelte +0 -154
  29. package/dist/components/dt/SchemaDisplay.svelte.d.ts +0 -7
  30. package/dist/components/layout/Footer.svelte +0 -88
  31. package/dist/components/layout/Footer.svelte.d.ts +0 -3
  32. package/dist/components/layout/NavBar.svelte +0 -114
  33. package/dist/components/layout/NavBar.svelte.d.ts +0 -3
  34. package/dist/components/layout/SignInCard.svelte +0 -72
  35. package/dist/components/layout/SignInCard.svelte.d.ts +0 -3
  36. package/dist/forge.svelte.d.ts +0 -18
  37. package/dist/forge.svelte.js +0 -37
  38. package/dist/theme/index.d.ts +0 -4
  39. package/dist/theme/index.js +0 -2
  40. package/dist/theme/static.d.ts +0 -16
  41. package/dist/theme/static.js +0 -45
  42. package/dist/tools/format.d.ts +0 -20
  43. package/dist/tools/format.js +0 -25
  44. package/dist/tools/pdf.d.ts +0 -1
  45. package/dist/tools/pdf.js +0 -320
@@ -1,363 +1,209 @@
1
1
  <script lang="ts">
2
- import ThemeSelector from "./layout/ThemeSelector.svelte";
3
-
4
- // State management with Svelte 5 runes
5
- let activeTab = $state(0);
6
- let counter = $state(0);
7
- let modalOpen = $state(false);
8
- let drawerOpen = $state(false);
9
- let loading = $state(false);
10
- let progress = $state(0);
11
- let rating = $state(3);
12
- let selected = $state("Option 1");
13
- let steps = $state(1);
14
- let toast = $state("");
2
+ import ThemeSelector from "./layout/ThemeSelector.svelte";
3
+ import {
4
+ PaintBucket,
5
+ Puzzle,
6
+ Ruler,
7
+ ChartBar,
8
+ Sparkle
9
+ } from "lucide-svelte";
15
10
 
16
- // Color variants for demonstration
17
- const colors = ['primary', 'secondary', 'accent', 'info', 'success', 'warning', 'error'];
18
- const sizes = ['xs', 'sm', 'md', 'lg'];
11
+ // State management with Svelte 5 runes
12
+ let activeTab = $state(0);
13
+ let modalOpen = $state(false);
14
+ let drawerOpen = $state(false);
15
+ let loading = $state(false);
16
+ let progress = $state(0);
17
+ let selected = $state("Option 1");
18
+ let toast = $state("");
19
+
20
+ // Color variants for demonstration
21
+ const colors = ['primary', 'secondary', 'accent', 'info', 'success', 'warning', 'error'];
22
+ const sizes = ['xs', 'sm', 'md', 'lg'];
23
+
24
+ // Define tabs with icon components (not JSX syntax)
25
+ const tabs = [
26
+ { title: 'Basic UI', icon: PaintBucket },
27
+ { title: 'Components', icon: Puzzle },
28
+ { title: 'Layout', icon: Ruler },
29
+ { title: 'Data Display', icon: ChartBar },
30
+ { title: 'Feedback', icon: Sparkle }
31
+ ];
19
32
 
20
- // Mock data for examples
21
- const stats = [
22
- { title: "Downloads", value: "31K", desc: "Jan 1st - Feb 1st" },
23
- { title: "New Users", value: "4,200", desc: "↗︎ 400 (22%)" },
24
- { title: "New Registers", value: "1,200", desc: "↘︎ 90 (14%)" }
25
- ];
26
-
27
- // Simulate loading
28
- function simulateLoading() {
29
- loading = true;
30
- setTimeout(() => loading = false, 2000);
31
- }
32
-
33
- // Show toast message
34
- function showToast(message: string) {
35
- toast = message;
36
- setTimeout(() => toast = "", 3000);
37
- }
38
-
39
- // Progress bar simulation
40
- $effect(() => {
41
- const interval = setInterval(() => {
42
- if (progress < 100) {
43
- progress += 1;
44
- } else {
45
- progress = 0;
46
- }
47
- }, 100);
48
-
49
- return () => clearInterval(interval);
50
- });
51
-
52
- const tabs = [
53
- { title: 'Basic UI', icon: '🎨' },
54
- { title: 'Components', icon: '🧩' },
55
- { title: 'Layout', icon: '📐' },
56
- { title: 'Data Display', icon: '📊' },
57
- { title: 'Feedback', icon: '💫' }
58
- ];
59
- </script>
60
-
61
- <div class="min-h-screen bg-base-200 p-4">
62
- <!-- Header with Theme Demo -->
63
- <div class="text-center mb-8 hero bg-base-100 rounded-box p-8">
64
- <div class="hero-content text-center">
65
- <div>
66
- <h1 class="text-5xl font-bold mb-4">DaisyUI Showcase</h1>
67
- <p class="text-xl opacity-75 mb-6">A comprehensive demonstration of DaisyUI components with Svelte 5</p>
68
- <div class="flex justify-center gap-2">
69
- <button class="btn btn-primary" onclick={() => modalOpen = true}>Open Modal</button>
70
- <button class="btn btn-secondary" onclick={() => drawerOpen = true}>Open Drawer</button>
71
- <ThemeSelector />
33
+ // Simulate loading
34
+ function simulateLoading() {
35
+ loading = true;
36
+ setTimeout(() => loading = false, 2000);
37
+ }
38
+
39
+ // Show toast message
40
+ function showToast(message: string) {
41
+ toast = message;
42
+ setTimeout(() => toast = "", 3000);
43
+ }
44
+
45
+ // Progress bar simulation
46
+ $effect(() => {
47
+ const interval = setInterval(() => {
48
+ if (progress < 100) {
49
+ progress += 1;
50
+ } else {
51
+ progress = 0;
52
+ }
53
+ }, 100);
54
+
55
+ return () => clearInterval(interval);
56
+ });
57
+ </script>
58
+
59
+ <div class="min-h-screen bg-base-200 p-4">
60
+ <!-- Header with Theme Demo -->
61
+ <div class="text-center mb-8 hero bg-base-100 rounded-box p-8">
62
+ <div class="hero-content text-center">
63
+ <div>
64
+ <h1 class="text-5xl font-bold mb-4">DaisyUI Showcase</h1>
65
+ <p class="text-xl opacity-75 mb-6">A comprehensive demonstration of DaisyUI components with Svelte 5</p>
66
+ <div class="flex justify-center gap-2">
67
+ <button class="btn btn-primary" onclick={() => modalOpen = true}>Open Modal</button>
68
+ <button class="btn btn-secondary" onclick={() => drawerOpen = true}>Open Drawer</button>
69
+ <button class="btn" onclick={() => showToast("This is a toast message!")}>Show Toast</button>
70
+ <ThemeSelector />
71
+ </div>
72
72
  </div>
73
73
  </div>
74
74
  </div>
75
- </div>
76
-
77
- <!-- Navigation Tabs -->
78
- <div class="tabs tabs-boxed justify-center mb-8">
79
- {#each tabs as tab, i}
80
- <button
81
- class="tab {activeTab === i ? 'tab-active' : ''}"
82
- onclick={() => activeTab = i}
83
- >
84
- <span class="mr-2">{tab.icon}</span>
85
- {tab.title}
86
- </button>
87
- {/each}
88
- </div>
89
-
90
- <!-- Main Content -->
91
- <div class="container mx-auto">
92
- {#if activeTab === 0}
93
- <!-- Basic UI Elements -->
94
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
95
- <!-- Buttons Showcase -->
96
- <div class="card bg-base-100 shadow-xl">
97
- <div class="card-body">
98
- <h2 class="card-title">Buttons</h2>
99
- <div class="flex flex-wrap gap-2">
100
- {#each colors as color}
101
- <button class="btn btn-{color}">{color}</button>
102
- {/each}
103
- </div>
104
- <div class="divider">Sizes</div>
105
- <div class="flex flex-wrap gap-2 items-center">
106
- {#each sizes as size}
107
- <button class="btn btn-{size}">{size}</button>
108
- {/each}
109
- </div>
110
- <div class="divider">States</div>
111
- <div class="flex flex-wrap gap-2">
112
- <button class="btn btn-primary loading">Loading</button>
113
- <button class="btn btn-disabled">Disabled</button>
114
- <button class="btn btn-outline">Outline</button>
115
- <button class="btn btn-link">Link</button>
116
- </div>
117
- </div>
118
- </div>
119
-
120
- <!-- Form Elements -->
121
- <div class="card bg-base-100 shadow-xl">
122
- <div class="card-body">
123
- <h2 class="card-title">Form Elements</h2>
124
- <div class="form-control gap-4">
125
- <label class="input-group">
126
- <span>Email</span>
127
- <input type="text" placeholder="info@site.com" class="input input-bordered" />
128
- </label>
129
-
130
- <select class="select select-bordered w-full" bind:value={selected}>
131
- <option>Option 1</option>
132
- <option>Option 2</option>
133
- <option>Option 3</option>
134
- </select>
135
-
136
- <div class="flex gap-4">
137
- <label class="label cursor-pointer">
138
- <span class="label-text mr-2">Toggle</span>
139
- <input type="checkbox" class="toggle toggle-primary" />
140
- </label>
141
-
142
- <label class="label cursor-pointer">
143
- <span class="label-text mr-2">Radio</span>
144
- <input type="radio" name="radio-10" class="radio radio-primary" />
145
- </label>
75
+
76
+ <!-- Navigation Tabs -->
77
+ <div class="tabs tabs-boxed justify-center mb-8">
78
+ {#each tabs as tab, i}
79
+ <button
80
+ class="tab {activeTab === i ? 'tab-active' : ''}"
81
+ onclick={() => activeTab = i}
82
+ >
83
+ <tab.icon size={18} class="mr-2" />
84
+ {tab.title}
85
+ </button>
86
+ {/each}
87
+ </div>
88
+
89
+ <!-- Main Content -->
90
+ <div class="container mx-auto">
91
+ {#if activeTab === 0}
92
+ <!-- Basic UI Elements -->
93
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
94
+ <!-- Buttons Showcase -->
95
+ <div class="card bg-base-100 shadow-xl">
96
+ <div class="card-body">
97
+ <h2 class="card-title">Buttons</h2>
98
+ <div class="flex flex-wrap gap-2">
99
+ {#each colors as color}
100
+ <button class="btn btn-{color}">{color}</button>
101
+ {/each}
146
102
  </div>
147
-
148
- <input type="range" min="0" max="100" class="range range-primary" />
149
- </div>
150
- </div>
151
- </div>
152
- </div>
153
-
154
- {:else if activeTab === 1}
155
- <!-- Advanced Components -->
156
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
157
- <!-- Dropdown and Menu -->
158
- <div class="card bg-base-100 shadow-xl">
159
- <div class="card-body">
160
- <h2 class="card-title">Dropdowns & Menus</h2>
161
- <div class="flex flex-wrap gap-4">
162
- <div class="dropdown">
163
- <button class="btn m-1">Dropdown</button>
164
- <ul class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52">
165
- <li><button class="w-full text-left">Item 1</button></li>
166
- <li><button class="w-full text-left">Item 2</button></li>
167
- </ul>
103
+ <div class="divider">Sizes</div>
104
+ <div class="flex flex-wrap gap-2 items-center">
105
+ {#each sizes as size}
106
+ <button class="btn btn-{size}">{size}</button>
107
+ {/each}
168
108
  </div>
169
-
170
- <div class="menu bg-base-200 rounded-box">
171
- <li><button class="w-full text-left">Menu Item 1</button></li>
172
- <li><button class="w-full text-left">Menu Item 2</button></li>
109
+ <div class="divider">States</div>
110
+ <div class="flex flex-wrap gap-2">
111
+ <button class="btn btn-primary loading">Loading</button>
112
+ <button class="btn btn-disabled">Disabled</button>
113
+ <button class="btn btn-outline">Outline</button>
114
+ <button class="btn btn-link">Link</button>
173
115
  </div>
174
116
  </div>
175
117
  </div>
176
- </div>
177
-
178
- <!-- Steps -->
179
- <div class="card bg-base-100 shadow-xl">
180
- <div class="card-body">
181
- <h2 class="card-title">Steps</h2>
182
- <ul class="steps steps-vertical lg:steps-horizontal w-full">
183
- <li class="step step-primary">Register</li>
184
- <li class="step" class:step-primary={steps >= 2}>Choose plan</li>
185
- <li class="step" class:step-primary={steps >= 3}>Purchase</li>
186
- <li class="step" class:step-primary={steps === 4}>Receive Product</li>
187
- </ul>
188
- <div class="flex justify-center mt-4">
189
- <button class="btn btn-primary" onclick={() => steps = steps < 4 ? steps + 1 : 1}>
190
- Next Step
191
- </button>
192
- </div>
193
- </div>
194
- </div>
195
- </div>
196
-
197
- {:else if activeTab === 2}
198
- <!-- Layout Elements -->
199
- <div class="grid grid-cols-1 gap-6">
200
- <!-- Hero Section -->
201
- <div class="card bg-base-100 shadow-xl">
202
- <div class="card-body">
203
- <h2 class="card-title">Hero Section</h2>
204
- <div class="hero bg-base-200 rounded-box">
205
- <div class="hero-content text-center">
206
- <div class="max-w-md">
207
- <h1 class="text-5xl font-bold">Hello there</h1>
208
- <p class="py-6">This is a sample hero section with a button below.</p>
209
- <button class="btn btn-primary">Get Started</button>
118
+
119
+ <!-- Form Elements -->
120
+ <div class="card bg-base-100 shadow-xl">
121
+ <div class="card-body">
122
+ <h2 class="card-title">Form Elements</h2>
123
+ <div class="form-control gap-4">
124
+ <label class="input-group">
125
+ <span>Email</span>
126
+ <input type="text" placeholder="info@site.com" class="input input-bordered" />
127
+ </label>
128
+
129
+ <select class="select select-bordered w-full" bind:value={selected}>
130
+ <option>Option 1</option>
131
+ <option>Option 2</option>
132
+ <option>Option 3</option>
133
+ </select>
134
+
135
+ <div class="flex gap-4">
136
+ <label class="label cursor-pointer">
137
+ <span class="label-text mr-2">Toggle</span>
138
+ <input type="checkbox" class="toggle toggle-primary" />
139
+ </label>
140
+
141
+ <label class="label cursor-pointer">
142
+ <span class="label-text mr-2">Radio</span>
143
+ <input type="radio" name="radio-10" class="radio radio-primary" />
144
+ </label>
210
145
  </div>
146
+
147
+ <input type="range" min="0" max="100" class="range range-primary" />
211
148
  </div>
212
149
  </div>
213
150
  </div>
214
151
  </div>
215
-
216
- <!-- Grid Layout -->
217
- <div class="card bg-base-100 shadow-xl">
218
- <div class="card-body">
219
- <h2 class="card-title">Grid Layout</h2>
220
- <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
221
- {#each Array(3) as _, i}
222
- <div class="bg-primary text-primary-content p-4 rounded-box text-center">
223
- Grid Item {i + 1}
224
- </div>
225
- {/each}
226
- </div>
227
- </div>
228
- </div>
229
- </div>
230
-
231
- {:else if activeTab === 3}
232
- <!-- Data Display -->
233
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
234
- <!-- Stats -->
235
- <div class="card bg-base-100 shadow-xl">
236
- <div class="card-body">
237
- <h2 class="card-title">Statistics</h2>
238
- <div class="stats shadow">
239
- {#each stats as stat}
240
- <div class="stat">
241
- <div class="stat-title">{stat.title}</div>
242
- <div class="stat-value">{stat.value}</div>
243
- <div class="stat-desc">{stat.desc}</div>
244
- </div>
245
- {/each}
246
- </div>
247
- </div>
248
- </div>
249
-
250
- <!-- Table -->
251
- <div class="card bg-base-100 shadow-xl">
252
- <div class="card-body">
253
- <h2 class="card-title">Table</h2>
254
- <div class="overflow-x-auto">
255
- <table class="table">
256
- <thead>
257
- <tr>
258
- <th>Name</th>
259
- <th>Job</th>
260
- <th>Action</th>
261
- </tr>
262
- </thead>
263
- <tbody>
264
- <tr>
265
- <td>Cy Ganderton</td>
266
- <td>Quality Control Specialist</td>
267
- <td><button class="btn btn-xs">Details</button></td>
268
- </tr>
269
- <tr>
270
- <td>Hart Hagerty</td>
271
- <td>Desktop Support Technician</td>
272
- <td><button class="btn btn-xs">Details</button></td>
273
- </tr>
274
- </tbody>
275
- </table>
276
- </div>
152
+ {/if}
153
+ <!-- Other tab content would go here -->
154
+ </div>
155
+
156
+ <!-- Modal -->
157
+ {#if modalOpen}
158
+ <div class="modal modal-open">
159
+ <div class="modal-box">
160
+ <h3 class="font-bold text-lg">Modal Title</h3>
161
+ <p class="py-4">This is a sample modal dialog using DaisyUI.</p>
162
+ <div class="modal-action">
163
+ <button class="btn" onclick={() => modalOpen = false}>Close</button>
277
164
  </div>
278
165
  </div>
166
+ <button
167
+ class="modal-backdrop"
168
+ onclick={() => modalOpen = false}
169
+ onkeydown={(e) => e.key === 'Escape' && (modalOpen = false)}
170
+ aria-label="Close modal"
171
+ ></button>
279
172
  </div>
280
-
281
- {:else if activeTab === 4}
282
- <!-- Feedback Elements -->
283
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
284
- <!-- Alerts -->
285
- <div class="card bg-base-100 shadow-xl">
286
- <div class="card-body">
287
- <h2 class="card-title">Alerts</h2>
288
- <div class="flex flex-col gap-2">
289
- {#each colors as color}
290
- <div class="alert alert-{color}">
291
- <span>This is a {color} alert</span>
292
- </div>
293
- {/each}
294
- </div>
295
- </div>
296
- </div>
297
-
298
- <!-- Progress & Loading -->
299
- <div class="card bg-base-100 shadow-xl">
300
- <div class="card-body">
301
- <h2 class="card-title">Progress & Loading</h2>
302
- <progress class="progress w-full" value={progress} max="100"></progress>
303
- <div class="flex gap-2 mt-4">
304
- <button class="btn" onclick={simulateLoading}>
305
- {loading ? 'Loading...' : 'Simulate Loading'}
306
- </button>
307
- {#if loading}
308
- <span class="loading loading-spinner loading-lg"></span>
309
- {/if}
310
- </div>
311
- </div>
173
+ {/if}
174
+
175
+ <!-- Drawer -->
176
+ {#if drawerOpen}
177
+ <div class="drawer drawer-end drawer-open">
178
+ <input id="my-drawer" type="checkbox" class="drawer-toggle" checked />
179
+ <div class="drawer-side">
180
+ <button
181
+ class="drawer-overlay"
182
+ onclick={() => drawerOpen = false}
183
+ onkeydown={(e) => e.key === 'Escape' && (drawerOpen = false)}
184
+ aria-label="Close drawer"
185
+ ></button>
186
+ <ul class="menu p-4 w-80 min-h-full bg-base-200 text-base-content">
187
+ <li><button class="w-full text-left">Sidebar Item 1</button></li>
188
+ <li><button class="w-full text-left">Sidebar Item 2</button></li>
189
+ </ul>
312
190
  </div>
313
191
  </div>
314
192
  {/if}
315
- </div>
316
-
317
- <!-- Modal -->
318
- {#if modalOpen}
319
- <div class="modal modal-open">
320
- <div class="modal-box">
321
- <h3 class="font-bold text-lg">Modal Title</h3>
322
- <p class="py-4">This is a sample modal dialog using DaisyUI.</p>
323
- <div class="modal-action">
324
- <button class="btn" onclick={() => modalOpen = false}>Close</button>
193
+
194
+ <!-- Toast -->
195
+ {#if toast}
196
+ <div class="toast toast-end">
197
+ <div class="alert alert-info">
198
+ <span>{toast}</span>
325
199
  </div>
326
200
  </div>
327
- </div>
328
- {/if}
329
-
330
- <!-- Drawer -->
331
- {#if drawerOpen}
332
- <div class="drawer drawer-end">
333
- <input id="my-drawer" type="checkbox" class="drawer-toggle" checked />
334
- <div class="drawer-side">
335
- <button
336
- class="drawer-overlay"
337
- onclick={() => drawerOpen = false}
338
- onkeydown={(e) => e.key === 'Escape' && (drawerOpen = false)}
339
- >.</button>
340
- <ul class="menu p-4 w-80 min-h-full bg-base-200 text-base-content">
341
- <li><button class="w-full text-left">Sidebar Item 1</button></li>
342
- <li><button class="w-full text-left">Sidebar Item 2</button></li>
343
- </ul>
344
- </div>
345
- </div>
346
- {/if}
347
-
348
- <!-- Toast -->
349
- {#if toast}
350
- <div class="toast toast-end">
351
- <div class="alert alert-info">
352
- <span>{toast}</span>
201
+ {/if}
202
+
203
+ <!-- Footer -->
204
+ <footer class="footer footer-center p-4 bg-base-300 text-base-content mt-8">
205
+ <div>
206
+ <p>Made with 💝 using Svelte 5 and DaisyUI</p>
353
207
  </div>
354
- </div>
355
- {/if}
356
-
357
- <!-- Footer -->
358
- <footer class="footer footer-center p-4 bg-base-300 text-base-content mt-8">
359
- <div>
360
- <p>Made with 💝 using Svelte 5 and DaisyUI</p>
361
- </div>
362
- </footer>
363
- </div>
208
+ </footer>
209
+ </div>
@@ -1,11 +1,14 @@
1
1
  <script lang="ts">
2
+ import { Brush } from "lucide-svelte";
2
3
  import { themeStore } from '../../stores/theme.svelte.js';
3
-
4
+
4
5
  // Local state using runes
5
6
  let showDropdown = $state(false);
6
7
 
7
8
  // Initialize theme on mount
8
- themeStore.init();
9
+ $effect(() => {
10
+ themeStore.init();
11
+ });
9
12
 
10
13
  // Get available themes
11
14
  const themes = themeStore.getAvailableThemes();
@@ -18,23 +21,38 @@
18
21
 
19
22
  <div class="relative">
20
23
  <button
21
- class="btn"
24
+ class="btn btn-ghost btn-circle"
22
25
  onclick={() => showDropdown = !showDropdown}
26
+ aria-label="Select theme"
23
27
  >
24
- <span>{themeStore.themeConfig.icon}</span>
28
+ <span class="flex items-center gap-1">
29
+ {#if themeStore.themeConfig.icon}
30
+ <span>{themeStore.themeConfig.icon}</span>
31
+ {:else}
32
+ <Brush size={20} />
33
+ {/if}
34
+ <span>{themeStore.currentTheme}</span>
35
+ </span>
25
36
  </button>
26
37
 
27
38
  {#if showDropdown}
28
- <ul class="absolute right-0 top-full mt-2 min-w-[150px] p-2 bg-base-100 border border-base-300 rounded-lg shadow-lg">
39
+ <div
40
+ class="fixed inset-0 z-10"
41
+ role="button"
42
+ tabindex="0"
43
+ onclick={() => showDropdown = false}
44
+ onkeydown={(e) => e.key === 'Escape' && (showDropdown = false)}
45
+ ></div>
46
+ <ul class="absolute right-0 top-full mt-2 min-w-56 p-2 bg-base-100 border border-base-300 rounded-xl shadow-lg z-20 max-h-96 overflow-y-auto">
29
47
  {#each themes as theme}
30
48
  <li>
31
49
  <button
32
- class="w-full flex items-center gap-2 px-2 py-2 text-left hover:bg-base-200 transition-colors duration-200 rounded-lg
50
+ class="w-full flex items-center gap-2 px-3 py-2 text-left hover:bg-base-200 transition-colors duration-200 rounded-lg
33
51
  {themeStore.currentTheme === theme.name ? 'bg-base-300' : ''}"
34
52
  onclick={() => handleThemeSelect(theme.name)}
35
53
  >
36
54
  <span>{theme.icon}</span>
37
- <span>{theme.name}</span>
55
+ <span class="capitalize">{theme.name}</span>
38
56
  </button>
39
57
  </li>
40
58
  {/each}