yaxa-svelte 1.0.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/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/assets/favicon.svg +15 -0
- package/dist/components/elements/Avatar.svelte +106 -0
- package/dist/components/elements/Avatar.svelte.d.ts +40 -0
- package/dist/components/elements/AvatarGroup.svelte +18 -0
- package/dist/components/elements/AvatarGroup.svelte.d.ts +8 -0
- package/dist/components/elements/Badge.svelte +202 -0
- package/dist/components/elements/Badge.svelte.d.ts +75 -0
- package/dist/components/elements/Button.svelte +300 -0
- package/dist/components/elements/Button.svelte.d.ts +109 -0
- package/dist/components/elements/ButtonGroup.svelte +22 -0
- package/dist/components/elements/ButtonGroup.svelte.d.ts +9 -0
- package/dist/components/elements/Chip.svelte +117 -0
- package/dist/components/elements/Chip.svelte.d.ts +15 -0
- package/dist/components/elements/DataTable.svelte +222 -0
- package/dist/components/elements/DataTable.svelte.d.ts +43 -0
- package/dist/components/elements/Icon.svelte +87 -0
- package/dist/components/elements/Icon.svelte.d.ts +10 -0
- package/dist/components/elements/Kbd.svelte +37 -0
- package/dist/components/elements/Kbd.svelte.d.ts +32 -0
- package/dist/components/elements/Link.svelte +63 -0
- package/dist/components/elements/Link.svelte.d.ts +15 -0
- package/dist/components/elements/Logo.svelte +77 -0
- package/dist/components/elements/Logo.svelte.d.ts +9 -0
- package/dist/components/elements/Meter.svelte +87 -0
- package/dist/components/elements/Meter.svelte.d.ts +14 -0
- package/dist/components/elements/Progress.svelte +63 -0
- package/dist/components/elements/Progress.svelte.d.ts +49 -0
- package/dist/components/elements/Skeleton.svelte +12 -0
- package/dist/components/elements/Skeleton.svelte.d.ts +6 -0
- package/dist/components/elements/Spinner.svelte +39 -0
- package/dist/components/elements/Spinner.svelte.d.ts +7 -0
- package/dist/components/forms/Checkbox.svelte +77 -0
- package/dist/components/forms/Checkbox.svelte.d.ts +17 -0
- package/dist/components/forms/ColorPicker.svelte +142 -0
- package/dist/components/forms/ColorPicker.svelte.d.ts +10 -0
- package/dist/components/forms/Form.svelte +84 -0
- package/dist/components/forms/Form.svelte.d.ts +14 -0
- package/dist/components/forms/FormField.svelte +56 -0
- package/dist/components/forms/FormField.svelte.d.ts +14 -0
- package/dist/components/forms/Input.svelte +138 -0
- package/dist/components/forms/Input.svelte.d.ts +58 -0
- package/dist/components/forms/RadioGroup.svelte +74 -0
- package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
- package/dist/components/forms/Select.svelte +91 -0
- package/dist/components/forms/Select.svelte.d.ts +50 -0
- package/dist/components/forms/Slider.svelte +37 -0
- package/dist/components/forms/Slider.svelte.d.ts +12 -0
- package/dist/components/forms/Switch.svelte +81 -0
- package/dist/components/forms/Switch.svelte.d.ts +53 -0
- package/dist/components/forms/Textarea.svelte +73 -0
- package/dist/components/forms/Textarea.svelte.d.ts +64 -0
- package/dist/components/layout/Card.svelte +89 -0
- package/dist/components/layout/Card.svelte.d.ts +64 -0
- package/dist/components/layout/Container.svelte +36 -0
- package/dist/components/layout/Container.svelte.d.ts +37 -0
- package/dist/components/layout/Divider.svelte +45 -0
- package/dist/components/layout/Divider.svelte.d.ts +11 -0
- package/dist/components/layout/Footer.svelte +96 -0
- package/dist/components/layout/Footer.svelte.d.ts +10 -0
- package/dist/components/layout/Header.svelte +175 -0
- package/dist/components/layout/Header.svelte.d.ts +12 -0
- package/dist/components/layout/Section.svelte +62 -0
- package/dist/components/layout/Section.svelte.d.ts +14 -0
- package/dist/components/layout/YaxaApp.svelte +48 -0
- package/dist/components/layout/YaxaApp.svelte.d.ts +12 -0
- package/dist/components/navigation/Breadcrumb.svelte +58 -0
- package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
- package/dist/components/navigation/CommandPalette.svelte +201 -0
- package/dist/components/navigation/CommandPalette.svelte.d.ts +19 -0
- package/dist/components/navigation/DropdownMenu.svelte +75 -0
- package/dist/components/navigation/DropdownMenu.svelte.d.ts +20 -0
- package/dist/components/navigation/Pagination.svelte +106 -0
- package/dist/components/navigation/Pagination.svelte.d.ts +10 -0
- package/dist/components/navigation/Tabs.svelte +91 -0
- package/dist/components/navigation/Tabs.svelte.d.ts +18 -0
- package/dist/components/overlays/Accordion.svelte +54 -0
- package/dist/components/overlays/Accordion.svelte.d.ts +16 -0
- package/dist/components/overlays/Alert.svelte +110 -0
- package/dist/components/overlays/Alert.svelte.d.ts +37 -0
- package/dist/components/overlays/ContextMenu.svelte +67 -0
- package/dist/components/overlays/ContextMenu.svelte.d.ts +21 -0
- package/dist/components/overlays/Modal.svelte +99 -0
- package/dist/components/overlays/Modal.svelte.d.ts +40 -0
- package/dist/components/overlays/Popover.svelte +33 -0
- package/dist/components/overlays/Popover.svelte.d.ts +11 -0
- package/dist/components/overlays/Slideover.svelte +102 -0
- package/dist/components/overlays/Slideover.svelte.d.ts +37 -0
- package/dist/components/overlays/Tooltip.svelte +39 -0
- package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
- package/dist/components/saas/AuthCard.svelte +273 -0
- package/dist/components/saas/AuthCard.svelte.d.ts +16 -0
- package/dist/components/saas/PricingCard.svelte +166 -0
- package/dist/components/saas/PricingCard.svelte.d.ts +19 -0
- package/dist/components/saas/PricingTable.svelte +139 -0
- package/dist/components/saas/PricingTable.svelte.d.ts +11 -0
- package/dist/components/saas/SubscriptionCard.svelte +162 -0
- package/dist/components/saas/SubscriptionCard.svelte.d.ts +16 -0
- package/dist/components/saas/UserMenu.svelte +137 -0
- package/dist/components/saas/UserMenu.svelte.d.ts +19 -0
- package/dist/components/seo/Favicons.svelte +22 -0
- package/dist/components/seo/Favicons.svelte.d.ts +8 -0
- package/dist/components/seo/OgImage.svelte +61 -0
- package/dist/components/seo/OgImage.svelte.d.ts +12 -0
- package/dist/components/seo/Seo.svelte +170 -0
- package/dist/components/seo/Seo.svelte.d.ts +21 -0
- package/dist/composables/useAuth.svelte.d.ts +23 -0
- package/dist/composables/useAuth.svelte.js +157 -0
- package/dist/composables/useClipboard.svelte.d.ts +9 -0
- package/dist/composables/useClipboard.svelte.js +36 -0
- package/dist/composables/useColorMode.svelte.d.ts +8 -0
- package/dist/composables/useColorMode.svelte.js +20 -0
- package/dist/composables/useDebounce.svelte.d.ts +3 -0
- package/dist/composables/useDebounce.svelte.js +17 -0
- package/dist/composables/useMediaQuery.svelte.d.ts +3 -0
- package/dist/composables/useMediaQuery.svelte.js +17 -0
- package/dist/composables/useShortcuts.svelte.d.ts +10 -0
- package/dist/composables/useShortcuts.svelte.js +50 -0
- package/dist/composables/useToast.d.ts +19 -0
- package/dist/composables/useToast.js +34 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +92 -0
- package/dist/server/auth/hook.d.ts +14 -0
- package/dist/server/auth/hook.js +36 -0
- package/dist/server/auth/index.d.ts +39 -0
- package/dist/server/auth/index.js +69 -0
- package/dist/server/db/index.d.ts +18 -0
- package/dist/server/db/index.js +43 -0
- package/dist/server/db/schema-pg.d.ts +977 -0
- package/dist/server/db/schema-pg.js +83 -0
- package/dist/server/db/schema-sqlite.d.ts +1045 -0
- package/dist/server/db/schema-sqlite.js +83 -0
- package/dist/server/db/types.d.ts +44 -0
- package/dist/server/db/types.js +1 -0
- package/dist/server/email/index.d.ts +27 -0
- package/dist/server/email/index.js +79 -0
- package/dist/server/polar/index.d.ts +29 -0
- package/dist/server/polar/index.js +49 -0
- package/dist/server/polar/webhook.d.ts +16 -0
- package/dist/server/polar/webhook.js +118 -0
- package/dist/site/config.d.ts +63 -0
- package/dist/site/config.js +48 -0
- package/dist/site/context.d.ts +11 -0
- package/dist/site/context.js +23 -0
- package/dist/site/hook.d.ts +15 -0
- package/dist/site/hook.js +38 -0
- package/dist/site/manifest.d.ts +6 -0
- package/dist/site/manifest.js +32 -0
- package/dist/site/og.d.ts +14 -0
- package/dist/site/og.js +116 -0
- package/dist/site/robots.d.ts +10 -0
- package/dist/site/robots.js +54 -0
- package/dist/site/schema.d.ts +67 -0
- package/dist/site/schema.js +67 -0
- package/dist/site/seo-helpers.d.ts +36 -0
- package/dist/site/seo-helpers.js +21 -0
- package/dist/site/sitemap-xsl.d.ts +6 -0
- package/dist/site/sitemap-xsl.js +164 -0
- package/dist/site/sitemap.d.ts +20 -0
- package/dist/site/sitemap.js +61 -0
- package/dist/styles/yaxa.css +84 -0
- package/dist/theme/theme.svelte.d.ts +74 -0
- package/dist/theme/theme.svelte.js +421 -0
- package/dist/utils/cn.d.ts +3 -0
- package/dist/utils/cn.js +3 -0
- package/dist/vite/index.d.ts +20 -0
- package/dist/vite/index.js +29 -0
- package/package.json +108 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { SiteConfig, NavItem } from '../../site/config';
|
|
4
|
+
import { theme } from '../../theme/theme.svelte';
|
|
5
|
+
import Container from './Container.svelte';
|
|
6
|
+
import Button from '../elements/Button.svelte';
|
|
7
|
+
import Icon from '../elements/Icon.svelte';
|
|
8
|
+
import Kbd from '../elements/Kbd.svelte';
|
|
9
|
+
import Link from '../elements/Link.svelte';
|
|
10
|
+
import Logo from '../elements/Logo.svelte';
|
|
11
|
+
import { getSiteConfig } from '../../site/context';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
config?: SiteConfig;
|
|
15
|
+
links?: NavItem[];
|
|
16
|
+
onOpenCommand?: () => void;
|
|
17
|
+
actions?: Snippet;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { config, links, onOpenCommand, actions, class: className = '' }: Props = $props();
|
|
22
|
+
|
|
23
|
+
let currentConfig = $derived(config || getSiteConfig());
|
|
24
|
+
let resolvedLinks = $derived(links || currentConfig.nav || []);
|
|
25
|
+
|
|
26
|
+
let mobileMenuOpen = $state(false);
|
|
27
|
+
let isScrolled = $state(false);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<svelte:window
|
|
31
|
+
onscroll={() => {
|
|
32
|
+
if (typeof window !== 'undefined') {
|
|
33
|
+
isScrolled = window.scrollY > 15;
|
|
34
|
+
}
|
|
35
|
+
}}
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
<header
|
|
39
|
+
class="sticky top-0 z-40 w-full transition-all duration-200 {isScrolled
|
|
40
|
+
? 'border-b border-neutral-200/80 bg-white/95 shadow-xs backdrop-blur-md dark:border-neutral-800/80 dark:bg-[#121212]/95'
|
|
41
|
+
: 'border-b border-transparent bg-white/60 backdrop-blur-sm dark:border-transparent dark:bg-[#121212]/60'} {className}"
|
|
42
|
+
>
|
|
43
|
+
<Container size="2xl">
|
|
44
|
+
<div class="flex h-16 items-center justify-between gap-4">
|
|
45
|
+
<!-- Logo, Brand & Version Pill -->
|
|
46
|
+
<div class="flex items-center gap-6">
|
|
47
|
+
<div class="flex items-center gap-2.5">
|
|
48
|
+
<a
|
|
49
|
+
href="/"
|
|
50
|
+
class="flex items-center gap-2.5 font-bold tracking-tight text-neutral-900 transition-opacity hover:opacity-90 dark:text-white"
|
|
51
|
+
>
|
|
52
|
+
<Logo size="sm" />
|
|
53
|
+
<span class="text-base font-extrabold tracking-tight">{currentConfig.name}</span>
|
|
54
|
+
</a>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- Desktop Navigation Links -->
|
|
58
|
+
<nav class="hidden items-center gap-1 md:flex">
|
|
59
|
+
{#each resolvedLinks as item}
|
|
60
|
+
<Link
|
|
61
|
+
href={item.href}
|
|
62
|
+
class="rounded-md px-3 py-1.5 text-sm font-medium text-neutral-600 transition-colors hover:text-neutral-900 dark:text-neutral-300 dark:hover:text-white"
|
|
63
|
+
activeClass="text-primary-600 dark:text-primary-400 font-semibold"
|
|
64
|
+
>
|
|
65
|
+
{#if item.icon}
|
|
66
|
+
<Icon name={item.icon} size="xs" />
|
|
67
|
+
{/if}
|
|
68
|
+
{item.label}
|
|
69
|
+
{#if item.badge}
|
|
70
|
+
<span
|
|
71
|
+
class="ml-1 rounded bg-primary-100 px-1.5 py-0.5 text-xs font-semibold text-primary-700 dark:bg-primary-950 dark:text-primary-300"
|
|
72
|
+
>
|
|
73
|
+
{item.badge}
|
|
74
|
+
</span>
|
|
75
|
+
{/if}
|
|
76
|
+
</Link>
|
|
77
|
+
{/each}
|
|
78
|
+
</nav>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<!-- Right Section: Search, Theme Toggle, Actions, Socials -->
|
|
82
|
+
<div class="flex items-center gap-2">
|
|
83
|
+
{#if onOpenCommand}
|
|
84
|
+
<button
|
|
85
|
+
onclick={onOpenCommand}
|
|
86
|
+
type="button"
|
|
87
|
+
class="hidden items-center gap-2 rounded-lg border border-neutral-200/80 bg-neutral-100/60 px-3 py-1.5 text-sm text-neutral-500 transition-colors hover:border-neutral-300 hover:bg-neutral-100 sm:flex dark:border-neutral-800 dark:bg-neutral-900/60 dark:text-neutral-400 dark:hover:border-neutral-700 dark:hover:bg-neutral-800"
|
|
88
|
+
aria-label="Search or run command"
|
|
89
|
+
>
|
|
90
|
+
<Icon name="search" size="xs" />
|
|
91
|
+
<span>Search documentation...</span>
|
|
92
|
+
<Kbd value="CTRL K" size="xs" class="ml-3" />
|
|
93
|
+
</button>
|
|
94
|
+
{/if}
|
|
95
|
+
|
|
96
|
+
<!-- Theme Mode Switcher -->
|
|
97
|
+
<Button
|
|
98
|
+
variant="ghost"
|
|
99
|
+
color="neutral"
|
|
100
|
+
square
|
|
101
|
+
size="sm"
|
|
102
|
+
onclick={() => theme.toggle()}
|
|
103
|
+
aria-label="Toggle color mode"
|
|
104
|
+
>
|
|
105
|
+
{#if theme.resolvedTheme === 'dark'}
|
|
106
|
+
<Icon name="sun" size="sm" class="text-amber-400" />
|
|
107
|
+
{:else}
|
|
108
|
+
<Icon name="moon" size="sm" class="text-neutral-600" />
|
|
109
|
+
{/if}
|
|
110
|
+
</Button>
|
|
111
|
+
|
|
112
|
+
<!-- GitHub Link -->
|
|
113
|
+
{#if currentConfig.socials?.github}
|
|
114
|
+
<Button
|
|
115
|
+
href={currentConfig.socials.github}
|
|
116
|
+
variant="ghost"
|
|
117
|
+
color="neutral"
|
|
118
|
+
square
|
|
119
|
+
size="sm"
|
|
120
|
+
aria-label="GitHub Repository"
|
|
121
|
+
>
|
|
122
|
+
<Icon name="github" size="sm" />
|
|
123
|
+
</Button>
|
|
124
|
+
{/if}
|
|
125
|
+
|
|
126
|
+
{#if actions}
|
|
127
|
+
{@render actions()}
|
|
128
|
+
{/if}
|
|
129
|
+
|
|
130
|
+
<!-- Mobile menu button -->
|
|
131
|
+
<div class="flex md:hidden">
|
|
132
|
+
<Button
|
|
133
|
+
variant="ghost"
|
|
134
|
+
color="neutral"
|
|
135
|
+
square
|
|
136
|
+
size="sm"
|
|
137
|
+
onclick={() => (mobileMenuOpen = !mobileMenuOpen)}
|
|
138
|
+
aria-label="Toggle navigation menu"
|
|
139
|
+
>
|
|
140
|
+
{#if mobileMenuOpen}
|
|
141
|
+
<Icon name="cross" size="sm" />
|
|
142
|
+
{:else}
|
|
143
|
+
<svg
|
|
144
|
+
class="h-5 w-5"
|
|
145
|
+
fill="none"
|
|
146
|
+
viewBox="0 0 24 24"
|
|
147
|
+
stroke="currentColor"
|
|
148
|
+
stroke-width="2"
|
|
149
|
+
>
|
|
150
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
|
|
151
|
+
</svg>
|
|
152
|
+
{/if}
|
|
153
|
+
</Button>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- Mobile Navigation Dropdown -->
|
|
159
|
+
{#if mobileMenuOpen}
|
|
160
|
+
<div class="border-t border-neutral-200 py-4 md:hidden dark:border-neutral-800">
|
|
161
|
+
<div class="flex flex-col space-y-2">
|
|
162
|
+
{#each resolvedLinks as item}
|
|
163
|
+
<a
|
|
164
|
+
href={item.href}
|
|
165
|
+
onclick={() => (mobileMenuOpen = false)}
|
|
166
|
+
class="rounded-lg px-3 py-2 text-base font-medium text-neutral-700 hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
|
|
167
|
+
>
|
|
168
|
+
{item.label}
|
|
169
|
+
</a>
|
|
170
|
+
{/each}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
{/if}
|
|
174
|
+
</Container>
|
|
175
|
+
</header>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { SiteConfig, NavItem } from '../../site/config';
|
|
3
|
+
interface Props {
|
|
4
|
+
config?: SiteConfig;
|
|
5
|
+
links?: NavItem[];
|
|
6
|
+
onOpenCommand?: () => void;
|
|
7
|
+
actions?: Snippet;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const Header: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type Header = ReturnType<typeof Header>;
|
|
12
|
+
export default Header;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import Container from './Container.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
id?: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
badge?: string;
|
|
10
|
+
align?: 'left' | 'center';
|
|
11
|
+
class?: string;
|
|
12
|
+
header?: Snippet;
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
id,
|
|
18
|
+
title,
|
|
19
|
+
description,
|
|
20
|
+
badge,
|
|
21
|
+
align = 'center',
|
|
22
|
+
class: className = '',
|
|
23
|
+
header,
|
|
24
|
+
children
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<section {id} class="py-16 sm:py-24 {className}">
|
|
29
|
+
<Container>
|
|
30
|
+
{#if header}
|
|
31
|
+
{@render header()}
|
|
32
|
+
{:else if title || description || badge}
|
|
33
|
+
<div
|
|
34
|
+
class="mb-12 {align === 'center' ? 'mx-auto max-w-3xl text-center' : 'max-w-2xl text-left'}"
|
|
35
|
+
>
|
|
36
|
+
{#if badge}
|
|
37
|
+
<span
|
|
38
|
+
class="mb-4 inline-flex items-center rounded-full bg-primary-50 px-3 py-1 text-sm font-semibold text-primary-700 ring-1 ring-primary-500/20 dark:bg-primary-950/60 dark:text-primary-300 dark:ring-primary-500/30"
|
|
39
|
+
>
|
|
40
|
+
{badge}
|
|
41
|
+
</span>
|
|
42
|
+
{/if}
|
|
43
|
+
{#if title}
|
|
44
|
+
<h2
|
|
45
|
+
class="text-3xl font-extrabold tracking-tight text-neutral-900 sm:text-4xl dark:text-white"
|
|
46
|
+
>
|
|
47
|
+
{title}
|
|
48
|
+
</h2>
|
|
49
|
+
{/if}
|
|
50
|
+
{#if description}
|
|
51
|
+
<p class="mt-4 text-lg text-neutral-600 dark:text-neutral-400">
|
|
52
|
+
{description}
|
|
53
|
+
</p>
|
|
54
|
+
{/if}
|
|
55
|
+
</div>
|
|
56
|
+
{/if}
|
|
57
|
+
|
|
58
|
+
{#if children}
|
|
59
|
+
{@render children()}
|
|
60
|
+
{/if}
|
|
61
|
+
</Container>
|
|
62
|
+
</section>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
id?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
badge?: string;
|
|
7
|
+
align?: 'left' | 'center';
|
|
8
|
+
class?: string;
|
|
9
|
+
header?: Snippet;
|
|
10
|
+
children?: Snippet;
|
|
11
|
+
}
|
|
12
|
+
declare const Section: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Section = ReturnType<typeof Section>;
|
|
14
|
+
export default Section;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { Toaster } from 'svelte-sonner';
|
|
4
|
+
import { Tooltip } from 'bits-ui';
|
|
5
|
+
import { type SiteConfig, DEFAULT_SITE_CONFIG } from '../../site/config';
|
|
6
|
+
import { themeInitScript } from '../../theme/theme.svelte';
|
|
7
|
+
import Favicons from '../seo/Favicons.svelte';
|
|
8
|
+
import Seo from '../seo/Seo.svelte';
|
|
9
|
+
import { setSiteConfig } from '../../site/context';
|
|
10
|
+
import '../../styles/yaxa.css';
|
|
11
|
+
|
|
12
|
+
export interface YaxaAppProps {
|
|
13
|
+
config?: SiteConfig;
|
|
14
|
+
enableDefaultSeo?: boolean;
|
|
15
|
+
children?: Snippet;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type Props = YaxaAppProps;
|
|
19
|
+
|
|
20
|
+
let { config = DEFAULT_SITE_CONFIG, enableDefaultSeo = true, children }: YaxaAppProps = $props();
|
|
21
|
+
|
|
22
|
+
// Provide config to all descendant components via Svelte 5 context
|
|
23
|
+
setSiteConfig(() => config);
|
|
24
|
+
|
|
25
|
+
const scriptTag = `<script>${themeInitScript}</` + `script>`;
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<svelte:head>
|
|
29
|
+
{@html scriptTag}
|
|
30
|
+
</svelte:head>
|
|
31
|
+
|
|
32
|
+
{#if enableDefaultSeo}
|
|
33
|
+
<Seo {config} />
|
|
34
|
+
{/if}
|
|
35
|
+
<Favicons {config} />
|
|
36
|
+
|
|
37
|
+
<Tooltip.Provider delayDuration={200}>
|
|
38
|
+
<div
|
|
39
|
+
class="flex min-h-screen flex-col bg-white text-neutral-900 transition-colors dark:bg-neutral-950 dark:text-neutral-50"
|
|
40
|
+
>
|
|
41
|
+
{#if children}
|
|
42
|
+
{@render children()}
|
|
43
|
+
{/if}
|
|
44
|
+
</div>
|
|
45
|
+
</Tooltip.Provider>
|
|
46
|
+
|
|
47
|
+
<!-- Global Toast Container -->
|
|
48
|
+
<Toaster richColors position="top-right" closeButton />
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type SiteConfig } from '../../site/config';
|
|
3
|
+
import '../../styles/yaxa.css';
|
|
4
|
+
export interface YaxaAppProps {
|
|
5
|
+
config?: SiteConfig;
|
|
6
|
+
enableDefaultSeo?: boolean;
|
|
7
|
+
children?: Snippet;
|
|
8
|
+
}
|
|
9
|
+
export type Props = YaxaAppProps;
|
|
10
|
+
declare const YaxaApp: import("svelte").Component<YaxaAppProps, {}, "">;
|
|
11
|
+
type YaxaApp = ReturnType<typeof YaxaApp>;
|
|
12
|
+
export default YaxaApp;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface BreadcrumbItem {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
}
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import Icon from '../elements/Icon.svelte';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
items: BreadcrumbItem[];
|
|
14
|
+
separator?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let { items, separator = 'chevron-right', class: className = '' }: Props = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<nav aria-label="Breadcrumb" class="flex {className}">
|
|
22
|
+
<ol
|
|
23
|
+
class="inline-flex items-center space-x-1.5 text-sm text-neutral-500 md:space-x-2 dark:text-neutral-400"
|
|
24
|
+
>
|
|
25
|
+
{#each items as item, index}
|
|
26
|
+
{@const isLast = index === items.length - 1}
|
|
27
|
+
<li class="inline-flex items-center">
|
|
28
|
+
{#if index > 0}
|
|
29
|
+
<Icon name={separator} size="xs" class="mx-1 text-neutral-400" />
|
|
30
|
+
{/if}
|
|
31
|
+
|
|
32
|
+
{#if item.href && !isLast}
|
|
33
|
+
<a
|
|
34
|
+
href={item.href}
|
|
35
|
+
class="inline-flex items-center gap-1.5 transition-colors hover:text-neutral-900 dark:hover:text-white"
|
|
36
|
+
>
|
|
37
|
+
{#if item.icon}
|
|
38
|
+
<Icon name={item.icon} size="xs" />
|
|
39
|
+
{/if}
|
|
40
|
+
<span>{item.label}</span>
|
|
41
|
+
</a>
|
|
42
|
+
{:else}
|
|
43
|
+
<span
|
|
44
|
+
class="inline-flex items-center gap-1.5 font-medium {isLast
|
|
45
|
+
? 'text-neutral-900 dark:text-white'
|
|
46
|
+
: ''}"
|
|
47
|
+
aria-current={isLast ? 'page' : undefined}
|
|
48
|
+
>
|
|
49
|
+
{#if item.icon}
|
|
50
|
+
<Icon name={item.icon} size="xs" />
|
|
51
|
+
{/if}
|
|
52
|
+
<span>{item.label}</span>
|
|
53
|
+
</span>
|
|
54
|
+
{/if}
|
|
55
|
+
</li>
|
|
56
|
+
{/each}
|
|
57
|
+
</ol>
|
|
58
|
+
</nav>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface BreadcrumbItem {
|
|
2
|
+
label: string;
|
|
3
|
+
href?: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
}
|
|
6
|
+
interface Props {
|
|
7
|
+
items: BreadcrumbItem[];
|
|
8
|
+
separator?: string;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const Breadcrumb: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type Breadcrumb = ReturnType<typeof Breadcrumb>;
|
|
13
|
+
export default Breadcrumb;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface CommandItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
icon?: string;
|
|
7
|
+
group?: string;
|
|
8
|
+
shortcut?: string;
|
|
9
|
+
onSelect?: () => void;
|
|
10
|
+
href?: string;
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import { browser } from '$app/environment';
|
|
16
|
+
import Icon from '../elements/Icon.svelte';
|
|
17
|
+
import Kbd from '../elements/Kbd.svelte';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
open?: boolean;
|
|
21
|
+
items?: CommandItem[];
|
|
22
|
+
placeholder?: string;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
open = $bindable(false),
|
|
28
|
+
items = [],
|
|
29
|
+
placeholder = 'Type a command or search...',
|
|
30
|
+
class: className = ''
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
|
|
33
|
+
let query = $state('');
|
|
34
|
+
let selectedIndex = $state(0);
|
|
35
|
+
|
|
36
|
+
let filteredItems = $derived.by(() => {
|
|
37
|
+
if (!query.trim()) return items;
|
|
38
|
+
const q = query.toLowerCase();
|
|
39
|
+
return items.filter(
|
|
40
|
+
(item) =>
|
|
41
|
+
item.label.toLowerCase().includes(q) ||
|
|
42
|
+
item.description?.toLowerCase().includes(q) ||
|
|
43
|
+
item.group?.toLowerCase().includes(q)
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
let groupedItems = $derived.by(() => {
|
|
48
|
+
const groups: Record<string, CommandItem[]> = {};
|
|
49
|
+
filteredItems.forEach((item) => {
|
|
50
|
+
const groupName = item.group || 'General';
|
|
51
|
+
if (!groups[groupName]) groups[groupName] = [];
|
|
52
|
+
groups[groupName].push(item);
|
|
53
|
+
});
|
|
54
|
+
return groups;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
function focusOnMount(node: HTMLElement) {
|
|
58
|
+
node.focus();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
62
|
+
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
open = !open;
|
|
65
|
+
if (open) query = '';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!open) return;
|
|
69
|
+
|
|
70
|
+
if (e.key === 'Escape') {
|
|
71
|
+
open = false;
|
|
72
|
+
} else if (e.key === 'ArrowDown') {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
selectedIndex = (selectedIndex + 1) % Math.max(1, filteredItems.length);
|
|
75
|
+
} else if (e.key === 'ArrowUp') {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
selectedIndex =
|
|
78
|
+
(selectedIndex - 1 + filteredItems.length) % Math.max(1, filteredItems.length);
|
|
79
|
+
} else if (e.key === 'Enter') {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
const selected = filteredItems[selectedIndex];
|
|
82
|
+
if (selected) {
|
|
83
|
+
executeItem(selected);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function executeItem(item: CommandItem) {
|
|
89
|
+
open = false;
|
|
90
|
+
if (item.onSelect) item.onSelect();
|
|
91
|
+
if (item.href && browser) {
|
|
92
|
+
window.location.href = item.href;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
$effect(() => {
|
|
97
|
+
if (browser) {
|
|
98
|
+
window.addEventListener('keydown', handleKeydown);
|
|
99
|
+
return () => window.removeEventListener('keydown', handleKeydown);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
{#if open}
|
|
105
|
+
<div
|
|
106
|
+
class="fixed inset-0 z-50 flex items-start justify-center p-4 pt-[15vh] sm:p-6 sm:pt-[20vh]"
|
|
107
|
+
role="dialog"
|
|
108
|
+
aria-modal="true"
|
|
109
|
+
>
|
|
110
|
+
<!-- Backdrop -->
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
class="fixed inset-0 bg-neutral-900/60 backdrop-blur-xs transition-opacity"
|
|
114
|
+
onclick={() => (open = false)}
|
|
115
|
+
aria-label="Close command palette"
|
|
116
|
+
></button>
|
|
117
|
+
|
|
118
|
+
<!-- Dialog Panel -->
|
|
119
|
+
<div
|
|
120
|
+
class="relative w-full max-w-xl overflow-hidden rounded-2xl border border-neutral-200 bg-white shadow-2xl transition-all dark:border-neutral-800 dark:bg-neutral-900 {className}"
|
|
121
|
+
>
|
|
122
|
+
<!-- Search Bar -->
|
|
123
|
+
<div class="flex items-center border-b border-neutral-200 px-4 dark:border-neutral-800">
|
|
124
|
+
<Icon name="search" size="sm" class="mr-3 text-neutral-400" />
|
|
125
|
+
<input
|
|
126
|
+
type="text"
|
|
127
|
+
bind:value={query}
|
|
128
|
+
{placeholder}
|
|
129
|
+
class="h-12 w-full bg-transparent text-sm text-neutral-900 placeholder:text-neutral-400 focus:outline-none dark:text-white"
|
|
130
|
+
use:focusOnMount
|
|
131
|
+
/>
|
|
132
|
+
<Kbd value="ESC" size="xs" />
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<!-- List Items -->
|
|
136
|
+
<div class="max-h-80 overflow-y-auto p-2">
|
|
137
|
+
{#if filteredItems.length === 0}
|
|
138
|
+
<div class="py-10 text-center text-sm text-neutral-500">
|
|
139
|
+
No results found for "{query}".
|
|
140
|
+
</div>
|
|
141
|
+
{:else}
|
|
142
|
+
{#each Object.entries(groupedItems) as [groupName, groupList]}
|
|
143
|
+
<div class="mb-2">
|
|
144
|
+
<div
|
|
145
|
+
class="px-3 py-1.5 text-[11px] font-semibold tracking-wider text-neutral-400 uppercase dark:text-neutral-500"
|
|
146
|
+
>
|
|
147
|
+
{groupName}
|
|
148
|
+
</div>
|
|
149
|
+
{#each groupList as item}
|
|
150
|
+
{@const isHighlighted = filteredItems.indexOf(item) === selectedIndex}
|
|
151
|
+
<button
|
|
152
|
+
type="button"
|
|
153
|
+
onclick={() => executeItem(item)}
|
|
154
|
+
class="flex w-full items-center justify-between rounded-xl px-3 py-2 text-left text-sm transition-colors {isHighlighted
|
|
155
|
+
? 'bg-primary-50 text-primary-900 dark:bg-primary-950/60 dark:text-primary-100'
|
|
156
|
+
: 'text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-800/60'}"
|
|
157
|
+
>
|
|
158
|
+
<div class="flex items-center gap-3">
|
|
159
|
+
{#if item.icon}
|
|
160
|
+
<Icon
|
|
161
|
+
name={item.icon}
|
|
162
|
+
size="sm"
|
|
163
|
+
class={isHighlighted
|
|
164
|
+
? 'text-primary-600 dark:text-primary-400'
|
|
165
|
+
: 'text-neutral-400'}
|
|
166
|
+
/>
|
|
167
|
+
{/if}
|
|
168
|
+
<div>
|
|
169
|
+
<div class="font-medium">{item.label}</div>
|
|
170
|
+
{#if item.description}
|
|
171
|
+
<div class="text-sm text-neutral-500 dark:text-neutral-400">
|
|
172
|
+
{item.description}
|
|
173
|
+
</div>
|
|
174
|
+
{/if}
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
{#if item.shortcut}
|
|
179
|
+
<Kbd value={item.shortcut} size="xs" />
|
|
180
|
+
{/if}
|
|
181
|
+
</button>
|
|
182
|
+
{/each}
|
|
183
|
+
</div>
|
|
184
|
+
{/each}
|
|
185
|
+
{/if}
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<!-- Footer -->
|
|
189
|
+
<div
|
|
190
|
+
class="flex items-center justify-between border-t border-neutral-100 bg-neutral-50/80 px-4 py-2 text-[11px] text-neutral-400 dark:border-neutral-800/80 dark:bg-neutral-900/50"
|
|
191
|
+
>
|
|
192
|
+
<span class="flex items-center gap-1.5">
|
|
193
|
+
<Kbd value="↑↓" size="xs" /> Navigate
|
|
194
|
+
</span>
|
|
195
|
+
<span class="flex items-center gap-1.5">
|
|
196
|
+
<Kbd value="↵" size="xs" /> Select
|
|
197
|
+
</span>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
{/if}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CommandItem {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
group?: string;
|
|
7
|
+
shortcut?: string;
|
|
8
|
+
onSelect?: () => void;
|
|
9
|
+
href?: string;
|
|
10
|
+
}
|
|
11
|
+
interface Props {
|
|
12
|
+
open?: boolean;
|
|
13
|
+
items?: CommandItem[];
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
declare const CommandPalette: import("svelte").Component<Props, {}, "open">;
|
|
18
|
+
type CommandPalette = ReturnType<typeof CommandPalette>;
|
|
19
|
+
export default CommandPalette;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export interface MenuItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
shortcut?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
separator?: boolean;
|
|
9
|
+
destructive?: boolean;
|
|
10
|
+
onSelect?: () => void;
|
|
11
|
+
}
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import { DropdownMenu } from 'bits-ui';
|
|
16
|
+
import type { Snippet } from 'svelte';
|
|
17
|
+
import Icon from '../elements/Icon.svelte';
|
|
18
|
+
import Kbd from '../elements/Kbd.svelte';
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
items: MenuItem[];
|
|
22
|
+
trigger?: Snippet;
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let { items, trigger, class: className = '' }: Props = $props();
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<DropdownMenu.Root>
|
|
30
|
+
<DropdownMenu.Trigger class="inline-flex">
|
|
31
|
+
{#if trigger}
|
|
32
|
+
{@render trigger()}
|
|
33
|
+
{:else}
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
class="inline-flex items-center gap-1 rounded-lg border border-neutral-200 bg-white px-3 py-1.5 text-sm font-medium text-neutral-800 shadow-xs hover:bg-neutral-50 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-200 dark:hover:bg-neutral-800"
|
|
37
|
+
>
|
|
38
|
+
Options
|
|
39
|
+
<Icon name="chevron-down" size="xs" />
|
|
40
|
+
</button>
|
|
41
|
+
{/if}
|
|
42
|
+
</DropdownMenu.Trigger>
|
|
43
|
+
|
|
44
|
+
<DropdownMenu.Portal>
|
|
45
|
+
<DropdownMenu.Content
|
|
46
|
+
class="animate-in fade-in-0 zoom-in-95 z-50 min-w-48 overflow-hidden rounded-xl border border-neutral-200 bg-white p-1.5 text-neutral-900 shadow-xl dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-100 {className}"
|
|
47
|
+
sideOffset={6}
|
|
48
|
+
>
|
|
49
|
+
{#each items as item}
|
|
50
|
+
{#if item.separator}
|
|
51
|
+
<DropdownMenu.Separator class="-mx-1 my-1 h-px bg-neutral-200 dark:bg-neutral-800" />
|
|
52
|
+
{:else}
|
|
53
|
+
<DropdownMenu.Item
|
|
54
|
+
disabled={item.disabled}
|
|
55
|
+
onSelect={item.onSelect}
|
|
56
|
+
class="flex cursor-pointer items-center justify-between rounded-lg px-2.5 py-1.5 text-sm transition-colors outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-highlighted:bg-neutral-100 data-highlighted:text-neutral-900 dark:data-highlighted:bg-neutral-800 dark:data-highlighted:text-neutral-100 {item.destructive
|
|
57
|
+
? 'text-rose-600 data-highlighted:bg-rose-50 dark:text-rose-400 dark:data-highlighted:bg-rose-950/50'
|
|
58
|
+
: ''}"
|
|
59
|
+
>
|
|
60
|
+
<div class="flex items-center gap-2">
|
|
61
|
+
{#if item.icon}
|
|
62
|
+
<Icon name={item.icon} size="xs" />
|
|
63
|
+
{/if}
|
|
64
|
+
<span>{item.label}</span>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{#if item.shortcut}
|
|
68
|
+
<Kbd value={item.shortcut} size="xs" />
|
|
69
|
+
{/if}
|
|
70
|
+
</DropdownMenu.Item>
|
|
71
|
+
{/if}
|
|
72
|
+
{/each}
|
|
73
|
+
</DropdownMenu.Content>
|
|
74
|
+
</DropdownMenu.Portal>
|
|
75
|
+
</DropdownMenu.Root>
|