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.
Files changed (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +163 -0
  3. package/dist/assets/favicon.svg +15 -0
  4. package/dist/components/elements/Avatar.svelte +106 -0
  5. package/dist/components/elements/Avatar.svelte.d.ts +40 -0
  6. package/dist/components/elements/AvatarGroup.svelte +18 -0
  7. package/dist/components/elements/AvatarGroup.svelte.d.ts +8 -0
  8. package/dist/components/elements/Badge.svelte +202 -0
  9. package/dist/components/elements/Badge.svelte.d.ts +75 -0
  10. package/dist/components/elements/Button.svelte +300 -0
  11. package/dist/components/elements/Button.svelte.d.ts +109 -0
  12. package/dist/components/elements/ButtonGroup.svelte +22 -0
  13. package/dist/components/elements/ButtonGroup.svelte.d.ts +9 -0
  14. package/dist/components/elements/Chip.svelte +117 -0
  15. package/dist/components/elements/Chip.svelte.d.ts +15 -0
  16. package/dist/components/elements/DataTable.svelte +222 -0
  17. package/dist/components/elements/DataTable.svelte.d.ts +43 -0
  18. package/dist/components/elements/Icon.svelte +87 -0
  19. package/dist/components/elements/Icon.svelte.d.ts +10 -0
  20. package/dist/components/elements/Kbd.svelte +37 -0
  21. package/dist/components/elements/Kbd.svelte.d.ts +32 -0
  22. package/dist/components/elements/Link.svelte +63 -0
  23. package/dist/components/elements/Link.svelte.d.ts +15 -0
  24. package/dist/components/elements/Logo.svelte +77 -0
  25. package/dist/components/elements/Logo.svelte.d.ts +9 -0
  26. package/dist/components/elements/Meter.svelte +87 -0
  27. package/dist/components/elements/Meter.svelte.d.ts +14 -0
  28. package/dist/components/elements/Progress.svelte +63 -0
  29. package/dist/components/elements/Progress.svelte.d.ts +49 -0
  30. package/dist/components/elements/Skeleton.svelte +12 -0
  31. package/dist/components/elements/Skeleton.svelte.d.ts +6 -0
  32. package/dist/components/elements/Spinner.svelte +39 -0
  33. package/dist/components/elements/Spinner.svelte.d.ts +7 -0
  34. package/dist/components/forms/Checkbox.svelte +77 -0
  35. package/dist/components/forms/Checkbox.svelte.d.ts +17 -0
  36. package/dist/components/forms/ColorPicker.svelte +142 -0
  37. package/dist/components/forms/ColorPicker.svelte.d.ts +10 -0
  38. package/dist/components/forms/Form.svelte +84 -0
  39. package/dist/components/forms/Form.svelte.d.ts +14 -0
  40. package/dist/components/forms/FormField.svelte +56 -0
  41. package/dist/components/forms/FormField.svelte.d.ts +14 -0
  42. package/dist/components/forms/Input.svelte +138 -0
  43. package/dist/components/forms/Input.svelte.d.ts +58 -0
  44. package/dist/components/forms/RadioGroup.svelte +74 -0
  45. package/dist/components/forms/RadioGroup.svelte.d.ts +17 -0
  46. package/dist/components/forms/Select.svelte +91 -0
  47. package/dist/components/forms/Select.svelte.d.ts +50 -0
  48. package/dist/components/forms/Slider.svelte +37 -0
  49. package/dist/components/forms/Slider.svelte.d.ts +12 -0
  50. package/dist/components/forms/Switch.svelte +81 -0
  51. package/dist/components/forms/Switch.svelte.d.ts +53 -0
  52. package/dist/components/forms/Textarea.svelte +73 -0
  53. package/dist/components/forms/Textarea.svelte.d.ts +64 -0
  54. package/dist/components/layout/Card.svelte +89 -0
  55. package/dist/components/layout/Card.svelte.d.ts +64 -0
  56. package/dist/components/layout/Container.svelte +36 -0
  57. package/dist/components/layout/Container.svelte.d.ts +37 -0
  58. package/dist/components/layout/Divider.svelte +45 -0
  59. package/dist/components/layout/Divider.svelte.d.ts +11 -0
  60. package/dist/components/layout/Footer.svelte +96 -0
  61. package/dist/components/layout/Footer.svelte.d.ts +10 -0
  62. package/dist/components/layout/Header.svelte +175 -0
  63. package/dist/components/layout/Header.svelte.d.ts +12 -0
  64. package/dist/components/layout/Section.svelte +62 -0
  65. package/dist/components/layout/Section.svelte.d.ts +14 -0
  66. package/dist/components/layout/YaxaApp.svelte +48 -0
  67. package/dist/components/layout/YaxaApp.svelte.d.ts +12 -0
  68. package/dist/components/navigation/Breadcrumb.svelte +58 -0
  69. package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
  70. package/dist/components/navigation/CommandPalette.svelte +201 -0
  71. package/dist/components/navigation/CommandPalette.svelte.d.ts +19 -0
  72. package/dist/components/navigation/DropdownMenu.svelte +75 -0
  73. package/dist/components/navigation/DropdownMenu.svelte.d.ts +20 -0
  74. package/dist/components/navigation/Pagination.svelte +106 -0
  75. package/dist/components/navigation/Pagination.svelte.d.ts +10 -0
  76. package/dist/components/navigation/Tabs.svelte +91 -0
  77. package/dist/components/navigation/Tabs.svelte.d.ts +18 -0
  78. package/dist/components/overlays/Accordion.svelte +54 -0
  79. package/dist/components/overlays/Accordion.svelte.d.ts +16 -0
  80. package/dist/components/overlays/Alert.svelte +110 -0
  81. package/dist/components/overlays/Alert.svelte.d.ts +37 -0
  82. package/dist/components/overlays/ContextMenu.svelte +67 -0
  83. package/dist/components/overlays/ContextMenu.svelte.d.ts +21 -0
  84. package/dist/components/overlays/Modal.svelte +99 -0
  85. package/dist/components/overlays/Modal.svelte.d.ts +40 -0
  86. package/dist/components/overlays/Popover.svelte +33 -0
  87. package/dist/components/overlays/Popover.svelte.d.ts +11 -0
  88. package/dist/components/overlays/Slideover.svelte +102 -0
  89. package/dist/components/overlays/Slideover.svelte.d.ts +37 -0
  90. package/dist/components/overlays/Tooltip.svelte +39 -0
  91. package/dist/components/overlays/Tooltip.svelte.d.ts +12 -0
  92. package/dist/components/saas/AuthCard.svelte +273 -0
  93. package/dist/components/saas/AuthCard.svelte.d.ts +16 -0
  94. package/dist/components/saas/PricingCard.svelte +166 -0
  95. package/dist/components/saas/PricingCard.svelte.d.ts +19 -0
  96. package/dist/components/saas/PricingTable.svelte +139 -0
  97. package/dist/components/saas/PricingTable.svelte.d.ts +11 -0
  98. package/dist/components/saas/SubscriptionCard.svelte +162 -0
  99. package/dist/components/saas/SubscriptionCard.svelte.d.ts +16 -0
  100. package/dist/components/saas/UserMenu.svelte +137 -0
  101. package/dist/components/saas/UserMenu.svelte.d.ts +19 -0
  102. package/dist/components/seo/Favicons.svelte +22 -0
  103. package/dist/components/seo/Favicons.svelte.d.ts +8 -0
  104. package/dist/components/seo/OgImage.svelte +61 -0
  105. package/dist/components/seo/OgImage.svelte.d.ts +12 -0
  106. package/dist/components/seo/Seo.svelte +170 -0
  107. package/dist/components/seo/Seo.svelte.d.ts +21 -0
  108. package/dist/composables/useAuth.svelte.d.ts +23 -0
  109. package/dist/composables/useAuth.svelte.js +157 -0
  110. package/dist/composables/useClipboard.svelte.d.ts +9 -0
  111. package/dist/composables/useClipboard.svelte.js +36 -0
  112. package/dist/composables/useColorMode.svelte.d.ts +8 -0
  113. package/dist/composables/useColorMode.svelte.js +20 -0
  114. package/dist/composables/useDebounce.svelte.d.ts +3 -0
  115. package/dist/composables/useDebounce.svelte.js +17 -0
  116. package/dist/composables/useMediaQuery.svelte.d.ts +3 -0
  117. package/dist/composables/useMediaQuery.svelte.js +17 -0
  118. package/dist/composables/useShortcuts.svelte.d.ts +10 -0
  119. package/dist/composables/useShortcuts.svelte.js +50 -0
  120. package/dist/composables/useToast.d.ts +19 -0
  121. package/dist/composables/useToast.js +34 -0
  122. package/dist/index.d.ts +101 -0
  123. package/dist/index.js +92 -0
  124. package/dist/server/auth/hook.d.ts +14 -0
  125. package/dist/server/auth/hook.js +36 -0
  126. package/dist/server/auth/index.d.ts +39 -0
  127. package/dist/server/auth/index.js +69 -0
  128. package/dist/server/db/index.d.ts +18 -0
  129. package/dist/server/db/index.js +43 -0
  130. package/dist/server/db/schema-pg.d.ts +977 -0
  131. package/dist/server/db/schema-pg.js +83 -0
  132. package/dist/server/db/schema-sqlite.d.ts +1045 -0
  133. package/dist/server/db/schema-sqlite.js +83 -0
  134. package/dist/server/db/types.d.ts +44 -0
  135. package/dist/server/db/types.js +1 -0
  136. package/dist/server/email/index.d.ts +27 -0
  137. package/dist/server/email/index.js +79 -0
  138. package/dist/server/polar/index.d.ts +29 -0
  139. package/dist/server/polar/index.js +49 -0
  140. package/dist/server/polar/webhook.d.ts +16 -0
  141. package/dist/server/polar/webhook.js +118 -0
  142. package/dist/site/config.d.ts +63 -0
  143. package/dist/site/config.js +48 -0
  144. package/dist/site/context.d.ts +11 -0
  145. package/dist/site/context.js +23 -0
  146. package/dist/site/hook.d.ts +15 -0
  147. package/dist/site/hook.js +38 -0
  148. package/dist/site/manifest.d.ts +6 -0
  149. package/dist/site/manifest.js +32 -0
  150. package/dist/site/og.d.ts +14 -0
  151. package/dist/site/og.js +116 -0
  152. package/dist/site/robots.d.ts +10 -0
  153. package/dist/site/robots.js +54 -0
  154. package/dist/site/schema.d.ts +67 -0
  155. package/dist/site/schema.js +67 -0
  156. package/dist/site/seo-helpers.d.ts +36 -0
  157. package/dist/site/seo-helpers.js +21 -0
  158. package/dist/site/sitemap-xsl.d.ts +6 -0
  159. package/dist/site/sitemap-xsl.js +164 -0
  160. package/dist/site/sitemap.d.ts +20 -0
  161. package/dist/site/sitemap.js +61 -0
  162. package/dist/styles/yaxa.css +84 -0
  163. package/dist/theme/theme.svelte.d.ts +74 -0
  164. package/dist/theme/theme.svelte.js +421 -0
  165. package/dist/utils/cn.d.ts +3 -0
  166. package/dist/utils/cn.js +3 -0
  167. package/dist/vite/index.d.ts +20 -0
  168. package/dist/vite/index.js +29 -0
  169. package/package.json +108 -0
@@ -0,0 +1,8 @@
1
+ import type { SiteConfig } from '../../site/config';
2
+ interface Props {
3
+ config?: SiteConfig;
4
+ faviconSvg?: string;
5
+ }
6
+ declare const Favicons: import("svelte").Component<Props, {}, "">;
7
+ type Favicons = ReturnType<typeof Favicons>;
8
+ export default Favicons;
@@ -0,0 +1,61 @@
1
+ <script lang="ts">
2
+ import type { SiteConfig } from '../../site/config';
3
+ import { getSiteConfig } from '../../site/context';
4
+
5
+ interface Props {
6
+ title?: string;
7
+ description?: string;
8
+ badge?: string;
9
+ theme?: 'dark' | 'light';
10
+ config?: SiteConfig;
11
+ class?: string;
12
+ }
13
+
14
+ let {
15
+ title,
16
+ description,
17
+ badge,
18
+ theme = 'dark',
19
+ config,
20
+ class: className = ''
21
+ }: Props = $props();
22
+
23
+ let currentConfig = $derived(config || getSiteConfig());
24
+
25
+ let ogUrl = $derived.by(() => {
26
+ const pairs: string[] = [];
27
+ if (title) pairs.push(`title=${encodeURIComponent(title)}`);
28
+ if (description) pairs.push(`description=${encodeURIComponent(description)}`);
29
+ if (badge) pairs.push(`badge=${encodeURIComponent(badge)}`);
30
+ if (theme) pairs.push(`theme=${encodeURIComponent(theme)}`);
31
+ if (currentConfig.name) pairs.push(`site=${encodeURIComponent(currentConfig.name)}`);
32
+ return `/api/og?${pairs.join('&')}`;
33
+ });
34
+ </script>
35
+
36
+ <div
37
+ class="relative overflow-hidden rounded-xl border border-neutral-200 shadow-xl dark:border-neutral-800 {className}"
38
+ >
39
+ <div
40
+ class="flex items-center justify-between border-b border-neutral-200 bg-neutral-100 px-4 py-2.5 text-sm font-medium text-neutral-600 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400"
41
+ >
42
+ <span class="flex items-center gap-1.5">
43
+ <span class="h-2 w-2 rounded-full bg-emerald-500"></span>
44
+ Live OpenGraph Preview (1200 × 630)
45
+ </span>
46
+ <a
47
+ href={ogUrl}
48
+ target="_blank"
49
+ rel="noopener noreferrer"
50
+ class="font-mono underline hover:text-primary-500"
51
+ >
52
+ Open SVG ↗
53
+ </a>
54
+ </div>
55
+ <img
56
+ src={ogUrl}
57
+ alt="Open Graph preview for {title || currentConfig.title}"
58
+ class="aspect-[1200/630] h-auto w-full bg-neutral-950 object-cover"
59
+ loading="lazy"
60
+ />
61
+ </div>
@@ -0,0 +1,12 @@
1
+ import type { SiteConfig } from '../../site/config';
2
+ interface Props {
3
+ title?: string;
4
+ description?: string;
5
+ badge?: string;
6
+ theme?: 'dark' | 'light';
7
+ config?: SiteConfig;
8
+ class?: string;
9
+ }
10
+ declare const OgImage: import("svelte").Component<Props, {}, "">;
11
+ type OgImage = ReturnType<typeof OgImage>;
12
+ export default OgImage;
@@ -0,0 +1,170 @@
1
+ <script lang="ts">
2
+ import { page } from '$app/state';
3
+ import type { SiteConfig } from '../../site/config';
4
+ import { getSiteConfig } from '../../site/context';
5
+ import { generateWebSiteSchema } from '../../site/schema';
6
+
7
+ interface Props {
8
+ title?: string;
9
+ description?: string;
10
+ canonical?: string;
11
+ ogImage?:
12
+ string | { title?: string; description?: string; badge?: string; theme?: 'dark' | 'light' };
13
+ twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
14
+ noindex?: boolean;
15
+ nofollow?: boolean;
16
+ keywords?: string[];
17
+ schema?: Record<string, unknown> | Record<string, unknown>[];
18
+ config?: SiteConfig;
19
+ }
20
+
21
+ let {
22
+ title: propTitle,
23
+ description: propDescription,
24
+ canonical: propCanonical,
25
+ ogImage: propOgImage,
26
+ twitterCard = 'summary_large_image',
27
+ noindex = false,
28
+ nofollow = false,
29
+ keywords: propKeywords,
30
+ schema,
31
+ config: propConfig
32
+ }: Props = $props();
33
+
34
+ // Read config from prop or Svelte 5 context
35
+ let config = $derived(propConfig || getSiteConfig());
36
+
37
+ // Dynamic resolution from props OR SvelteKit page.data (e.g. +page.server.ts load data)
38
+ let pageData = $derived(page.data || {});
39
+ let pageSeo = $derived((pageData.seo as Record<string, unknown>) || {});
40
+
41
+ let resolvedTitle = $derived(
42
+ propTitle ||
43
+ (pageSeo.title as string) ||
44
+ (pageData.title as string) ||
45
+ config.title ||
46
+ config.name
47
+ );
48
+
49
+ let fullTitle = $derived.by(() => {
50
+ if (propTitle || pageSeo.title || pageData.title) {
51
+ const activeTitle = (propTitle || pageSeo.title || pageData.title) as string;
52
+ if (config.seo?.titleTemplate) {
53
+ return config.seo.titleTemplate.replace('%s', activeTitle);
54
+ }
55
+ return `${activeTitle} · ${config.name}`;
56
+ }
57
+ return config.title || config.name;
58
+ });
59
+
60
+ let metaDescription = $derived(
61
+ propDescription ||
62
+ (pageSeo.description as string) ||
63
+ (pageData.description as string) ||
64
+ config.description
65
+ );
66
+
67
+ let currentUrl = $derived.by(() => {
68
+ if (propCanonical) return propCanonical;
69
+ try {
70
+ return `${config.url}${page.url.pathname}`;
71
+ } catch {
72
+ return config.url;
73
+ }
74
+ });
75
+
76
+ let ogImage = $derived(propOgImage || pageSeo.ogImage || pageData.ogImage);
77
+
78
+ let resolvedOgImageUrl = $derived.by(() => {
79
+ if (typeof ogImage === 'string') {
80
+ return ogImage.startsWith('http') ? ogImage : `${config.url}${ogImage}`;
81
+ }
82
+ if (typeof ogImage === 'object' && ogImage !== null) {
83
+ const pairs: string[] = [];
84
+ const img = ogImage as {
85
+ title?: string;
86
+ description?: string;
87
+ badge?: string;
88
+ theme?: 'dark' | 'light';
89
+ };
90
+ if (img.title) pairs.push(`title=${encodeURIComponent(img.title)}`);
91
+ if (img.description) pairs.push(`description=${encodeURIComponent(img.description)}`);
92
+ if (img.badge) pairs.push(`badge=${encodeURIComponent(img.badge)}`);
93
+ if (img.theme) pairs.push(`theme=${encodeURIComponent(img.theme)}`);
94
+ return `${config.url}/api/og?${pairs.join('&')}`;
95
+ }
96
+ if (resolvedTitle && resolvedTitle !== config.name) {
97
+ const pairs: string[] = [
98
+ `title=${encodeURIComponent(resolvedTitle)}`,
99
+ `description=${encodeURIComponent(metaDescription)}`
100
+ ];
101
+ return `${config.url}/api/og?${pairs.join('&')}`;
102
+ }
103
+ return `${config.url}${config.seo?.defaultOgImage || '/api/og'}`;
104
+ });
105
+
106
+ let robotsContent = $derived.by(() => {
107
+ const parts: string[] = [];
108
+ if (noindex || pageSeo.noindex) parts.push('noindex');
109
+ else parts.push('index');
110
+
111
+ if (nofollow || pageSeo.nofollow) parts.push('nofollow');
112
+ else parts.push('follow');
113
+
114
+ return parts.join(', ');
115
+ });
116
+
117
+ let keywords = $derived(
118
+ propKeywords || (pageSeo.keywords as string[]) || config.seo?.keywords || []
119
+ );
120
+
121
+ let jsonLdSchemas = $derived.by(() => {
122
+ const schemas: Record<string, unknown>[] = [generateWebSiteSchema(config)];
123
+ if (schema) {
124
+ if (Array.isArray(schema)) {
125
+ schemas.push(...schema);
126
+ } else {
127
+ schemas.push(schema);
128
+ }
129
+ }
130
+ return schemas;
131
+ });
132
+ </script>
133
+
134
+ <svelte:head>
135
+ <!-- Standard Metadata -->
136
+ <title>{fullTitle}</title>
137
+ <meta name="description" content={metaDescription} />
138
+ {#if keywords && keywords.length > 0}
139
+ <meta name="keywords" content={keywords.join(', ')} />
140
+ {/if}
141
+ <link rel="canonical" href={currentUrl} />
142
+ <meta name="robots" content={robotsContent} />
143
+ <meta name="theme-color" content={config.theme?.primaryColor || '#ff3e00'} />
144
+
145
+ <!-- Open Graph / Facebook -->
146
+ <meta property="og:type" content="website" />
147
+ <meta property="og:url" content={currentUrl} />
148
+ <meta property="og:title" content={fullTitle} />
149
+ <meta property="og:description" content={metaDescription} />
150
+ <meta property="og:image" content={resolvedOgImageUrl} />
151
+ <meta property="og:image:width" content="1200" />
152
+ <meta property="og:image:height" content="630" />
153
+ <meta property="og:site_name" content={config.name} />
154
+ <meta property="og:locale" content={config.defaultLocale || 'en'} />
155
+
156
+ <!-- Twitter -->
157
+ <meta name="twitter:card" content={twitterCard} />
158
+ <meta name="twitter:url" content={currentUrl} />
159
+ <meta name="twitter:title" content={fullTitle} />
160
+ <meta name="twitter:description" content={metaDescription} />
161
+ <meta name="twitter:image" content={resolvedOgImageUrl} />
162
+ {#if config.author?.twitter}
163
+ <meta name="twitter:creator" content={config.author.twitter} />
164
+ {/if}
165
+
166
+ <!-- Schema.org JSON-LD -->
167
+ {#each jsonLdSchemas as schemaItem}
168
+ {@html `<script type="application/ld+json">${JSON.stringify(schemaItem)}</` + `script>`}
169
+ {/each}
170
+ </svelte:head>
@@ -0,0 +1,21 @@
1
+ import type { SiteConfig } from '../../site/config';
2
+ interface Props {
3
+ title?: string;
4
+ description?: string;
5
+ canonical?: string;
6
+ ogImage?: string | {
7
+ title?: string;
8
+ description?: string;
9
+ badge?: string;
10
+ theme?: 'dark' | 'light';
11
+ };
12
+ twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
13
+ noindex?: boolean;
14
+ nofollow?: boolean;
15
+ keywords?: string[];
16
+ schema?: Record<string, unknown> | Record<string, unknown>[];
17
+ config?: SiteConfig;
18
+ }
19
+ declare const Seo: import("svelte").Component<Props, {}, "">;
20
+ type Seo = ReturnType<typeof Seo>;
21
+ export default Seo;
@@ -0,0 +1,23 @@
1
+ export interface UseAuthOptions {
2
+ baseURL?: string;
3
+ }
4
+ export type SocialProvider = 'github' | 'google' | 'discord' | 'apple';
5
+ /**
6
+ * Creates a reactive Svelte 5 Rune composable for Better-Auth authentication.
7
+ */
8
+ export declare function useAuth(options?: UseAuthOptions): {
9
+ user: any;
10
+ readonly session: any;
11
+ readonly isLoading: boolean;
12
+ readonly isAuthenticated: boolean;
13
+ readonly error: string | null;
14
+ client: import("better-auth/svelte").SvelteAuthClient<{
15
+ baseURL: string;
16
+ }>;
17
+ fetchSession: () => Promise<void>;
18
+ signInWithEmail: (email: string, password: string) => Promise<boolean>;
19
+ signUpWithEmail: (email: string, password: string, name: string) => Promise<boolean>;
20
+ signInWithSocial: (provider: SocialProvider) => Promise<void>;
21
+ signInWithMagicLink: (email: string) => Promise<boolean>;
22
+ signOut: () => Promise<void>;
23
+ };
@@ -0,0 +1,157 @@
1
+ import { createAuthClient } from 'better-auth/svelte';
2
+ /**
3
+ * Creates a reactive Svelte 5 Rune composable for Better-Auth authentication.
4
+ */
5
+ export function useAuth(options = {}) {
6
+ const authClient = createAuthClient({
7
+ baseURL: options.baseURL || (typeof window !== 'undefined' ? window.location.origin : '')
8
+ });
9
+ let user = $state(null);
10
+ let session = $state(null);
11
+ let isLoading = $state(false);
12
+ let error = $state(null);
13
+ const isAuthenticated = $derived(!!user);
14
+ async function fetchSession() {
15
+ try {
16
+ isLoading = true;
17
+ const res = await authClient.getSession();
18
+ if (res.data) {
19
+ user = res.data.user;
20
+ session = res.data.session;
21
+ }
22
+ else {
23
+ user = null;
24
+ session = null;
25
+ }
26
+ }
27
+ catch (err) {
28
+ user = null;
29
+ session = null;
30
+ error = err?.message || 'Failed to fetch session';
31
+ }
32
+ finally {
33
+ isLoading = false;
34
+ }
35
+ }
36
+ async function signInWithEmail(email, password) {
37
+ isLoading = true;
38
+ error = null;
39
+ try {
40
+ const res = await authClient.signIn.email({ email, password });
41
+ if (res.error) {
42
+ error = res.error.message || 'Invalid email or password';
43
+ return false;
44
+ }
45
+ await fetchSession();
46
+ return true;
47
+ }
48
+ catch (err) {
49
+ error = err?.message || 'Authentication error';
50
+ return false;
51
+ }
52
+ finally {
53
+ isLoading = false;
54
+ }
55
+ }
56
+ async function signUpWithEmail(email, password, name) {
57
+ isLoading = true;
58
+ error = null;
59
+ try {
60
+ const res = await authClient.signUp.email({ email, password, name });
61
+ if (res.error) {
62
+ error = res.error.message || 'Could not create account';
63
+ return false;
64
+ }
65
+ await fetchSession();
66
+ return true;
67
+ }
68
+ catch (err) {
69
+ error = err?.message || 'Registration error';
70
+ return false;
71
+ }
72
+ finally {
73
+ isLoading = false;
74
+ }
75
+ }
76
+ async function signInWithSocial(provider) {
77
+ isLoading = true;
78
+ error = null;
79
+ try {
80
+ await authClient.signIn.social({
81
+ provider,
82
+ callbackURL: typeof window !== 'undefined' ? `${window.location.origin}/dashboard` : '/'
83
+ });
84
+ }
85
+ catch (err) {
86
+ error = err?.message || `Failed to sign in with ${provider}`;
87
+ isLoading = false;
88
+ }
89
+ }
90
+ async function signInWithMagicLink(email) {
91
+ isLoading = true;
92
+ error = null;
93
+ try {
94
+ const res = await authClient.signIn.magicLink?.({
95
+ email,
96
+ callbackURL: typeof window !== 'undefined' ? `${window.location.origin}/dashboard` : '/'
97
+ });
98
+ if (res?.error) {
99
+ error = res.error.message || 'Failed to send magic link';
100
+ return false;
101
+ }
102
+ return true;
103
+ }
104
+ catch (err) {
105
+ error = err?.message || 'Failed to send magic link';
106
+ return false;
107
+ }
108
+ finally {
109
+ isLoading = false;
110
+ }
111
+ }
112
+ async function signOut() {
113
+ isLoading = true;
114
+ try {
115
+ await authClient.signOut();
116
+ user = null;
117
+ session = null;
118
+ }
119
+ catch (err) {
120
+ error = err?.message || 'Failed to sign out';
121
+ }
122
+ finally {
123
+ isLoading = false;
124
+ }
125
+ }
126
+ // Initialize session on mount if in browser
127
+ if (typeof window !== 'undefined') {
128
+ fetchSession();
129
+ }
130
+ return {
131
+ get user() {
132
+ return user;
133
+ },
134
+ set user(val) {
135
+ user = val;
136
+ },
137
+ get session() {
138
+ return session;
139
+ },
140
+ get isLoading() {
141
+ return isLoading;
142
+ },
143
+ get isAuthenticated() {
144
+ return isAuthenticated;
145
+ },
146
+ get error() {
147
+ return error;
148
+ },
149
+ client: authClient,
150
+ fetchSession,
151
+ signInWithEmail,
152
+ signUpWithEmail,
153
+ signInWithSocial,
154
+ signInWithMagicLink,
155
+ signOut
156
+ };
157
+ }
@@ -0,0 +1,9 @@
1
+ export interface UseClipboardOptions {
2
+ timeout?: number;
3
+ }
4
+ export declare function useClipboard(options?: UseClipboardOptions): {
5
+ readonly copied: boolean;
6
+ readonly text: string;
7
+ readonly isSupported: boolean;
8
+ copy: (value: string) => Promise<boolean>;
9
+ };
@@ -0,0 +1,36 @@
1
+ import { browser } from '$app/environment';
2
+ export function useClipboard(options = {}) {
3
+ const { timeout = 2000 } = options;
4
+ let copied = $state(false);
5
+ let text = $state('');
6
+ const isSupported = browser && typeof navigator !== 'undefined' && 'clipboard' in navigator;
7
+ async function copy(value) {
8
+ if (!isSupported)
9
+ return false;
10
+ try {
11
+ await navigator.clipboard.writeText(value);
12
+ text = value;
13
+ copied = true;
14
+ setTimeout(() => {
15
+ copied = false;
16
+ }, timeout);
17
+ return true;
18
+ }
19
+ catch {
20
+ copied = false;
21
+ return false;
22
+ }
23
+ }
24
+ return {
25
+ get copied() {
26
+ return copied;
27
+ },
28
+ get text() {
29
+ return text;
30
+ },
31
+ get isSupported() {
32
+ return isSupported;
33
+ },
34
+ copy
35
+ };
36
+ }
@@ -0,0 +1,8 @@
1
+ import { type ThemeMode } from '../theme/theme.svelte';
2
+ export declare function useColorMode(): {
3
+ readonly value: ThemeMode;
4
+ readonly resolvedTheme: "light" | "dark";
5
+ readonly isDark: boolean;
6
+ set(mode: ThemeMode): void;
7
+ toggle(): void;
8
+ };
@@ -0,0 +1,20 @@
1
+ import { theme } from '../theme/theme.svelte';
2
+ export function useColorMode() {
3
+ return {
4
+ get value() {
5
+ return theme.mode;
6
+ },
7
+ get resolvedTheme() {
8
+ return theme.resolvedTheme;
9
+ },
10
+ get isDark() {
11
+ return theme.resolvedTheme === 'dark';
12
+ },
13
+ set(mode) {
14
+ theme.setMode(mode);
15
+ },
16
+ toggle() {
17
+ theme.toggle();
18
+ }
19
+ };
20
+ }
@@ -0,0 +1,3 @@
1
+ export declare function useDebounce<T>(getValue: () => T, delay?: number): {
2
+ readonly value: T;
3
+ };
@@ -0,0 +1,17 @@
1
+ export function useDebounce(getValue, delay = 300) {
2
+ let debouncedValue = $state(getValue());
3
+ let timer;
4
+ $effect(() => {
5
+ const current = getValue();
6
+ clearTimeout(timer);
7
+ timer = setTimeout(() => {
8
+ debouncedValue = current;
9
+ }, delay);
10
+ return () => clearTimeout(timer);
11
+ });
12
+ return {
13
+ get value() {
14
+ return debouncedValue;
15
+ }
16
+ };
17
+ }
@@ -0,0 +1,3 @@
1
+ export declare function useMediaQuery(query: string): {
2
+ readonly matches: boolean;
3
+ };
@@ -0,0 +1,17 @@
1
+ import { browser } from '$app/environment';
2
+ export function useMediaQuery(query) {
3
+ let matches = $state(false);
4
+ if (browser && typeof window !== 'undefined') {
5
+ const mediaQuery = window.matchMedia(query);
6
+ matches = mediaQuery.matches;
7
+ const listener = (e) => {
8
+ matches = e.matches;
9
+ };
10
+ mediaQuery.addEventListener('change', listener);
11
+ }
12
+ return {
13
+ get matches() {
14
+ return matches;
15
+ }
16
+ };
17
+ }
@@ -0,0 +1,10 @@
1
+ export interface ShortcutItem {
2
+ handler: (e: KeyboardEvent) => void;
3
+ disabled?: boolean;
4
+ }
5
+ export type ShortcutConfig = (e: KeyboardEvent) => void | ShortcutItem;
6
+ /**
7
+ * Global keyboard shortcut listener matching Nuxt UI's defineShortcuts.
8
+ * Example: useShortcuts({ 'meta_k': () => openSearch(), 'escape': () => closeModal() })
9
+ */
10
+ export declare function useShortcuts(shortcuts: Record<string, ((e: KeyboardEvent) => void) | ShortcutItem>): () => void;
@@ -0,0 +1,50 @@
1
+ import { browser } from '$app/environment';
2
+ /**
3
+ * Global keyboard shortcut listener matching Nuxt UI's defineShortcuts.
4
+ * Example: useShortcuts({ 'meta_k': () => openSearch(), 'escape': () => closeModal() })
5
+ */
6
+ export function useShortcuts(shortcuts) {
7
+ if (!browser)
8
+ return () => { };
9
+ function handleKeyDown(e) {
10
+ if (e.defaultPrevented)
11
+ return;
12
+ const target = e.target;
13
+ const isInput = target &&
14
+ (target.tagName === 'INPUT' ||
15
+ target.tagName === 'TEXTAREA' ||
16
+ target.tagName === 'SELECT' ||
17
+ target.isContentEditable);
18
+ for (const [keyCombo, config] of Object.entries(shortcuts)) {
19
+ const handler = typeof config === 'function' ? config : config.handler;
20
+ const disabled = typeof config === 'object' ? config.disabled : false;
21
+ if (disabled)
22
+ continue;
23
+ const parts = keyCombo.toLowerCase().split(/[\s_+]/);
24
+ const mainKey = parts[parts.length - 1];
25
+ const requiresMeta = parts.includes('meta') || parts.includes('cmd') || parts.includes('command');
26
+ const requiresCtrl = parts.includes('ctrl') || parts.includes('control');
27
+ const requiresShift = parts.includes('shift');
28
+ const requiresAlt = parts.includes('alt');
29
+ const metaMatch = requiresMeta ? e.metaKey || e.ctrlKey : true;
30
+ const ctrlMatch = requiresCtrl ? e.ctrlKey : true;
31
+ const shiftMatch = requiresShift ? e.shiftKey : true;
32
+ const altMatch = requiresAlt ? e.altKey : true;
33
+ const keyMatch = e.key.toLowerCase() === mainKey ||
34
+ e.code.toLowerCase() === `key${mainKey}` ||
35
+ (mainKey === 'k' && e.key.toLowerCase() === 'k');
36
+ if (metaMatch && ctrlMatch && shiftMatch && altMatch && keyMatch) {
37
+ if (isInput && !requiresMeta && !requiresCtrl) {
38
+ continue;
39
+ }
40
+ e.preventDefault();
41
+ handler(e);
42
+ break;
43
+ }
44
+ }
45
+ }
46
+ window.addEventListener('keydown', handleKeyDown);
47
+ return () => {
48
+ window.removeEventListener('keydown', handleKeyDown);
49
+ };
50
+ }
@@ -0,0 +1,19 @@
1
+ export interface ToastAction {
2
+ label: string;
3
+ onClick: () => void;
4
+ }
5
+ export interface ToastOptions {
6
+ title?: string;
7
+ description?: string;
8
+ color?: 'primary' | 'success' | 'warning' | 'error' | 'neutral';
9
+ duration?: number;
10
+ action?: ToastAction;
11
+ }
12
+ export declare function useToast(): {
13
+ add: (options: ToastOptions | string) => string | number;
14
+ success: (title: string, description?: string) => string | number;
15
+ error: (title: string, description?: string) => string | number;
16
+ warning: (title: string, description?: string) => string | number;
17
+ info: (title: string, description?: string) => string | number;
18
+ dismiss: (id?: number | string) => string | number | undefined;
19
+ };