yaxa-svelte 2.8.1 → 2.9.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 (31) hide show
  1. package/README.md +40 -28
  2. package/dist/components/content/Callout.svelte +182 -0
  3. package/dist/components/content/Callout.svelte.d.ts +39 -0
  4. package/dist/components/content/CodeGroup.svelte +238 -0
  5. package/dist/components/content/CodeGroup.svelte.d.ts +41 -0
  6. package/dist/components/content/Steps.svelte +146 -0
  7. package/dist/components/content/Steps.svelte.d.ts +39 -0
  8. package/dist/components/content/content.test.d.ts +1 -0
  9. package/dist/components/content/content.test.js +115 -0
  10. package/dist/components/content/index.d.ts +6 -0
  11. package/dist/components/content/index.js +3 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +2 -0
  14. package/dist/root-purity.test.js +6 -0
  15. package/dist/server/endpoint/index.js +89 -14
  16. package/dist/server/endpoint/types.d.ts +21 -0
  17. package/dist/server/index.d.ts +2 -0
  18. package/dist/server/index.js +2 -0
  19. package/dist/server/observability/index.d.ts +4 -0
  20. package/dist/server/observability/index.js +4 -0
  21. package/dist/server/observability/logger.d.ts +14 -0
  22. package/dist/server/observability/logger.js +117 -0
  23. package/dist/server/observability/observability.test.d.ts +1 -0
  24. package/dist/server/observability/observability.test.js +159 -0
  25. package/dist/server/observability/timing.d.ts +6 -0
  26. package/dist/server/observability/timing.js +65 -0
  27. package/dist/server/observability/trace.d.ts +19 -0
  28. package/dist/server/observability/trace.js +66 -0
  29. package/dist/server/observability/types.d.ts +51 -0
  30. package/dist/server/observability/types.js +1 -0
  31. package/package.json +5 -1
package/README.md CHANGED
@@ -15,6 +15,7 @@ _Nuxt UI v4 & Nuxt UI Pro Equivalent for SvelteKit 2 & 3 & Svelte 5 with Built-i
15
15
  [![License: MIT](https://img.shields.io/badge/License-MIT-emerald.svg)](LICENSE)
16
16
  [![Security Policy](https://img.shields.io/badge/Security-Policy-blue.svg)](SECURITY.md)
17
17
  [![Contributing](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
18
+ [![Sponsor](https://img.shields.io/badge/Sponsor-%E2%99%A5-ea4aaa?style=flat&logo=githubsponsors&logoColor=white)](https://github.com/sponsors/FuntionalFrost)
18
19
 
19
20
  <br />
20
21
 
@@ -553,39 +554,40 @@ A zero-overhead, runes-native query and mutation cache built specifically for Sv
553
554
 
554
555
  ## 📦 Subpath Exports Architecture
555
556
 
556
- | Subpath | Description |
557
- | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
558
- | `yaxa-svelte` | Core UI Primitives, SEO Components, `<Gate />`, Type-Safe Client RPC (`createApiClient`), Runes Query Engine (`createQuery`, `createMutation`, `QueryClient`), Theme System & Composables (`useGate`, `useUpload`, `useSSE`, `useToast`, `useClipboard`, etc.) |
559
- | `yaxa-svelte/seo` | Standalone SEO & Agent Engine: Sitemaps, robots.txt, XSL stylesheets, OpenGraph SVG, Schema.org JSON-LD (FAQPage, Product), WebManifest, llms.txt, skill.md, `useSeo()` runes, and prerendering utilities (Zero DB/Auth dependencies; 100% compatible with `@sveltejs/adapter-static`) |
560
- | `yaxa-svelte/admin` | Drizzle ORM Schema Introspection, `<AdminDashboard />`, `createYaxaAdminHook`, `createDrizzleAdmin`, `<DevSandbox />`, `<RecordDrawer />` |
561
- | `yaxa-svelte/server` | `yaxaEndpoint` Pipeline, Declarative Guards, OpenAPI 3.1 & Scalar Docs, RFC 7807, Real-Time SSE (`yaxaSSE`, `broadcast`), AI Streaming (`createAiStreamHandler`), Background Jobs (`enqueueJob`, `createJobRunner`), `eventBus`, Server Hooks, DB, Storage & Email |
562
- | `yaxa-svelte/auth` | Better-Auth client & server integration (`useAuth`, `<SignInCard />`, `<SignUpCard />`, `<ResetPasswordCard />`, `<AuthCard />`, `<PasskeyUI />`, `<TwoFactorModal />`, `createYaxaAuth`) |
563
- | `yaxa-svelte/db` | Multi-dialect Drizzle ORM clients & schemas for Neon (PostgreSQL) and Turso (SQLite/LibSQL) |
564
- | `yaxa-svelte/polar` | Polar.sh billing, customer portal sessions, and webhook synchronization |
565
- | `yaxa-svelte/email` | Resend transactional email templates with automatic development console fallback |
566
- | `yaxa-svelte/storage` | AWS S3 and Cloudflare R2 presigned upload URL generators |
567
- | `yaxa-svelte/testing` | Test mocks & utilities (`createMockSiteConfig`, `createMockUser`, `createTestWrapperProps`) |
568
- | `yaxa-svelte/vite` | Turnkey Vite plugin (`yaxa()`) |
569
- | `yaxa-svelte/yaxa.css` | Standalone turnkey Tailwind CSS v4 stylesheet with base styling, `@theme`, and scrollbars |
570
- | `yaxa-svelte/theme.css` | Pure Tailwind CSS v4 design tokens, `:root` variables, and harmonic radius scale without `@import 'tailwindcss'` |
557
+ | Subpath | Description |
558
+ | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
559
+ | `yaxa-svelte` | Core UI Primitives, SEO Components, `<Gate />`, Type-Safe Client RPC (`createApiClient`), Runes Query Engine (`createQuery`, `createMutation`, `QueryClient`), Theme System & Composables (`useGate`, `useUpload`, `useSSE`, `useToast`, `useClipboard`, etc.) |
560
+ | `yaxa-svelte/seo` | Standalone SEO & Agent Engine: Sitemaps, robots.txt, XSL stylesheets, OpenGraph SVG, Schema.org JSON-LD (FAQPage, Product), WebManifest, llms.txt, skill.md, `useSeo()` runes, and prerendering utilities (Zero DB/Auth dependencies; 100% compatible with `@sveltejs/adapter-static`) |
561
+ | `yaxa-svelte/admin` | Drizzle ORM Schema Introspection, `<AdminDashboard />`, `createYaxaAdminHook`, `createDrizzleAdmin`, `<DevSandbox />`, `<RecordDrawer />` |
562
+ | `yaxa-svelte/server` | `yaxaEndpoint` Pipeline, W3C Distributed Tracing (`traceparent`, `requestId`), Contextual Logger & `Server-Timing` Metrics, Declarative Guards, OpenAPI 3.1 & Scalar Docs, RFC 7807, Real-Time SSE (`yaxaSSE`, `broadcast`), AI Streaming (`createAiStreamHandler`), Background Jobs (`enqueueJob`, `createJobRunner`), `eventBus`, Server Hooks, DB, Storage & Email |
563
+ | `yaxa-svelte/auth` | Better-Auth client & server integration (`useAuth`, `<SignInCard />`, `<SignUpCard />`, `<ResetPasswordCard />`, `<AuthCard />`, `<PasskeyUI />`, `<TwoFactorModal />`, `createYaxaAuth`) |
564
+ | `yaxa-svelte/db` | Multi-dialect Drizzle ORM clients & schemas for Neon (PostgreSQL) and Turso (SQLite/LibSQL) |
565
+ | `yaxa-svelte/polar` | Polar.sh billing, customer portal sessions, and webhook synchronization |
566
+ | `yaxa-svelte/email` | Resend transactional email templates with automatic development console fallback |
567
+ | `yaxa-svelte/storage` | AWS S3 and Cloudflare R2 presigned upload URL generators |
568
+ | `yaxa-svelte/testing` | Test mocks & utilities (`createMockSiteConfig`, `createMockUser`, `createTestWrapperProps`) |
569
+ | `yaxa-svelte/vite` | Turnkey Vite plugin (`yaxa()`) |
570
+ | `yaxa-svelte/yaxa.css` | Standalone turnkey Tailwind CSS v4 stylesheet with base styling, `@theme`, and scrollbars |
571
+ | `yaxa-svelte/theme.css` | Pure Tailwind CSS v4 design tokens, `:root` variables, and harmonic radius scale without `@import 'tailwindcss'` |
571
572
 
572
573
  ---
573
574
 
574
575
  ## 🧩 Component Library
575
576
 
576
- | Category | Components |
577
- | :--------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
578
- | **Theming & Studio** | `<ThemeStudio>`, `theme`, `ACCENT_PALETTES`, `NEUTRAL_PALETTES`, `RADIUS_PRESETS`, Harmonic Radius Multiplier Scale, Chart Palette Series Tokens |
579
- | **AI Primitives** | `<AiChat>`, `<PromptBar>`, `<AiThought>`, `<AiToolCall>`, `useAiChat` |
580
- | **Charts (Pure SVG)** | `<LineChart>`, `<BarChart>`, `<DonutChart>` (0 external charting dependencies, auto-responsive, dark/light theme aware) |
581
- | **SaaS & Auth Suite** | `<Gate>`, `<UserMenu>`, `<OrgSwitcher>`, `<PricingCard>`, `<PricingTable>`, `<SubscriptionCard>`, `useGate`, `useUpload` (Root); `<SignInCard>`, `<SignUpCard>`, `<ResetPasswordCard>`, `<AuthCard>`, `<PasskeyUI>`, `<TwoFactorModal>`, `useAuth` (`yaxa-svelte/auth`) |
582
- | **Admin Suite** | `<AdminDashboard>`, `<RecordDrawer>`, `<DevSandbox>`, `<ImpersonationBanner>`, `createYaxaAdminHook`, `createDrizzleAdmin` |
583
- | **Elements & Code** | `<CodeDiff>` (Interactive Diff & Changelog), `<CodeBlock>`, `<DataTable>` (Client & Server Modes), `Button`, `ButtonGroup`, `Badge`, `Avatar`, `AvatarGroup`, `Chip`, `Meter`, `MetricCard`, `Kbd`, `Icon`, `Spinner`, `Progress`, `Skeleton`, `Link`, `Logo`, `Tree`, `Timeline`, `Terminal`, `VirtualList`, `Carousel` |
584
- | **Forms (Auto-Wired)** | `<Form>`, `<FormField>`, `Input`, `InputOTP`, `Textarea`, `Checkbox`, `Switch`, `Select`, `Combobox`, `MultiSelect`, `NumberInput`, `PhoneInput`, `CreditCardInput`, `RadioGroup`, `Slider`, `ColorPicker`, `ToggleGroup`, `DatePicker`, `DateRangePicker`, `Dropzone`, `<RichTextEditor>` |
585
- | **Layout & Blocks** | `Container`, `Header`, `Footer`, `Section`, `Card`, `Divider`, `YaxaApp`, `DashboardShell` (with independent sidebar tokens), `ResizablePanels`, Page Blocks (`Hero`, `SaaS Metrics`, `FAQ`, `Testimonials`, `FeatureGrid`) |
586
- | **Overlays & Nav** | `Tabs`, `Breadcrumb`, `Pagination`, `CommandPalette` (`⌘K`), `DropdownMenu`, `ContextMenu`, `Modal`, `Drawer`, `Slideover`, `Popover`, `Tooltip` (with `arrow={false}` & `unstyled`), `Alert`, `Accordion`, `NotificationCenter`, `Stepper` |
587
- | **Motion & Actions** | `springTilt`, `smoothSlide`, `staggerFly`, `autosize`, `clickOutside`, `portal`, `sortableItem`, `infiniteScrollAction` |
588
- | **Composables** | `useGate`, `useUpload`, `useClipboard`, `useShortcuts`, `useColorMode`, `useToast`, `useMediaQuery`, `useDebounce`, `useIdle`, `useInfiniteScroll`, `useSortable`, `useDataTable`, `useLocale` (Root); `useAuth` (`yaxa-svelte/auth`) |
577
+ | Category | Components |
578
+ | :--------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
579
+ | **Theming & Studio** | `<ThemeStudio>`, `theme`, `ACCENT_PALETTES`, `NEUTRAL_PALETTES`, `RADIUS_PRESETS`, Harmonic Radius Multiplier Scale, Chart Palette Series Tokens |
580
+ | **AI Primitives** | `<AiChat>`, `<PromptBar>`, `<AiThought>`, `<AiToolCall>`, `useAiChat` |
581
+ | **Charts (Pure SVG)** | `<LineChart>`, `<BarChart>`, `<DonutChart>` (0 external charting dependencies, auto-responsive, dark/light theme aware) |
582
+ | **SaaS & Auth Suite** | `<Gate>`, `<UserMenu>`, `<OrgSwitcher>`, `<PricingCard>`, `<PricingTable>`, `<SubscriptionCard>`, `useGate`, `useUpload` (Root); `<SignInCard>`, `<SignUpCard>`, `<ResetPasswordCard>`, `<AuthCard>`, `<PasskeyUI>`, `<TwoFactorModal>`, `useAuth` (`yaxa-svelte/auth`) |
583
+ | **Admin Suite** | `<AdminDashboard>`, `<RecordDrawer>`, `<DevSandbox>`, `<ImpersonationBanner>`, `createYaxaAdminHook`, `createDrizzleAdmin` |
584
+ | **Docs & Content** | `<Callout>` (`note`, `tip`, `important`, `warning`, `caution`, `danger`), `<CodeGroup>` (Synced multi-tab code blocks with `syncKey`), `<Steps>` (Numbered vertical step guides), `<CodeDiff>`, `<CodeBlock>` |
585
+ | **Elements & Data** | `<DataTable>` (Client & Server Modes), `Button`, `ButtonGroup`, `Badge`, `Avatar`, `AvatarGroup`, `Chip`, `Meter`, `MetricCard`, `Kbd`, `Icon`, `Spinner`, `Progress`, `Skeleton`, `Link`, `Logo`, `Tree`, `Timeline`, `Terminal`, `VirtualList`, `Carousel` |
586
+ | **Forms (Auto-Wired)** | `<Form>`, `<FormField>`, `Input`, `InputOTP`, `Textarea`, `Checkbox`, `Switch`, `Select`, `Combobox`, `MultiSelect`, `NumberInput`, `PhoneInput`, `CreditCardInput`, `RadioGroup`, `Slider`, `ColorPicker`, `ToggleGroup`, `DatePicker`, `DateRangePicker`, `Dropzone`, `<RichTextEditor>` |
587
+ | **Layout & Blocks** | `Container`, `Header`, `Footer`, `Section`, `Card`, `Divider`, `YaxaApp`, `DashboardShell` (with independent sidebar tokens), `ResizablePanels`, Page Blocks (`Hero`, `SaaS Metrics`, `FAQ`, `Testimonials`, `FeatureGrid`) |
588
+ | **Overlays & Nav** | `Tabs`, `Breadcrumb`, `Pagination`, `CommandPalette` (`⌘K`), `DropdownMenu`, `ContextMenu`, `Modal`, `Drawer`, `Slideover`, `Popover`, `Tooltip` (with `arrow={false}` & `unstyled`), `Alert`, `Accordion`, `NotificationCenter`, `Stepper` |
589
+ | **Motion & Actions** | `springTilt`, `smoothSlide`, `staggerFly`, `autosize`, `clickOutside`, `portal`, `sortableItem`, `infiniteScrollAction` |
590
+ | **Composables** | `useGate`, `useUpload`, `useClipboard`, `useShortcuts`, `useColorMode`, `useToast`, `useMediaQuery`, `useDebounce`, `useIdle`, `useInfiniteScroll`, `useSortable`, `useDataTable`, `useLocale` (Root); `useAuth` (`yaxa-svelte/auth`) |
589
591
 
590
592
  ---
591
593
 
@@ -615,6 +617,16 @@ Contributions are always welcome! Please read our [Contributing Guidelines](CONT
615
617
 
616
618
  ---
617
619
 
620
+ ## 💖 Sponsors & Backing
621
+
622
+ If you find Yaxa helpful for your applications or business, consider supporting its continuous development and maintenance:
623
+
624
+ [![Sponsor on GitHub](https://img.shields.io/badge/Sponsor_on_GitHub-ea4aaa?style=for-the-badge&logo=githubsponsors&logoColor=white)](https://github.com/sponsors/FuntionalFrost)
625
+
626
+ - **[GitHub Sponsors](https://github.com/sponsors/FuntionalFrost)**: Fund ongoing feature additions, bug fixes, SvelteKit version tracking, and documentation improvements.
627
+
628
+ ---
629
+
618
630
  ## 📄 License
619
631
 
620
632
  [MIT License](LICENSE) © 2026 Yaxa Contributors.
@@ -0,0 +1,182 @@
1
+ <script module lang="ts">
2
+ import { tv, cn, type VariantProps } from '../../utils/cn.js';
3
+ import type { Snippet } from 'svelte';
4
+ import type { IconSource } from '../elements/Icon.svelte';
5
+
6
+ export const calloutVariants = tv({
7
+ base: 'not-prose group relative my-4 flex w-full gap-3.5 overflow-hidden rounded-xl border border-l-4 p-4 text-sm transition-all duration-150',
8
+ variants: {
9
+ type: {
10
+ note: 'border-sky-200/80 border-l-sky-500 bg-sky-50/50 text-sky-950 dark:border-sky-900/40 dark:border-l-sky-400 dark:bg-sky-950/25 dark:text-sky-100',
11
+ info: 'border-sky-200/80 border-l-sky-500 bg-sky-50/50 text-sky-950 dark:border-sky-900/40 dark:border-l-sky-400 dark:bg-sky-950/25 dark:text-sky-100',
12
+ tip: 'border-emerald-200/80 border-l-emerald-500 bg-emerald-50/50 text-emerald-950 dark:border-emerald-900/40 dark:border-l-emerald-400 dark:bg-emerald-950/25 dark:text-emerald-100',
13
+ success:
14
+ 'border-emerald-200/80 border-l-emerald-500 bg-emerald-50/50 text-emerald-950 dark:border-emerald-900/40 dark:border-l-emerald-400 dark:bg-emerald-950/25 dark:text-emerald-100',
15
+ important:
16
+ 'border-violet-200/80 border-l-violet-500 bg-violet-50/50 text-violet-950 dark:border-violet-900/40 dark:border-l-violet-400 dark:bg-violet-950/25 dark:text-violet-100',
17
+ warning:
18
+ 'border-amber-200/80 border-l-amber-500 bg-amber-50/50 text-amber-950 dark:border-amber-900/40 dark:border-l-amber-400 dark:bg-amber-950/25 dark:text-amber-100',
19
+ caution:
20
+ 'border-rose-200/80 border-l-rose-500 bg-rose-50/50 text-rose-950 dark:border-rose-900/40 dark:border-l-rose-400 dark:bg-rose-950/25 dark:text-rose-100',
21
+ danger:
22
+ 'border-rose-200/80 border-l-rose-500 bg-rose-50/50 text-rose-950 dark:border-rose-900/40 dark:border-l-rose-400 dark:bg-rose-950/25 dark:text-rose-100'
23
+ }
24
+ },
25
+ defaultVariants: {
26
+ type: 'note'
27
+ }
28
+ });
29
+
30
+ export type CalloutType = NonNullable<VariantProps<typeof calloutVariants>['type']>;
31
+
32
+ export interface CalloutSlots {
33
+ root?: string;
34
+ icon?: string;
35
+ content?: string;
36
+ title?: string;
37
+ body?: string;
38
+ close?: string;
39
+ }
40
+
41
+ export type CalloutProps = VariantProps<typeof calloutVariants> & {
42
+ title?: string;
43
+ description?: string;
44
+ icon?: IconSource;
45
+ href?: string;
46
+ target?: string;
47
+ closable?: boolean;
48
+ onclose?: () => void;
49
+ class?: string;
50
+ ui?: CalloutSlots;
51
+ children?: Snippet;
52
+ };
53
+ </script>
54
+
55
+ <script lang="ts">
56
+ import Icon from '../elements/Icon.svelte';
57
+
58
+ let {
59
+ type = 'note',
60
+ title,
61
+ description,
62
+ icon,
63
+ href,
64
+ target,
65
+ closable = false,
66
+ onclose,
67
+ class: className = '',
68
+ ui,
69
+ children
70
+ }: CalloutProps = $props();
71
+
72
+ let visible = $state(true);
73
+
74
+ const iconColorMap: Record<CalloutType, string> = {
75
+ note: 'text-sky-600 dark:text-sky-400',
76
+ info: 'text-sky-600 dark:text-sky-400',
77
+ tip: 'text-emerald-600 dark:text-emerald-400',
78
+ success: 'text-emerald-600 dark:text-emerald-400',
79
+ important: 'text-violet-600 dark:text-violet-400',
80
+ warning: 'text-amber-600 dark:text-amber-400',
81
+ caution: 'text-rose-600 dark:text-rose-400',
82
+ danger: 'text-rose-600 dark:text-rose-400'
83
+ };
84
+
85
+ let resolvedIcon = $derived.by((): IconSource => {
86
+ if (icon) return icon;
87
+ switch (type) {
88
+ case 'tip':
89
+ case 'success':
90
+ return 'check';
91
+ case 'important':
92
+ return 'sparkles';
93
+ case 'warning':
94
+ case 'caution':
95
+ case 'danger':
96
+ return 'alert';
97
+ case 'note':
98
+ case 'info':
99
+ default:
100
+ return 'info';
101
+ }
102
+ });
103
+
104
+ function handleClose(e: MouseEvent) {
105
+ e.preventDefault();
106
+ e.stopPropagation();
107
+ visible = false;
108
+ onclose?.();
109
+ }
110
+ </script>
111
+
112
+ {#if visible}
113
+ <svelte:element
114
+ this={href ? 'a' : 'div'}
115
+ {href}
116
+ {target}
117
+ rel={target === '_blank' ? 'noopener noreferrer' : undefined}
118
+ data-slot="root"
119
+ data-callout-type={type}
120
+ role="note"
121
+ class={cn(
122
+ calloutVariants({ type, class: className }),
123
+ href && 'cursor-pointer hover:shadow-sm',
124
+ ui?.root
125
+ )}
126
+ >
127
+ <div data-slot="icon" class={cn('mt-0.5 shrink-0', iconColorMap[type ?? 'note'], ui?.icon)}>
128
+ <Icon name={resolvedIcon} size="sm" />
129
+ </div>
130
+
131
+ <div data-slot="content" class={cn('min-w-0 flex-1 space-y-1', ui?.content)}>
132
+ {#if title}
133
+ <div
134
+ data-slot="title"
135
+ class={cn('flex items-center gap-1.5 font-semibold tracking-tight', ui?.title)}
136
+ >
137
+ <span>{title}</span>
138
+ {#if href}
139
+ <Icon
140
+ name="arrow-right"
141
+ size="xs"
142
+ class="transition-transform duration-150 group-hover:translate-x-0.5"
143
+ />
144
+ {/if}
145
+ </div>
146
+ {/if}
147
+
148
+ {#if description}
149
+ <div data-slot="body" class={cn('leading-relaxed opacity-90', ui?.body)}>
150
+ {description}
151
+ </div>
152
+ {/if}
153
+
154
+ {#if children}
155
+ <div
156
+ data-slot="body"
157
+ class={cn(
158
+ 'leading-relaxed opacity-90 [&_code]:rounded [&_code]:bg-black/5 [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs dark:[&_code]:bg-white/10',
159
+ ui?.body
160
+ )}
161
+ >
162
+ {@render children()}
163
+ </div>
164
+ {/if}
165
+ </div>
166
+
167
+ {#if closable}
168
+ <button
169
+ type="button"
170
+ data-slot="close"
171
+ onclick={handleClose}
172
+ class={cn(
173
+ '-mt-1 -mr-1 inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-lg opacity-60 transition-opacity hover:bg-black/5 hover:opacity-100 focus:outline-none dark:hover:bg-white/10',
174
+ ui?.close
175
+ )}
176
+ aria-label="Dismiss callout"
177
+ >
178
+ <Icon name="close" size="xs" />
179
+ </button>
180
+ {/if}
181
+ </svelte:element>
182
+ {/if}
@@ -0,0 +1,39 @@
1
+ import { type VariantProps } from '../../utils/cn.js';
2
+ import type { Snippet } from 'svelte';
3
+ import type { IconSource } from '../elements/Icon.svelte';
4
+ export declare const calloutVariants: import("tailwind-variants").TVReturnType<{
5
+ type: {
6
+ note: string;
7
+ info: string;
8
+ tip: string;
9
+ success: string;
10
+ important: string;
11
+ warning: string;
12
+ caution: string;
13
+ danger: string;
14
+ };
15
+ }, undefined, "not-prose group relative my-4 flex w-full gap-3.5 overflow-hidden rounded-xl border border-l-4 p-4 text-sm transition-all duration-150", undefined, undefined, undefined>;
16
+ export type CalloutType = NonNullable<VariantProps<typeof calloutVariants>['type']>;
17
+ export interface CalloutSlots {
18
+ root?: string;
19
+ icon?: string;
20
+ content?: string;
21
+ title?: string;
22
+ body?: string;
23
+ close?: string;
24
+ }
25
+ export type CalloutProps = VariantProps<typeof calloutVariants> & {
26
+ title?: string;
27
+ description?: string;
28
+ icon?: IconSource;
29
+ href?: string;
30
+ target?: string;
31
+ closable?: boolean;
32
+ onclose?: () => void;
33
+ class?: string;
34
+ ui?: CalloutSlots;
35
+ children?: Snippet;
36
+ };
37
+ declare const Callout: import("svelte").Component<CalloutProps, {}, "">;
38
+ type Callout = ReturnType<typeof Callout>;
39
+ export default Callout;
@@ -0,0 +1,238 @@
1
+ <script module lang="ts">
2
+ import { tv, cn, type VariantProps } from '../../utils/cn.js';
3
+ import { SvelteMap } from 'svelte/reactivity';
4
+ import type { Snippet } from 'svelte';
5
+ import type { IconSource } from '../elements/Icon.svelte';
6
+
7
+ // Reactive cross-instance tab synchronization registry
8
+ const syncedTabs = new SvelteMap<string, string>();
9
+
10
+ export const codeGroupVariants = tv({
11
+ base: 'not-prose my-4 overflow-hidden rounded-xl border border-neutral-800 bg-neutral-950 shadow-sm',
12
+ variants: {
13
+ themeMode: {
14
+ dark: 'border-neutral-800 bg-neutral-950 text-neutral-100',
15
+ adaptive:
16
+ 'border-neutral-200 bg-neutral-50 text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-100'
17
+ }
18
+ },
19
+ defaultVariants: {
20
+ themeMode: 'dark'
21
+ }
22
+ });
23
+
24
+ export interface CodeGroupItem {
25
+ label: string;
26
+ value?: string;
27
+ code: string;
28
+ language?: string;
29
+ filename?: string;
30
+ icon?: IconSource;
31
+ showLineNumbers?: boolean;
32
+ highlightLines?: number[];
33
+ }
34
+
35
+ export interface CodeGroupSlots {
36
+ root?: string;
37
+ header?: string;
38
+ tabList?: string;
39
+ tab?: string;
40
+ activeTab?: string;
41
+ copyButton?: string;
42
+ body?: string;
43
+ }
44
+
45
+ export type CodeGroupProps = VariantProps<typeof codeGroupVariants> & {
46
+ items?: CodeGroupItem[];
47
+ activeTab?: string;
48
+ syncKey?: string;
49
+ showLineNumbers?: boolean;
50
+ wrap?: boolean;
51
+ class?: string;
52
+ ui?: CodeGroupSlots;
53
+ children?: Snippet<[string]>;
54
+ };
55
+ </script>
56
+
57
+ <script lang="ts">
58
+ import CodeBlock from '../elements/CodeBlock.svelte';
59
+ import Icon from '../elements/Icon.svelte';
60
+
61
+ let {
62
+ items = [],
63
+ activeTab = $bindable(''),
64
+ syncKey,
65
+ showLineNumbers = false,
66
+ wrap = false,
67
+ themeMode = 'dark',
68
+ class: className = '',
69
+ ui,
70
+ children
71
+ }: CodeGroupProps = $props();
72
+
73
+ let copied = $state(false);
74
+ let copyTimeout: ReturnType<typeof setTimeout> | undefined;
75
+
76
+ function getItemValue(item: CodeGroupItem): string {
77
+ return item.value ?? item.label;
78
+ }
79
+
80
+ function inferLanguage(item: CodeGroupItem): string {
81
+ if (item.language) return item.language;
82
+ const lower = (item.filename || item.label).toLowerCase();
83
+ if (['pnpm', 'npm', 'bun', 'yarn', 'npx', 'bash', 'sh', 'terminal', 'shell'].includes(lower)) {
84
+ return 'bash';
85
+ }
86
+ if (lower.endsWith('.svelte')) return 'svelte';
87
+ if (lower.endsWith('.ts') || lower.endsWith('.mts')) return 'typescript';
88
+ if (lower.endsWith('.js') || lower.endsWith('.mjs')) return 'javascript';
89
+ if (lower.endsWith('.json')) return 'json';
90
+ if (lower.endsWith('.css')) return 'css';
91
+ if (lower.endsWith('.yml') || lower.endsWith('.yaml')) return 'yaml';
92
+ if (lower.endsWith('.md') || lower.endsWith('.svx')) return 'markdown';
93
+ if (lower.endsWith('.sql')) return 'sql';
94
+ return 'typescript';
95
+ }
96
+
97
+ let resolvedTab = $derived.by(() => {
98
+ if (syncKey) {
99
+ const memVal = syncedTabs.get(syncKey);
100
+ if (memVal && items.some((item) => getItemValue(item) === memVal)) {
101
+ return memVal;
102
+ }
103
+ if (typeof window !== 'undefined') {
104
+ try {
105
+ const stored = window.localStorage.getItem(`yaxa-cg:${syncKey}`);
106
+ if (stored && items.some((item) => getItemValue(item) === stored)) {
107
+ return stored;
108
+ }
109
+ } catch {
110
+ // Ignore storage access errors
111
+ }
112
+ }
113
+ }
114
+ if (activeTab && items.some((item) => getItemValue(item) === activeTab)) {
115
+ return activeTab;
116
+ }
117
+ return items.length > 0 ? getItemValue(items[0]) : '';
118
+ });
119
+
120
+ let currentItem = $derived(
121
+ items.find((item) => getItemValue(item) === resolvedTab) ?? items[0] ?? null
122
+ );
123
+
124
+ function selectTab(val: string) {
125
+ activeTab = val;
126
+ if (syncKey) {
127
+ syncedTabs.set(syncKey, val);
128
+ if (typeof window !== 'undefined') {
129
+ try {
130
+ window.localStorage.setItem(`yaxa-cg:${syncKey}`, val);
131
+ } catch {
132
+ // Ignore storage write errors
133
+ }
134
+ }
135
+ }
136
+ }
137
+
138
+ async function handleCopy() {
139
+ if (!currentItem?.code) return;
140
+ try {
141
+ await navigator.clipboard.writeText(currentItem.code);
142
+ copied = true;
143
+ if (copyTimeout) clearTimeout(copyTimeout);
144
+ copyTimeout = setTimeout(() => {
145
+ copied = false;
146
+ }, 2000);
147
+ } catch {
148
+ // Clipboard API unavailable
149
+ }
150
+ }
151
+ </script>
152
+
153
+ <div data-slot="root" class={cn(codeGroupVariants({ themeMode, class: className }), ui?.root)}>
154
+ <!-- Tab Bar Header -->
155
+ <div
156
+ data-slot="header"
157
+ class={cn(
158
+ 'flex items-center justify-between border-b px-3 py-1.5',
159
+ themeMode === 'adaptive'
160
+ ? 'border-neutral-200 bg-neutral-100/80 dark:border-neutral-800 dark:bg-neutral-900/80'
161
+ : 'border-neutral-800 bg-neutral-900/80',
162
+ ui?.header
163
+ )}
164
+ >
165
+ <div
166
+ data-slot="tabList"
167
+ role="tablist"
168
+ aria-label="Code group tabs"
169
+ class={cn('flex items-center gap-1 overflow-x-auto', ui?.tabList)}
170
+ >
171
+ {#each items as item (item.value ?? item.label)}
172
+ {@const val = getItemValue(item)}
173
+ {@const isActive = resolvedTab === val}
174
+ <button
175
+ type="button"
176
+ role="tab"
177
+ aria-selected={isActive}
178
+ data-slot="tab"
179
+ data-state={isActive ? 'active' : 'inactive'}
180
+ onclick={() => selectTab(val)}
181
+ class={cn(
182
+ 'relative inline-flex cursor-pointer items-center gap-1.5 rounded-lg px-2.5 py-1 font-mono text-xs font-medium whitespace-nowrap transition-colors focus:outline-none',
183
+ themeMode === 'adaptive'
184
+ ? isActive
185
+ ? 'bg-white text-neutral-950 shadow-2xs dark:bg-neutral-800 dark:text-white'
186
+ : 'text-neutral-500 hover:text-neutral-800 dark:text-neutral-400 dark:hover:text-neutral-200'
187
+ : isActive
188
+ ? 'bg-neutral-800 text-white shadow-2xs'
189
+ : 'text-neutral-400 hover:bg-neutral-800/50 hover:text-neutral-200',
190
+ ui?.tab,
191
+ isActive && ui?.activeTab
192
+ )}
193
+ >
194
+ {#if item.icon}
195
+ <Icon name={item.icon} size="xs" />
196
+ {/if}
197
+ <span>{item.label}</span>
198
+ </button>
199
+ {/each}
200
+ </div>
201
+
202
+ {#if currentItem}
203
+ <button
204
+ type="button"
205
+ data-slot="copyButton"
206
+ onclick={handleCopy}
207
+ class={cn(
208
+ 'ml-2 inline-flex shrink-0 cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors',
209
+ themeMode === 'adaptive'
210
+ ? 'text-neutral-500 hover:bg-neutral-200/70 hover:text-neutral-900 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-white'
211
+ : 'text-neutral-400 hover:bg-neutral-800 hover:text-white',
212
+ ui?.copyButton
213
+ )}
214
+ aria-label="Copy code"
215
+ >
216
+ <Icon name={copied ? 'check' : 'copy'} size="xs" />
217
+ <span>{copied ? 'Copied' : 'Copy'}</span>
218
+ </button>
219
+ {/if}
220
+ </div>
221
+
222
+ <!-- Active Code Body -->
223
+ <div data-slot="body" class={cn('[&>div]:my-0 [&>div]:rounded-none [&>div]:border-0', ui?.body)}>
224
+ {#if children}
225
+ {@render children(resolvedTab)}
226
+ {:else if currentItem}
227
+ <CodeBlock
228
+ code={currentItem.code}
229
+ language={inferLanguage(currentItem)}
230
+ showLineNumbers={currentItem.showLineNumbers ?? showLineNumbers}
231
+ highlightLines={currentItem.highlightLines}
232
+ {wrap}
233
+ {themeMode}
234
+ class="my-0 rounded-none border-0 shadow-none"
235
+ />
236
+ {/if}
237
+ </div>
238
+ </div>
@@ -0,0 +1,41 @@
1
+ import { type VariantProps } from '../../utils/cn.js';
2
+ import type { Snippet } from 'svelte';
3
+ import type { IconSource } from '../elements/Icon.svelte';
4
+ export declare const codeGroupVariants: import("tailwind-variants").TVReturnType<{
5
+ themeMode: {
6
+ dark: string;
7
+ adaptive: string;
8
+ };
9
+ }, undefined, "not-prose my-4 overflow-hidden rounded-xl border border-neutral-800 bg-neutral-950 shadow-sm", undefined, undefined, undefined>;
10
+ export interface CodeGroupItem {
11
+ label: string;
12
+ value?: string;
13
+ code: string;
14
+ language?: string;
15
+ filename?: string;
16
+ icon?: IconSource;
17
+ showLineNumbers?: boolean;
18
+ highlightLines?: number[];
19
+ }
20
+ export interface CodeGroupSlots {
21
+ root?: string;
22
+ header?: string;
23
+ tabList?: string;
24
+ tab?: string;
25
+ activeTab?: string;
26
+ copyButton?: string;
27
+ body?: string;
28
+ }
29
+ export type CodeGroupProps = VariantProps<typeof codeGroupVariants> & {
30
+ items?: CodeGroupItem[];
31
+ activeTab?: string;
32
+ syncKey?: string;
33
+ showLineNumbers?: boolean;
34
+ wrap?: boolean;
35
+ class?: string;
36
+ ui?: CodeGroupSlots;
37
+ children?: Snippet<[string]>;
38
+ };
39
+ declare const CodeGroup: import("svelte").Component<CodeGroupProps, {}, "activeTab">;
40
+ type CodeGroup = ReturnType<typeof CodeGroup>;
41
+ export default CodeGroup;