@nucel/ui 0.1.0
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/README.md +235 -0
- package/package.json +88 -0
- package/src/lib/components/ui/Backdrop.svelte +19 -0
- package/src/lib/components/ui/CountBadge.svelte +40 -0
- package/src/lib/components/ui/EmptyState.svelte +68 -0
- package/src/lib/components/ui/KbdShortcut.svelte +20 -0
- package/src/lib/components/ui/MarkdownRenderer.svelte +85 -0
- package/src/lib/components/ui/ProgressRing.svelte +61 -0
- package/src/lib/components/ui/ProviderIcon.svelte +57 -0
- package/src/lib/components/ui/ReviewBadge.svelte +54 -0
- package/src/lib/components/ui/Sparkline.svelte +61 -0
- package/src/lib/components/ui/StatusBadge.svelte +32 -0
- package/src/lib/components/ui/StatusDot.svelte +65 -0
- package/src/lib/components/ui/TabBar.svelte +127 -0
- package/src/lib/components/ui/VerticalSeparator.svelte +9 -0
- package/src/lib/components/ui/accordion/accordion-content.svelte +22 -0
- package/src/lib/components/ui/accordion/accordion-item.svelte +17 -0
- package/src/lib/components/ui/accordion/accordion-trigger.svelte +32 -0
- package/src/lib/components/ui/accordion/accordion.svelte +16 -0
- package/src/lib/components/ui/accordion/index.ts +16 -0
- package/src/lib/components/ui/avatar/avatar-fallback.svelte +17 -0
- package/src/lib/components/ui/avatar/avatar-image.svelte +17 -0
- package/src/lib/components/ui/avatar/avatar.svelte +19 -0
- package/src/lib/components/ui/avatar/index.ts +13 -0
- package/src/lib/components/ui/badge/badge.svelte +49 -0
- package/src/lib/components/ui/badge/index.ts +2 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-item.svelte +20 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-link.svelte +31 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-list.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-page.svelte +23 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte +27 -0
- package/src/lib/components/ui/breadcrumb/breadcrumb.svelte +21 -0
- package/src/lib/components/ui/breadcrumb/index.ts +25 -0
- package/src/lib/components/ui/button/button.svelte +82 -0
- package/src/lib/components/ui/button/index.ts +17 -0
- package/src/lib/components/ui/card/card-action.svelte +20 -0
- package/src/lib/components/ui/card/card-content.svelte +15 -0
- package/src/lib/components/ui/card/card-description.svelte +20 -0
- package/src/lib/components/ui/card/card-footer.svelte +20 -0
- package/src/lib/components/ui/card/card-header.svelte +23 -0
- package/src/lib/components/ui/card/card-title.svelte +20 -0
- package/src/lib/components/ui/card/card.svelte +23 -0
- package/src/lib/components/ui/card/index.ts +25 -0
- package/src/lib/components/ui/collapsible/collapsible-content.svelte +7 -0
- package/src/lib/components/ui/collapsible/collapsible-trigger.svelte +7 -0
- package/src/lib/components/ui/collapsible/collapsible.svelte +11 -0
- package/src/lib/components/ui/collapsible/index.ts +13 -0
- package/src/lib/components/ui/dialog/dialog-close.svelte +7 -0
- package/src/lib/components/ui/dialog/dialog-content.svelte +45 -0
- package/src/lib/components/ui/dialog/dialog-description.svelte +17 -0
- package/src/lib/components/ui/dialog/dialog-footer.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-overlay.svelte +20 -0
- package/src/lib/components/ui/dialog/dialog-portal.svelte +7 -0
- package/src/lib/components/ui/dialog/dialog-title.svelte +17 -0
- package/src/lib/components/ui/dialog/dialog-trigger.svelte +7 -0
- package/src/lib/components/ui/dialog/dialog.svelte +7 -0
- package/src/lib/components/ui/dialog/index.ts +34 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +41 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +29 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +31 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
- package/src/lib/components/ui/dropdown-menu/index.ts +54 -0
- package/src/lib/components/ui/input/index.ts +7 -0
- package/src/lib/components/ui/input/input.svelte +52 -0
- package/src/lib/components/ui/label/index.ts +7 -0
- package/src/lib/components/ui/label/label.svelte +20 -0
- package/src/lib/components/ui/navigation-menu/index.ts +28 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-content.svelte +21 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-indicator.svelte +22 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-item.svelte +17 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-link.svelte +20 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-list.svelte +17 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-trigger.svelte +34 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu-viewport.svelte +22 -0
- package/src/lib/components/ui/navigation-menu/navigation-menu.svelte +32 -0
- package/src/lib/components/ui/progress/index.ts +1 -0
- package/src/lib/components/ui/progress/progress.svelte +33 -0
- package/src/lib/components/ui/scroll-area/index.ts +10 -0
- package/src/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte +31 -0
- package/src/lib/components/ui/scroll-area/scroll-area.svelte +43 -0
- package/src/lib/components/ui/select/index.ts +37 -0
- package/src/lib/components/ui/select/select-content.svelte +45 -0
- package/src/lib/components/ui/select/select-group-heading.svelte +21 -0
- package/src/lib/components/ui/select/select-group.svelte +7 -0
- package/src/lib/components/ui/select/select-item.svelte +38 -0
- package/src/lib/components/ui/select/select-label.svelte +20 -0
- package/src/lib/components/ui/select/select-portal.svelte +7 -0
- package/src/lib/components/ui/select/select-scroll-down-button.svelte +20 -0
- package/src/lib/components/ui/select/select-scroll-up-button.svelte +20 -0
- package/src/lib/components/ui/select/select-separator.svelte +18 -0
- package/src/lib/components/ui/select/select-trigger.svelte +29 -0
- package/src/lib/components/ui/select/select.svelte +11 -0
- package/src/lib/components/ui/separator/index.ts +7 -0
- package/src/lib/components/ui/separator/separator.svelte +21 -0
- package/src/lib/components/ui/sheet/index.ts +34 -0
- package/src/lib/components/ui/sheet/sheet-close.svelte +7 -0
- package/src/lib/components/ui/sheet/sheet-content.svelte +62 -0
- package/src/lib/components/ui/sheet/sheet-description.svelte +17 -0
- package/src/lib/components/ui/sheet/sheet-footer.svelte +20 -0
- package/src/lib/components/ui/sheet/sheet-header.svelte +20 -0
- package/src/lib/components/ui/sheet/sheet-overlay.svelte +20 -0
- package/src/lib/components/ui/sheet/sheet-portal.svelte +7 -0
- package/src/lib/components/ui/sheet/sheet-title.svelte +17 -0
- package/src/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
- package/src/lib/components/ui/sheet/sheet.svelte +7 -0
- package/src/lib/components/ui/skeleton/index.ts +1 -0
- package/src/lib/components/ui/skeleton/skeleton.svelte +17 -0
- package/src/lib/components/ui/sonner/index.ts +1 -0
- package/src/lib/components/ui/sonner/sonner.svelte +10 -0
- package/src/lib/components/ui/tabs/index.ts +16 -0
- package/src/lib/components/ui/tabs/tabs-content.svelte +17 -0
- package/src/lib/components/ui/tabs/tabs-list.svelte +16 -0
- package/src/lib/components/ui/tabs/tabs-trigger.svelte +20 -0
- package/src/lib/components/ui/tabs/tabs.svelte +19 -0
- package/src/lib/components/ui/textarea/index.ts +7 -0
- package/src/lib/components/ui/textarea/textarea.svelte +23 -0
- package/src/lib/components/ui/toggle/index.ts +13 -0
- package/src/lib/components/ui/toggle/toggle.svelte +52 -0
- package/src/lib/components/ui/toggle-group/index.ts +10 -0
- package/src/lib/components/ui/toggle-group/toggle-group-item.svelte +35 -0
- package/src/lib/components/ui/toggle-group/toggle-group.svelte +65 -0
- package/src/lib/components/ui/tooltip/index.ts +19 -0
- package/src/lib/components/ui/tooltip/tooltip-content.svelte +52 -0
- package/src/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/src/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/src/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/src/lib/components/ui/tooltip/tooltip.svelte +7 -0
- package/src/lib/index.ts +244 -0
- package/src/lib/utils/cn.test.ts +993 -0
- package/src/lib/utils/cn.ts +6 -0
- package/src/lib/utils.ts +12 -0
- package/src/styles.css +127 -0
package/README.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# @nucel/ui
|
|
2
|
+
|
|
3
|
+
A comprehensive Svelte 5 UI component library for Nucel projects.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **36 Components**: 24 shadcn-svelte primitives + 12 custom components
|
|
8
|
+
- **115 Component Files**: Full comprehensive coverage
|
|
9
|
+
- **Svelte 5**: Built with runes and modern Svelte patterns
|
|
10
|
+
- **TypeScript**: Full type safety
|
|
11
|
+
- **Tailwind CSS v4**: Seamless styling integration
|
|
12
|
+
- **Accessible**: Built on top of bits-ui for accessibility
|
|
13
|
+
- **Tree-shakeable**: Only import what you need
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @nucel/ui
|
|
19
|
+
# or
|
|
20
|
+
bun add @nucel/ui
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Peer Dependencies
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install svelte@^5.0.0 tailwindcss@^4.0.0
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```svelte
|
|
32
|
+
<script>
|
|
33
|
+
import {
|
|
34
|
+
Button,
|
|
35
|
+
Card,
|
|
36
|
+
CardContent,
|
|
37
|
+
CardHeader,
|
|
38
|
+
CardTitle,
|
|
39
|
+
StatusBadge,
|
|
40
|
+
Sparkline,
|
|
41
|
+
} from '@nucel/ui';
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<Card>
|
|
45
|
+
<CardHeader>
|
|
46
|
+
<CardTitle>Dashboard</CardTitle>
|
|
47
|
+
</CardHeader>
|
|
48
|
+
<CardContent>
|
|
49
|
+
<StatusBadge status="running" />
|
|
50
|
+
<Sparkline data={[10, 25, 15, 30, 20, 35, 28]} />
|
|
51
|
+
<Button>Action</Button>
|
|
52
|
+
</CardContent>
|
|
53
|
+
</Card>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Components
|
|
57
|
+
|
|
58
|
+
### shadcn-svelte Primitives (24)
|
|
59
|
+
|
|
60
|
+
- **Accordion**: Expandable/collapsible sections
|
|
61
|
+
- **Avatar**: User avatar with fallback
|
|
62
|
+
- **Badge**: Status badges with variants
|
|
63
|
+
- **Breadcrumb**: Navigation breadcrumbs
|
|
64
|
+
- **Button**: Action buttons with multiple variants
|
|
65
|
+
- **Card**: Content containers with header, content, footer
|
|
66
|
+
- **Collapsible**: Expandable/collapsible content
|
|
67
|
+
- **Dialog**: Modal dialogs with overlay
|
|
68
|
+
- **DropdownMenu**: Context menus and dropdowns
|
|
69
|
+
- **Input**: Text input fields
|
|
70
|
+
- **Label**: Form labels
|
|
71
|
+
- **NavigationMenu**: Top-level navigation menus
|
|
72
|
+
- **Progress**: Progress bars
|
|
73
|
+
- **ScrollArea**: Custom scrollable areas
|
|
74
|
+
- **Select**: Dropdown selects with search
|
|
75
|
+
- **Separator**: Visual dividers
|
|
76
|
+
- **Sheet**: Side panels and drawers
|
|
77
|
+
- **Skeleton**: Loading placeholders
|
|
78
|
+
- **Sonner**: Toast notifications
|
|
79
|
+
- **Tabs**: Tabbed interfaces
|
|
80
|
+
- **Textarea**: Multi-line text inputs
|
|
81
|
+
- **Toggle**: Toggle switches
|
|
82
|
+
- **ToggleGroup**: Grouped toggles
|
|
83
|
+
- **Tooltip**: Hover tooltips
|
|
84
|
+
|
|
85
|
+
### Custom Components (12)
|
|
86
|
+
|
|
87
|
+
- **Backdrop**: Fixed overlay backdrop for modals
|
|
88
|
+
- **CountBadge**: Animated count indicators (99+)
|
|
89
|
+
- **EmptyState**: Empty state placeholders
|
|
90
|
+
- **KbdShortcut**: Keyboard shortcut displays
|
|
91
|
+
- **MarkdownRenderer**: Sanitized markdown rendering
|
|
92
|
+
- **ProgressRing**: Circular progress indicator
|
|
93
|
+
- **ReviewBadge**: PR review status badges
|
|
94
|
+
- **Sparkline**: Mini line charts for data visualization
|
|
95
|
+
- **StatusBadge**: Combined status indicator with badge
|
|
96
|
+
- **StatusDot**: Animated status dots
|
|
97
|
+
- **TabBar**: Tab navigation with variants
|
|
98
|
+
- **VerticalSeparator**: Inline vertical dividers
|
|
99
|
+
|
|
100
|
+
## Usage Examples
|
|
101
|
+
|
|
102
|
+
### Dialog
|
|
103
|
+
|
|
104
|
+
```svelte
|
|
105
|
+
<script>
|
|
106
|
+
import {
|
|
107
|
+
Dialog,
|
|
108
|
+
DialogContent,
|
|
109
|
+
DialogHeader,
|
|
110
|
+
DialogTitle,
|
|
111
|
+
DialogDescription,
|
|
112
|
+
DialogFooter,
|
|
113
|
+
Button,
|
|
114
|
+
} from '@nucel/ui';
|
|
115
|
+
let open = $state(false);
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<Button onclick={() => (open = true)}>Open Dialog</Button>
|
|
119
|
+
|
|
120
|
+
<Dialog bind:open>
|
|
121
|
+
<DialogContent>
|
|
122
|
+
<DialogHeader>
|
|
123
|
+
<DialogTitle>Confirm Action</DialogTitle>
|
|
124
|
+
<DialogDescription>Are you sure you want to proceed?</DialogDescription>
|
|
125
|
+
</DialogHeader>
|
|
126
|
+
<DialogFooter>
|
|
127
|
+
<Button variant="outline" onclick={() => (open = false)}>Cancel</Button>
|
|
128
|
+
<Button onclick={() => (open = false)}>Confirm</Button>
|
|
129
|
+
</DialogFooter>
|
|
130
|
+
</DialogContent>
|
|
131
|
+
</Dialog>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Status Components
|
|
135
|
+
|
|
136
|
+
```svelte
|
|
137
|
+
<script>
|
|
138
|
+
import { StatusDot, StatusBadge } from '@nucel/ui';
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<!-- Simple status dot -->
|
|
142
|
+
<StatusDot color="bg-green-500" animated />
|
|
143
|
+
<StatusDot status="running" />
|
|
144
|
+
<StatusDot status="failed" size="sm" />
|
|
145
|
+
|
|
146
|
+
<!-- Combined status badge -->
|
|
147
|
+
<StatusBadge status="open" />
|
|
148
|
+
<StatusBadge status="merged" label="Merged by john" />
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Data Visualization
|
|
152
|
+
|
|
153
|
+
```svelte
|
|
154
|
+
<script>
|
|
155
|
+
import { Sparkline, ProgressRing } from '@nucel/ui';
|
|
156
|
+
</script>
|
|
157
|
+
|
|
158
|
+
<!-- Mini chart -->
|
|
159
|
+
<Sparkline data={[10, 25, 15, 30, 20]} color="stroke-blue-500" />
|
|
160
|
+
|
|
161
|
+
<!-- Circular progress -->
|
|
162
|
+
<ProgressRing spent={75} limit={100} size={24} />
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Navigation
|
|
166
|
+
|
|
167
|
+
```svelte
|
|
168
|
+
<script>
|
|
169
|
+
import { TabBar } from '@nucel/ui';
|
|
170
|
+
import { File, GitBranch, Settings } from '@lucide/svelte';
|
|
171
|
+
|
|
172
|
+
const items = [
|
|
173
|
+
{ id: 'files', label: 'Files', icon: File },
|
|
174
|
+
{ id: 'branches', label: 'Branches', icon: GitBranch, count: 3 },
|
|
175
|
+
{ id: 'settings', label: 'Settings', icon: Settings },
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
let selected = $state('files');
|
|
179
|
+
</script>
|
|
180
|
+
|
|
181
|
+
<TabBar {items} {selected} onselect={(id) => (selected = id)} variant="underline" />
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Styling
|
|
185
|
+
|
|
186
|
+
Components use Tailwind CSS v4 with the following theme structure:
|
|
187
|
+
|
|
188
|
+
```css
|
|
189
|
+
@theme {
|
|
190
|
+
--color-background: oklch(...);
|
|
191
|
+
--color-foreground: oklch(...);
|
|
192
|
+
--color-primary: oklch(...);
|
|
193
|
+
--color-secondary: oklch(...);
|
|
194
|
+
--color-destructive: oklch(...);
|
|
195
|
+
/* ... more theme variables */
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Import the styles in your app:
|
|
200
|
+
|
|
201
|
+
```css
|
|
202
|
+
@import '@nucel/ui/src/styles.css';
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Storybook
|
|
206
|
+
|
|
207
|
+
Explore all components with interactive examples:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
cd packages/ui
|
|
211
|
+
bun run storybook
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Install dependencies
|
|
218
|
+
bun install
|
|
219
|
+
|
|
220
|
+
# Run checks
|
|
221
|
+
bun run check
|
|
222
|
+
|
|
223
|
+
# Run linter
|
|
224
|
+
bun run lint
|
|
225
|
+
|
|
226
|
+
# Format code
|
|
227
|
+
bun run format
|
|
228
|
+
|
|
229
|
+
# Build Storybook
|
|
230
|
+
bun run build-storybook
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT © Nucel Team
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nucel/ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A comprehensive Svelte 5 UI component library for Nucel projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"svelte": "./src/lib/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/lib/index.ts",
|
|
10
|
+
"svelte": "./src/lib/index.ts",
|
|
11
|
+
"default": "./src/lib/index.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src/lib",
|
|
16
|
+
"src/styles.css"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"build": "vite build",
|
|
21
|
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
22
|
+
"lint": "eslint .",
|
|
23
|
+
"format": "prettier --write .",
|
|
24
|
+
"format:check": "prettier --check .",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"storybook": "storybook dev -p 6006",
|
|
28
|
+
"build-storybook": "storybook build",
|
|
29
|
+
"prepublishOnly": "echo 'publishing @nucel/ui'"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/nucel/ui.git"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"svelte",
|
|
37
|
+
"svelte5",
|
|
38
|
+
"ui",
|
|
39
|
+
"components",
|
|
40
|
+
"shadcn",
|
|
41
|
+
"tailwindcss",
|
|
42
|
+
"bits-ui",
|
|
43
|
+
"component-library"
|
|
44
|
+
],
|
|
45
|
+
"author": "Nucel Team",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@lucide/svelte": "^0.564.0",
|
|
49
|
+
"bits-ui": "^2.16.1",
|
|
50
|
+
"clsx": "^2.1.1",
|
|
51
|
+
"dompurify": "^3.3.3",
|
|
52
|
+
"marked": "^17.0.5",
|
|
53
|
+
"svelte-sonner": "^1.0.7",
|
|
54
|
+
"tailwind-merge": "^3.5.0",
|
|
55
|
+
"tailwind-variants": "^3.2.2"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@eslint/js": "^9.39.2",
|
|
59
|
+
"@storybook/addon-a11y": "^10.0.0",
|
|
60
|
+
"@storybook/addon-docs": "^10.0.0",
|
|
61
|
+
"@storybook/addon-svelte-csf": "^5.0.0",
|
|
62
|
+
"@storybook/addon-vitest": "^10.0.0",
|
|
63
|
+
"@storybook/svelte-vite": "^10.3.4",
|
|
64
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
65
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
66
|
+
"@types/dompurify": "^3.2.0",
|
|
67
|
+
"eslint": "^9.39.2",
|
|
68
|
+
"eslint-config-prettier": "^10.1.8",
|
|
69
|
+
"eslint-plugin-svelte": "^3.14.0",
|
|
70
|
+
"globals": "^17.3.0",
|
|
71
|
+
"prettier": "^3.8.1",
|
|
72
|
+
"prettier-plugin-svelte": "^3.4.1",
|
|
73
|
+
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
74
|
+
"storybook": "^10.0.0",
|
|
75
|
+
"svelte": "^5.49.2",
|
|
76
|
+
"svelte-check": "^4.3.6",
|
|
77
|
+
"tailwindcss": "^4.1.18",
|
|
78
|
+
"tw-animate-css": "^1.4.0",
|
|
79
|
+
"typescript": "^5.8.0",
|
|
80
|
+
"typescript-eslint": "^8.54.0",
|
|
81
|
+
"vite": "^8.0.0",
|
|
82
|
+
"vitest": "^4.0.18"
|
|
83
|
+
},
|
|
84
|
+
"peerDependencies": {
|
|
85
|
+
"svelte": "^5.0.0",
|
|
86
|
+
"tailwindcss": "^4.0.0"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
onclick,
|
|
4
|
+
zIndex = 40,
|
|
5
|
+
}: {
|
|
6
|
+
onclick?: () => void;
|
|
7
|
+
zIndex?: number;
|
|
8
|
+
} = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
12
|
+
<div
|
|
13
|
+
class="fixed inset-0"
|
|
14
|
+
style="z-index: {zIndex}"
|
|
15
|
+
{onclick}
|
|
16
|
+
onkeydown={(e) => {
|
|
17
|
+
if (e.key === 'Escape') onclick?.();
|
|
18
|
+
}}
|
|
19
|
+
></div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
count,
|
|
4
|
+
max = 99,
|
|
5
|
+
size = 'md',
|
|
6
|
+
variant = 'primary',
|
|
7
|
+
}: {
|
|
8
|
+
count: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
size?: 'sm' | 'md';
|
|
11
|
+
variant?: 'primary' | 'secondary' | 'destructive';
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
const display = $derived(count > max ? `${max}+` : `${count}`);
|
|
15
|
+
|
|
16
|
+
const sizeClass = $derived(
|
|
17
|
+
size === 'sm'
|
|
18
|
+
? 'h-3.5 min-w-[14px] text-[7px] px-0.5'
|
|
19
|
+
: 'h-[18px] min-w-[18px] text-[9px] px-1',
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const variantClass = $derived.by(() => {
|
|
23
|
+
switch (variant) {
|
|
24
|
+
case 'destructive':
|
|
25
|
+
return 'bg-destructive text-destructive-foreground shadow-sm shadow-destructive/30';
|
|
26
|
+
case 'secondary':
|
|
27
|
+
return 'bg-secondary text-secondary-foreground';
|
|
28
|
+
default:
|
|
29
|
+
return 'bg-primary text-primary-foreground shadow-sm shadow-primary/30';
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
{#if count > 0}
|
|
35
|
+
<span
|
|
36
|
+
class="flex items-center justify-center rounded-full font-bold {sizeClass} {variantClass} animate-in zoom-in-50 duration-200"
|
|
37
|
+
>
|
|
38
|
+
{display}
|
|
39
|
+
</span>
|
|
40
|
+
{/if}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet, Component } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
icon,
|
|
6
|
+
title,
|
|
7
|
+
description,
|
|
8
|
+
action,
|
|
9
|
+
variant = 'default',
|
|
10
|
+
}: {
|
|
11
|
+
icon?: Component<{ class?: string }>;
|
|
12
|
+
title: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
action?: Snippet;
|
|
15
|
+
variant?: 'default' | 'compact' | 'inline';
|
|
16
|
+
} = $props();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
{#if variant === 'inline'}
|
|
20
|
+
<div class="text-muted-foreground flex items-center gap-2 px-3 py-2 text-xs">
|
|
21
|
+
{#if icon}
|
|
22
|
+
{@const Icon = icon}
|
|
23
|
+
<Icon class="h-3.5 w-3.5 shrink-0" />
|
|
24
|
+
{/if}
|
|
25
|
+
<span>{title}</span>
|
|
26
|
+
</div>
|
|
27
|
+
{:else if variant === 'compact'}
|
|
28
|
+
<div class="flex flex-col items-center gap-1.5 px-4 py-6 text-center">
|
|
29
|
+
{#if icon}
|
|
30
|
+
{@const Icon = icon}
|
|
31
|
+
<div class="bg-secondary flex h-9 w-9 items-center justify-center rounded-lg">
|
|
32
|
+
<Icon class="text-muted-foreground h-4 w-4" />
|
|
33
|
+
</div>
|
|
34
|
+
{/if}
|
|
35
|
+
<p class="text-muted-foreground text-xs font-medium">{title}</p>
|
|
36
|
+
{#if description}
|
|
37
|
+
<p class="text-muted-foreground/70 max-w-[200px] text-[10px]">{description}</p>
|
|
38
|
+
{/if}
|
|
39
|
+
{#if action}
|
|
40
|
+
<div class="mt-1">
|
|
41
|
+
{@render action()}
|
|
42
|
+
</div>
|
|
43
|
+
{/if}
|
|
44
|
+
</div>
|
|
45
|
+
{:else}
|
|
46
|
+
<div class="flex flex-col items-center gap-2 px-6 py-10 text-center">
|
|
47
|
+
{#if icon}
|
|
48
|
+
{@const Icon = icon}
|
|
49
|
+
<div class="relative mb-1">
|
|
50
|
+
<div class="bg-secondary flex h-12 w-12 items-center justify-center rounded-xl">
|
|
51
|
+
<Icon class="text-muted-foreground h-5 w-5" />
|
|
52
|
+
</div>
|
|
53
|
+
<div
|
|
54
|
+
class="border-background bg-muted absolute -right-0.5 -bottom-0.5 h-3 w-3 rounded-full border-2"
|
|
55
|
+
></div>
|
|
56
|
+
</div>
|
|
57
|
+
{/if}
|
|
58
|
+
<p class="text-foreground/80 text-sm font-medium">{title}</p>
|
|
59
|
+
{#if description}
|
|
60
|
+
<p class="text-muted-foreground max-w-[260px] text-xs">{description}</p>
|
|
61
|
+
{/if}
|
|
62
|
+
{#if action}
|
|
63
|
+
<div class="mt-2">
|
|
64
|
+
{@render action()}
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
67
|
+
</div>
|
|
68
|
+
{/if}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
keys,
|
|
4
|
+
size = 'sm',
|
|
5
|
+
}: {
|
|
6
|
+
keys: string[];
|
|
7
|
+
size?: 'xs' | 'sm';
|
|
8
|
+
} = $props();
|
|
9
|
+
|
|
10
|
+
const sizeClass = $derived(size === 'xs' ? 'px-0.5 py-px text-[9px]' : 'px-1 py-0.5 text-[10px]');
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<span class="inline-flex items-center gap-0.5">
|
|
14
|
+
{#each keys as key, i (key)}
|
|
15
|
+
{#if i > 0}
|
|
16
|
+
<span class="text-muted-foreground/30 text-[10px]">+</span>
|
|
17
|
+
{/if}
|
|
18
|
+
<kbd class="bg-muted rounded {sizeClass} text-muted-foreground font-mono">{key}</kbd>
|
|
19
|
+
{/each}
|
|
20
|
+
</span>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { marked } from 'marked';
|
|
3
|
+
import DOMPurify from 'dompurify';
|
|
4
|
+
|
|
5
|
+
let { content }: { content: string } = $props();
|
|
6
|
+
|
|
7
|
+
let html = $derived(
|
|
8
|
+
DOMPurify.sanitize(marked.parse(content, { gfm: true, breaks: false }) as string),
|
|
9
|
+
);
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div class="md-body">
|
|
13
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
14
|
+
{@html html}
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<style>
|
|
18
|
+
/* :global() is required here to style injected {@html} content */
|
|
19
|
+
.md-body {
|
|
20
|
+
max-width: none;
|
|
21
|
+
}
|
|
22
|
+
.md-body :global(h1),
|
|
23
|
+
.md-body :global(h2),
|
|
24
|
+
.md-body :global(h3),
|
|
25
|
+
.md-body :global(h4) {
|
|
26
|
+
border-bottom: 1px solid var(--border);
|
|
27
|
+
padding-bottom: 0.5rem;
|
|
28
|
+
color: var(--foreground);
|
|
29
|
+
}
|
|
30
|
+
.md-body :global(code) {
|
|
31
|
+
background: var(--muted);
|
|
32
|
+
padding: 0.125rem 0.375rem;
|
|
33
|
+
border-radius: 0.25rem;
|
|
34
|
+
font-family: var(--font-mono, monospace);
|
|
35
|
+
font-size: 0.875em;
|
|
36
|
+
color: var(--foreground);
|
|
37
|
+
}
|
|
38
|
+
.md-body :global(pre) {
|
|
39
|
+
background: var(--background);
|
|
40
|
+
padding: 1rem;
|
|
41
|
+
border-radius: 0.375rem;
|
|
42
|
+
overflow-x: auto;
|
|
43
|
+
border: 1px solid var(--border);
|
|
44
|
+
}
|
|
45
|
+
.md-body :global(pre code) {
|
|
46
|
+
background: none;
|
|
47
|
+
padding: 0;
|
|
48
|
+
color: var(--muted-foreground);
|
|
49
|
+
}
|
|
50
|
+
.md-body :global(a) {
|
|
51
|
+
color: var(--primary);
|
|
52
|
+
}
|
|
53
|
+
.md-body :global(p) {
|
|
54
|
+
color: var(--muted-foreground);
|
|
55
|
+
line-height: 1.6;
|
|
56
|
+
}
|
|
57
|
+
.md-body :global(ul),
|
|
58
|
+
.md-body :global(ol) {
|
|
59
|
+
color: var(--muted-foreground);
|
|
60
|
+
}
|
|
61
|
+
.md-body :global(blockquote) {
|
|
62
|
+
border-left: 3px solid var(--border);
|
|
63
|
+
margin-left: 0;
|
|
64
|
+
padding-left: 1rem;
|
|
65
|
+
color: var(--muted-foreground);
|
|
66
|
+
}
|
|
67
|
+
.md-body :global(hr) {
|
|
68
|
+
border: none;
|
|
69
|
+
border-top: 1px solid var(--border);
|
|
70
|
+
}
|
|
71
|
+
.md-body :global(table) {
|
|
72
|
+
border-collapse: collapse;
|
|
73
|
+
width: 100%;
|
|
74
|
+
}
|
|
75
|
+
.md-body :global(th),
|
|
76
|
+
.md-body :global(td) {
|
|
77
|
+
border: 1px solid var(--border);
|
|
78
|
+
padding: 0.5rem 0.75rem;
|
|
79
|
+
color: var(--muted-foreground);
|
|
80
|
+
}
|
|
81
|
+
.md-body :global(th) {
|
|
82
|
+
background: var(--card);
|
|
83
|
+
color: var(--foreground);
|
|
84
|
+
}
|
|
85
|
+
</style>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn, type WithElementRef } from '../../utils.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
spent = 0,
|
|
8
|
+
limit = 0,
|
|
9
|
+
size = 16,
|
|
10
|
+
strokeWidth = 2,
|
|
11
|
+
class: className,
|
|
12
|
+
...restProps
|
|
13
|
+
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> & {
|
|
14
|
+
spent?: number;
|
|
15
|
+
limit?: number;
|
|
16
|
+
size?: number;
|
|
17
|
+
strokeWidth?: number;
|
|
18
|
+
} = $props();
|
|
19
|
+
|
|
20
|
+
const radius = $derived((size - strokeWidth) / 2);
|
|
21
|
+
const circumference = $derived(2 * Math.PI * radius);
|
|
22
|
+
const pct = $derived(limit > 0 ? Math.min(1, spent / limit) : 0);
|
|
23
|
+
const dashOffset = $derived(circumference * (1 - pct));
|
|
24
|
+
const color = $derived(
|
|
25
|
+
pct >= 0.8 ? 'stroke-destructive' : pct >= 0.5 ? 'stroke-warning' : 'stroke-success',
|
|
26
|
+
);
|
|
27
|
+
const bgColor = $derived(
|
|
28
|
+
pct >= 0.8 ? 'stroke-destructive/20' : pct >= 0.5 ? 'stroke-warning/20' : 'stroke-success/20',
|
|
29
|
+
);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
{#if limit > 0}
|
|
33
|
+
<span
|
|
34
|
+
bind:this={ref}
|
|
35
|
+
title="${spent.toFixed(2)} / ${limit.toFixed(2)} ({Math.round(pct * 100)}%)"
|
|
36
|
+
class={cn(className)}
|
|
37
|
+
{...restProps}
|
|
38
|
+
>
|
|
39
|
+
<svg width={size} height={size} class="shrink-0 -rotate-90">
|
|
40
|
+
<circle
|
|
41
|
+
cx={size / 2}
|
|
42
|
+
cy={size / 2}
|
|
43
|
+
r={radius}
|
|
44
|
+
fill="none"
|
|
45
|
+
stroke-width={strokeWidth}
|
|
46
|
+
class={bgColor}
|
|
47
|
+
/>
|
|
48
|
+
<circle
|
|
49
|
+
cx={size / 2}
|
|
50
|
+
cy={size / 2}
|
|
51
|
+
r={radius}
|
|
52
|
+
fill="none"
|
|
53
|
+
stroke-width={strokeWidth}
|
|
54
|
+
stroke-linecap="round"
|
|
55
|
+
stroke-dasharray={circumference}
|
|
56
|
+
stroke-dashoffset={dashOffset}
|
|
57
|
+
class="{color} transition-[stroke-dashoffset] duration-500 ease-out"
|
|
58
|
+
/>
|
|
59
|
+
</svg>
|
|
60
|
+
</span>
|
|
61
|
+
{/if}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils.js';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
provider,
|
|
6
|
+
class: className,
|
|
7
|
+
...restProps
|
|
8
|
+
}: {
|
|
9
|
+
provider: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
} = $props();
|
|
12
|
+
|
|
13
|
+
const cls = $derived(cn('h-4 w-4', className));
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
{#if provider === 'github' || provider === 'github_issues'}
|
|
17
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="GitHub" {...restProps}>
|
|
18
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/>
|
|
19
|
+
</svg>
|
|
20
|
+
{:else if provider === 'gitlab'}
|
|
21
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="GitLab" {...restProps}>
|
|
22
|
+
<path d="M22.65 14.39L12 22.13 1.35 14.39a.84.84 0 0 1-.3-.94l1.22-3.78 2.44-7.51A.42.42 0 0 1 4.82 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.49h8.1l2.44-7.51A.42.42 0 0 1 18.6 2a.43.43 0 0 1 .58 0 .42.42 0 0 1 .11.18l2.44 7.51 1.22 3.78a.84.84 0 0 1-.3.92z"/>
|
|
23
|
+
</svg>
|
|
24
|
+
{:else if provider === 'linear'}
|
|
25
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="Linear" {...restProps}>
|
|
26
|
+
<path d="M3.464 1.51 13.104 11.15A7.992 7.992 0 0 0 4.455 3.046l-.99-.99A.5.5 0 0 0 3.464 1.51zM2.1 3.697a.5.5 0 0 0 0 .707L13.596 15.9a8.027 8.027 0 0 0 1.017-1.696L3.515 3.105a.5.5 0 0 0-.707 0l-.707.592zM1.509 6.232a.5.5 0 0 0-.15.354v9.003a.5.5 0 0 0 .854.354L13.82 4.346A8.036 8.036 0 0 0 12 4a7.99 7.99 0 0 0-5.657 2.343L1.51 6.23a.5.5 0 0 0 0 0zM4 17.172l5.172 5.172A8 8 0 0 1 4 17.172zM1.359 10.24a.5.5 0 0 0 0 .707l11.697 11.697A8.03 8.03 0 0 0 15.654 21.4L2.066 10.24a.5.5 0 0 0-.707 0z"/>
|
|
27
|
+
</svg>
|
|
28
|
+
{:else if provider === 'jira'}
|
|
29
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="Jira" {...restProps}>
|
|
30
|
+
<path d="M11.975 0C5.962 0 .05 5.36.05 11.992c0 6.278 4.818 11.42 10.966 11.96v-3.8c-3.968-.512-7.006-3.91-7.006-7.983 0-4.44 3.602-8.04 8.04-8.04 4.438 0 8.04 3.6 8.04 8.04 0 3.96-2.862 7.276-6.65 7.93v-5.28h3.24l.93-3.84H13.44V9.73c0-1.12.55-2.2 2.3-2.2h1.78V4.21s-1.614-.276-3.158-.276c-3.22 0-5.32 1.952-5.32 5.49v2.57H5.63v3.84h3.41v5.424C4.01 20.3 0 16.46 0 11.993 0 5.36 5.36 0 12 0z"/>
|
|
31
|
+
<path d="M11.975 5.195C8.9 5.195 6.4 7.696 6.4 10.77h5.575v5.576c3.076 0 5.576-2.5 5.576-5.576s-2.5-5.575-5.576-5.575z"/>
|
|
32
|
+
</svg>
|
|
33
|
+
{:else if provider === 'bitbucket'}
|
|
34
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="Bitbucket" {...restProps}>
|
|
35
|
+
<path d="M.778 1.213a.768.768 0 0 0-.768.892l3.263 19.81c.084.5.515.868 1.022.873H19.95a.772.772 0 0 0 .77-.646l3.27-20.03a.768.768 0 0 0-.768-.891zM14.52 15.53H9.522L8.17 8.466h7.561z"/>
|
|
36
|
+
</svg>
|
|
37
|
+
{:else if provider === 'plane'}
|
|
38
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="Plane" {...restProps}>
|
|
39
|
+
<path d="M22.2 2.4 1.8 9.6l8.4 2.4 2.4 8.4 2.4-7.2 7.2-10.8z"/>
|
|
40
|
+
</svg>
|
|
41
|
+
{:else if provider === 'digitalocean' || provider === 'do'}
|
|
42
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="DigitalOcean" {...restProps}>
|
|
43
|
+
<path d="M12.04 0C5.408 0 .05 5.36.05 11.992c0 6.278 4.818 11.42 10.966 11.96v-3.8c-3.968-.512-7.006-3.91-7.006-7.983 0-4.44 3.602-8.04 8.04-8.04 4.438 0 8.04 3.6 8.04 8.04 0 3.96-2.862 7.276-6.65 7.93v-5.28h3.24l.93-3.84H13.44V9.73c0-1.12.55-2.2 2.3-2.2h1.78V4.21s-1.614-.276-3.158-.276c-3.22 0-5.32 1.952-5.32 5.49v2.57H5.63v3.84h3.41v5.424C4.01 20.3 0 16.46 0 11.993 0 5.36 5.36 0 12 0z"/>
|
|
44
|
+
</svg>
|
|
45
|
+
{:else if provider === 'azure' || provider === 'azuredevops'}
|
|
46
|
+
<svg class={cls} viewBox="0 0 24 24" fill="currentColor" aria-label="Azure DevOps" {...restProps}>
|
|
47
|
+
<path d="M0 5.26l2.65-.37 8.54 9.24v-8.5l-2.47-.34L0 5.26zm23.99 3.44L14.6 0l-5.4 4.82 8.12 2.65v9.75l6.67-8.52zM8.85 14.01L6.47 24l4.8-1.7 5.66-8.25-8.08-.04z"/>
|
|
48
|
+
</svg>
|
|
49
|
+
{:else}
|
|
50
|
+
<!-- Generic git/provider icon -->
|
|
51
|
+
<svg class={cls} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-label="Provider" {...restProps}>
|
|
52
|
+
<circle cx="18" cy="18" r="3"/>
|
|
53
|
+
<circle cx="6" cy="6" r="3"/>
|
|
54
|
+
<circle cx="6" cy="18" r="3"/>
|
|
55
|
+
<path d="M6 9v6M18 15v1a3 3 0 0 1-3 3H9"/>
|
|
56
|
+
</svg>
|
|
57
|
+
{/if}
|