@autumnsgrove/groveengine 0.6.5 → 0.7.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.
@@ -0,0 +1,39 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ /**
4
+ * GlassCard - A card component with glassmorphism styling
5
+ *
6
+ * Beautiful translucent cards with backdrop blur effects.
7
+ * Includes optional header, footer, and hoverable state.
8
+ *
9
+ * @example Basic glass card
10
+ * ```svelte
11
+ * <GlassCard title="Settings" description="Manage your preferences">
12
+ * <p>Card content here</p>
13
+ * </GlassCard>
14
+ * ```
15
+ *
16
+ * @example Accent card with footer
17
+ * ```svelte
18
+ * <GlassCard variant="accent" hoverable>
19
+ * {#snippet header()}<CustomHeader />{/snippet}
20
+ * Content here
21
+ * {#snippet footer()}<Button>Save</Button>{/snippet}
22
+ * </GlassCard>
23
+ * ```
24
+ */
25
+ type GlassVariant = "default" | "accent" | "dark" | "muted" | "frosted";
26
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "class"> {
27
+ variant?: GlassVariant;
28
+ title?: string;
29
+ description?: string;
30
+ hoverable?: boolean;
31
+ border?: boolean;
32
+ class?: string;
33
+ header?: Snippet;
34
+ footer?: Snippet;
35
+ children?: Snippet;
36
+ }
37
+ declare const GlassCard: import("svelte").Component<Props, {}, "">;
38
+ type GlassCard = ReturnType<typeof GlassCard>;
39
+ export default GlassCard;
@@ -0,0 +1,208 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import { cn } from "../../utils";
4
+ import { fade, scale } from "svelte/transition";
5
+ import { AlertTriangle, Trash2, HelpCircle } from "lucide-svelte";
6
+ import Button from "./Button.svelte";
7
+
8
+ /**
9
+ * GlassConfirmDialog - A confirmation dialog with glassmorphism styling
10
+ *
11
+ * Perfect for destructive actions like delete, or any action that needs user confirmation.
12
+ * Features a glass-effect card over a blurred overlay.
13
+ *
14
+ * @example Basic confirmation
15
+ * ```svelte
16
+ * <GlassConfirmDialog
17
+ * bind:open={showDeleteDialog}
18
+ * title="Delete Post"
19
+ * message="Are you sure you want to delete this post? This cannot be undone."
20
+ * confirmLabel="Delete"
21
+ * variant="danger"
22
+ * onconfirm={handleDelete}
23
+ * />
24
+ * ```
25
+ *
26
+ * @example Custom content
27
+ * ```svelte
28
+ * <GlassConfirmDialog bind:open={showDialog} title="Confirm Changes" onconfirm={save}>
29
+ * <p>You have unsaved changes. Would you like to save them?</p>
30
+ * </GlassConfirmDialog>
31
+ * ```
32
+ */
33
+
34
+ type DialogVariant = "default" | "danger" | "warning";
35
+
36
+ interface Props {
37
+ /** Whether the dialog is open (bindable) */
38
+ open?: boolean;
39
+ /** Dialog title */
40
+ title: string;
41
+ /** Dialog message (if not using children) */
42
+ message?: string;
43
+ /** Confirm button label */
44
+ confirmLabel?: string;
45
+ /** Cancel button label */
46
+ cancelLabel?: string;
47
+ /** Dialog variant (affects styling and icon) */
48
+ variant?: DialogVariant;
49
+ /** Whether the confirm action is loading */
50
+ loading?: boolean;
51
+ /** Called when user confirms */
52
+ onconfirm?: () => void | Promise<void>;
53
+ /** Called when user cancels or closes */
54
+ oncancel?: () => void;
55
+ /** Custom content (overrides message) */
56
+ children?: Snippet;
57
+ }
58
+
59
+ let {
60
+ open = $bindable(false),
61
+ title,
62
+ message,
63
+ confirmLabel = "Confirm",
64
+ cancelLabel = "Cancel",
65
+ variant = "default",
66
+ loading = false,
67
+ onconfirm,
68
+ oncancel,
69
+ children
70
+ }: Props = $props();
71
+
72
+ // Variant-specific styling
73
+ const variantConfig = {
74
+ default: {
75
+ icon: HelpCircle,
76
+ iconClass: "text-accent-muted",
77
+ confirmVariant: "primary" as const
78
+ },
79
+ danger: {
80
+ icon: Trash2,
81
+ iconClass: "text-red-500 dark:text-red-400",
82
+ confirmVariant: "danger" as const
83
+ },
84
+ warning: {
85
+ icon: AlertTriangle,
86
+ iconClass: "text-amber-500 dark:text-amber-400",
87
+ confirmVariant: "primary" as const
88
+ }
89
+ };
90
+
91
+ const config = $derived(variantConfig[variant]);
92
+
93
+ function handleCancel() {
94
+ open = false;
95
+ oncancel?.();
96
+ }
97
+
98
+ async function handleConfirm() {
99
+ try {
100
+ await onconfirm?.();
101
+ open = false;
102
+ } catch (error) {
103
+ // Don't close on error - let the caller handle it
104
+ console.error('Confirm action failed:', error);
105
+ }
106
+ }
107
+
108
+ function handleKeydown(event: KeyboardEvent) {
109
+ if (event.key === "Escape") {
110
+ handleCancel();
111
+ }
112
+ }
113
+
114
+ function handleBackdropClick(event: MouseEvent) {
115
+ // Only close if clicking the backdrop itself, not the dialog
116
+ if (event.target === event.currentTarget) {
117
+ handleCancel();
118
+ }
119
+ }
120
+ </script>
121
+
122
+ <svelte:window onkeydown={handleKeydown} />
123
+
124
+ {#if open}
125
+ <!-- Backdrop with glass effect -->
126
+ <div
127
+ class="fixed inset-0 z-50 flex items-center justify-center p-4"
128
+ onclick={handleBackdropClick}
129
+ role="dialog"
130
+ aria-modal="true"
131
+ aria-labelledby="confirm-dialog-title"
132
+ transition:fade={{ duration: 150 }}
133
+ >
134
+ <!-- Dark overlay with blur -->
135
+ <div
136
+ class="absolute inset-0 bg-black/50 dark:bg-black/60 backdrop-blur-sm"
137
+ aria-hidden="true"
138
+ ></div>
139
+
140
+ <!-- Dialog card -->
141
+ <div
142
+ class={cn(
143
+ "relative z-10 w-full max-w-md",
144
+ "bg-white/80 dark:bg-slate-900/80",
145
+ "backdrop-blur-xl",
146
+ "border border-white/40 dark:border-slate-700/40",
147
+ "rounded-2xl shadow-2xl",
148
+ "overflow-hidden"
149
+ )}
150
+ transition:scale={{ duration: 150, start: 0.95 }}
151
+ >
152
+ <!-- Header with icon -->
153
+ <div class="px-6 pt-6 pb-4 flex items-start gap-4">
154
+ <div class={cn(
155
+ "flex-shrink-0 p-3 rounded-full",
156
+ variant === "danger" && "bg-red-100 dark:bg-red-900/30",
157
+ variant === "warning" && "bg-amber-100 dark:bg-amber-900/30",
158
+ variant === "default" && "bg-accent/10 dark:bg-accent/20"
159
+ )}>
160
+ <svelte:component this={config.icon} class={cn("w-6 h-6", config.iconClass)} />
161
+ </div>
162
+ <div class="flex-1 min-w-0">
163
+ <h3
164
+ id="confirm-dialog-title"
165
+ class="text-lg font-semibold text-foreground leading-tight"
166
+ >
167
+ {title}
168
+ </h3>
169
+ {#if message && !children}
170
+ <p class="mt-2 text-sm text-muted-foreground leading-relaxed">
171
+ {message}
172
+ </p>
173
+ {/if}
174
+ {#if children}
175
+ <div class="mt-2 text-sm text-muted-foreground">
176
+ {@render children()}
177
+ </div>
178
+ {/if}
179
+ </div>
180
+ </div>
181
+
182
+ <!-- Footer with actions -->
183
+ <div class="px-6 py-4 bg-slate-50/50 dark:bg-slate-800/30 border-t border-white/20 dark:border-slate-700/30 flex justify-end gap-3">
184
+ <Button
185
+ variant="ghost"
186
+ onclick={handleCancel}
187
+ disabled={loading}
188
+ >
189
+ {cancelLabel}
190
+ </Button>
191
+ <Button
192
+ variant={config.confirmVariant}
193
+ onclick={handleConfirm}
194
+ disabled={loading}
195
+ >
196
+ {#if loading}
197
+ <span class="inline-flex items-center gap-2">
198
+ <span class="w-4 h-4 border-2 border-current border-t-transparent rounded-full animate-spin"></span>
199
+ Processing...
200
+ </span>
201
+ {:else}
202
+ {confirmLabel}
203
+ {/if}
204
+ </Button>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ {/if}
@@ -0,0 +1,52 @@
1
+ import type { Snippet } from "svelte";
2
+ /**
3
+ * GlassConfirmDialog - A confirmation dialog with glassmorphism styling
4
+ *
5
+ * Perfect for destructive actions like delete, or any action that needs user confirmation.
6
+ * Features a glass-effect card over a blurred overlay.
7
+ *
8
+ * @example Basic confirmation
9
+ * ```svelte
10
+ * <GlassConfirmDialog
11
+ * bind:open={showDeleteDialog}
12
+ * title="Delete Post"
13
+ * message="Are you sure you want to delete this post? This cannot be undone."
14
+ * confirmLabel="Delete"
15
+ * variant="danger"
16
+ * onconfirm={handleDelete}
17
+ * />
18
+ * ```
19
+ *
20
+ * @example Custom content
21
+ * ```svelte
22
+ * <GlassConfirmDialog bind:open={showDialog} title="Confirm Changes" onconfirm={save}>
23
+ * <p>You have unsaved changes. Would you like to save them?</p>
24
+ * </GlassConfirmDialog>
25
+ * ```
26
+ */
27
+ type DialogVariant = "default" | "danger" | "warning";
28
+ interface Props {
29
+ /** Whether the dialog is open (bindable) */
30
+ open?: boolean;
31
+ /** Dialog title */
32
+ title: string;
33
+ /** Dialog message (if not using children) */
34
+ message?: string;
35
+ /** Confirm button label */
36
+ confirmLabel?: string;
37
+ /** Cancel button label */
38
+ cancelLabel?: string;
39
+ /** Dialog variant (affects styling and icon) */
40
+ variant?: DialogVariant;
41
+ /** Whether the confirm action is loading */
42
+ loading?: boolean;
43
+ /** Called when user confirms */
44
+ onconfirm?: () => void | Promise<void>;
45
+ /** Called when user cancels or closes */
46
+ oncancel?: () => void;
47
+ /** Custom content (overrides message) */
48
+ children?: Snippet;
49
+ }
50
+ declare const GlassConfirmDialog: import("svelte").Component<Props, {}, "open">;
51
+ type GlassConfirmDialog = ReturnType<typeof GlassConfirmDialog>;
52
+ export default GlassConfirmDialog;
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+ import { cn } from "../../utils";
5
+
6
+ /**
7
+ * GlassOverlay - A fullscreen overlay with glassmorphism effect
8
+ *
9
+ * Perfect for modal backdrops, loading screens, and focus overlays.
10
+ * Provides a dark translucent backdrop with blur effect.
11
+ *
12
+ * @example Basic overlay
13
+ * ```svelte
14
+ * {#if showModal}
15
+ * <GlassOverlay onclick={closeModal} />
16
+ * {/if}
17
+ * ```
18
+ *
19
+ * @example Custom intensity
20
+ * ```svelte
21
+ * <GlassOverlay variant="light" intensity="strong" />
22
+ * ```
23
+ */
24
+
25
+ type OverlayVariant =
26
+ | "dark" // Dark overlay (default for modals)
27
+ | "light" // Light overlay
28
+ | "accent"; // Tinted with accent color
29
+
30
+ type BlurIntensity =
31
+ | "none" // No blur
32
+ | "light" // 4px blur
33
+ | "medium" // 8px blur
34
+ | "strong"; // 12px blur
35
+
36
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "class"> {
37
+ variant?: OverlayVariant;
38
+ intensity?: BlurIntensity;
39
+ /** Whether clicking the overlay should be interactive (for close handlers) */
40
+ interactive?: boolean;
41
+ class?: string;
42
+ children?: Snippet;
43
+ }
44
+
45
+ let {
46
+ variant = "dark",
47
+ intensity = "light",
48
+ interactive = true,
49
+ class: className,
50
+ children,
51
+ ...restProps
52
+ }: Props = $props();
53
+
54
+ // Variant backgrounds
55
+ const variantClasses: Record<OverlayVariant, string> = {
56
+ dark: "bg-black/50 dark:bg-black/60",
57
+ light: "bg-white/50 dark:bg-slate-900/50",
58
+ accent: "bg-accent/30 dark:bg-accent/20"
59
+ };
60
+
61
+ // Blur intensities
62
+ const intensityClasses: Record<BlurIntensity, string> = {
63
+ none: "",
64
+ light: "backdrop-blur-sm",
65
+ medium: "backdrop-blur",
66
+ strong: "backdrop-blur-md"
67
+ };
68
+
69
+ const computedClass = $derived(
70
+ cn(
71
+ "fixed inset-0 z-50",
72
+ variantClasses[variant],
73
+ intensityClasses[intensity],
74
+ interactive && "cursor-pointer",
75
+ // Animation classes for when used with transitions
76
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
77
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
78
+ className
79
+ )
80
+ );
81
+ </script>
82
+
83
+ <div
84
+ class={computedClass}
85
+ role={interactive ? "button" : "presentation"}
86
+ tabindex={interactive ? 0 : -1}
87
+ aria-label={interactive ? "Close overlay" : undefined}
88
+ {...restProps}
89
+ >
90
+ {#if children}
91
+ {@render children()}
92
+ {/if}
93
+ </div>
@@ -0,0 +1,33 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ /**
4
+ * GlassOverlay - A fullscreen overlay with glassmorphism effect
5
+ *
6
+ * Perfect for modal backdrops, loading screens, and focus overlays.
7
+ * Provides a dark translucent backdrop with blur effect.
8
+ *
9
+ * @example Basic overlay
10
+ * ```svelte
11
+ * {#if showModal}
12
+ * <GlassOverlay onclick={closeModal} />
13
+ * {/if}
14
+ * ```
15
+ *
16
+ * @example Custom intensity
17
+ * ```svelte
18
+ * <GlassOverlay variant="light" intensity="strong" />
19
+ * ```
20
+ */
21
+ type OverlayVariant = "dark" | "light" | "accent";
22
+ type BlurIntensity = "none" | "light" | "medium" | "strong";
23
+ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "class"> {
24
+ variant?: OverlayVariant;
25
+ intensity?: BlurIntensity;
26
+ /** Whether clicking the overlay should be interactive (for close handlers) */
27
+ interactive?: boolean;
28
+ class?: string;
29
+ children?: Snippet;
30
+ }
31
+ declare const GlassOverlay: import("svelte").Component<Props, {}, "">;
32
+ type GlassOverlay = ReturnType<typeof GlassOverlay>;
33
+ export default GlassOverlay;
@@ -15,6 +15,11 @@ export { default as Table } from './Table.svelte';
15
15
  export { default as CollapsibleSection } from './CollapsibleSection.svelte';
16
16
  export { default as Logo } from './Logo.svelte';
17
17
  export { default as LogoLoader } from './LogoLoader.svelte';
18
+ export { default as Glass } from './Glass.svelte';
19
+ export { default as GlassButton } from './GlassButton.svelte';
20
+ export { default as GlassCard } from './GlassCard.svelte';
21
+ export { default as GlassConfirmDialog } from './GlassConfirmDialog.svelte';
22
+ export { default as GlassOverlay } from './GlassOverlay.svelte';
18
23
  export { TableHeader, TableBody, TableRow, TableCell, TableHead, TableFooter, TableCaption, } from '../primitives/table';
19
24
  export * from './toast.js';
20
25
  export declare const UI_VERSION = "0.2.0";
@@ -23,6 +23,12 @@ export { default as Table } from './Table.svelte';
23
23
  export { default as CollapsibleSection } from './CollapsibleSection.svelte';
24
24
  export { default as Logo } from './Logo.svelte';
25
25
  export { default as LogoLoader } from './LogoLoader.svelte';
26
+ // Glass suite - glassmorphism components
27
+ export { default as Glass } from './Glass.svelte';
28
+ export { default as GlassButton } from './GlassButton.svelte';
29
+ export { default as GlassCard } from './GlassCard.svelte';
30
+ export { default as GlassConfirmDialog } from './GlassConfirmDialog.svelte';
31
+ export { default as GlassOverlay } from './GlassOverlay.svelte';
26
32
  // Table sub-components (from primitives)
27
33
  export { TableHeader, TableBody, TableRow, TableCell, TableHead, TableFooter, TableCaption, } from '../primitives/table';
28
34
  // Toast utility
@@ -712,4 +712,140 @@
712
712
  outline: none;
713
713
  box-shadow: inset 0 0 0 2px var(--grove-500);
714
714
  }
715
+
716
+ /* ─────────────────────────────────────────────────────────────
717
+ GLASSMORPHISM UTILITIES
718
+
719
+ Apply these classes to any element for glass effects.
720
+ Combine with Tailwind backdrop-blur-* for blur intensity.
721
+
722
+ Usage:
723
+ <div class="glass">...</div>
724
+ <div class="glass-accent backdrop-blur-md">...</div>
725
+ ───────────────────────────────────────────────────────────── */
726
+
727
+ /* Base glass - light translucent background */
728
+ .glass {
729
+ background-color: rgb(255 255 255 / 0.7);
730
+ border: 1px solid rgb(255 255 255 / 0.3);
731
+ backdrop-filter: blur(4px);
732
+ }
733
+
734
+ :is(.dark .glass) {
735
+ background-color: rgb(30 41 59 / 0.6);
736
+ border-color: rgb(71 85 105 / 0.3);
737
+ }
738
+
739
+ /* Glass surface - higher opacity for headers/navbars */
740
+ .glass-surface {
741
+ background-color: rgb(255 255 255 / 0.95);
742
+ border: 1px solid rgb(255 255 255 / 0.3);
743
+ backdrop-filter: blur(4px);
744
+ }
745
+
746
+ :is(.dark .glass-surface) {
747
+ background-color: rgb(30 41 59 / 0.95);
748
+ border-color: rgb(71 85 105 / 0.3);
749
+ }
750
+
751
+ /* Glass overlay - dark backdrop for modals */
752
+ .glass-overlay {
753
+ background-color: rgb(0 0 0 / 0.5);
754
+ backdrop-filter: blur(4px);
755
+ }
756
+
757
+ :is(.dark .glass-overlay) {
758
+ background-color: rgb(0 0 0 / 0.6);
759
+ }
760
+
761
+ /* Glass accent - tinted with grove green (accent color) */
762
+ .glass-accent {
763
+ background-color: color-mix(in srgb, var(--grove-500) 20%, transparent);
764
+ border: 1px solid color-mix(in srgb, var(--grove-500) 30%, transparent);
765
+ backdrop-filter: blur(4px);
766
+ }
767
+
768
+ :is(.dark .glass-accent) {
769
+ background-color: color-mix(in srgb, var(--grove-500) 15%, transparent);
770
+ border-color: color-mix(in srgb, var(--grove-500) 20%, transparent);
771
+ }
772
+
773
+ /* Glass muted - subtle, barely visible */
774
+ .glass-muted {
775
+ background-color: rgb(255 255 255 / 0.4);
776
+ border: 1px solid rgb(255 255 255 / 0.2);
777
+ backdrop-filter: blur(4px);
778
+ }
779
+
780
+ :is(.dark .glass-muted) {
781
+ background-color: rgb(15 23 42 / 0.3);
782
+ border-color: rgb(51 65 85 / 0.2);
783
+ }
784
+
785
+ /* Glass tint - light background for text readability */
786
+ .glass-tint {
787
+ background-color: rgb(255 255 255 / 0.6);
788
+ border: 1px solid rgb(255 255 255 / 0.2);
789
+ backdrop-filter: blur(4px);
790
+ }
791
+
792
+ :is(.dark .glass-tint) {
793
+ background-color: rgb(15 23 42 / 0.5);
794
+ border-color: rgb(51 65 85 / 0.3);
795
+ }
796
+
797
+ /* Glass dark - dark translucent background */
798
+ .glass-dark {
799
+ background-color: rgb(15 23 42 / 0.7);
800
+ border: 1px solid rgb(51 65 85 / 0.3);
801
+ backdrop-filter: blur(4px);
802
+ color: white;
803
+ }
804
+
805
+ :is(.dark .glass-dark) {
806
+ background-color: rgb(2 6 23 / 0.8);
807
+ border-color: rgb(51 65 85 / 0.3);
808
+ }
809
+
810
+ /* Glass frosted - stronger effect, more opaque */
811
+ .glass-frosted {
812
+ background-color: rgb(255 255 255 / 0.85);
813
+ border: 1px solid rgb(255 255 255 / 0.4);
814
+ backdrop-filter: blur(12px);
815
+ }
816
+
817
+ :is(.dark .glass-frosted) {
818
+ background-color: rgb(30 41 59 / 0.8);
819
+ border-color: rgb(71 85 105 / 0.4);
820
+ }
821
+
822
+ /* No border variants */
823
+ .glass-borderless {
824
+ border: none;
825
+ }
826
+
827
+ /* Hover states for interactive glass elements */
828
+ .glass-hover:hover {
829
+ background-color: rgb(255 255 255 / 0.85);
830
+ border-color: rgb(255 255 255 / 0.5);
831
+ }
832
+
833
+ :is(.dark .glass-hover:hover) {
834
+ background-color: rgb(30 41 59 / 0.75);
835
+ border-color: rgb(71 85 105 / 0.4);
836
+ }
837
+
838
+ /* Reduced motion: disable blur effects for performance/accessibility */
839
+ @media (prefers-reduced-motion: reduce) {
840
+ .glass,
841
+ .glass-surface,
842
+ .glass-overlay,
843
+ .glass-accent,
844
+ .glass-muted,
845
+ .glass-tint,
846
+ .glass-dark,
847
+ .glass-frosted {
848
+ backdrop-filter: none;
849
+ }
850
+ }
715
851
  }
@@ -5,6 +5,8 @@
5
5
  * and anchor resolution. Used by ContentWithGutter component and related
6
6
  * functionality across the site.
7
7
  */
8
+ import type { GutterItem as MarkdownGutterItem } from "./markdown.js";
9
+ export type GutterItem = MarkdownGutterItem;
8
10
  /** Anchor types supported by the gutter system */
9
11
  export type AnchorType = "none" | "paragraph" | "tag" | "header";
10
12
  /** Parsed anchor result */
@@ -18,14 +20,6 @@ export interface Header {
18
20
  text: string;
19
21
  level?: number;
20
22
  }
21
- /** Gutter item with anchor */
22
- export interface GutterItem {
23
- anchor?: string;
24
- type?: string;
25
- content?: string;
26
- src?: string;
27
- [key: string]: unknown;
28
- }
29
23
  /**
30
24
  * Parse anchor string to determine anchor type and value
31
25
  * @param anchor - The anchor string from manifest
@@ -54,6 +54,7 @@ export interface GutterItem extends GutterItemBase {
54
54
  content?: string;
55
55
  src?: string;
56
56
  images?: GalleryImage[];
57
+ [key: string]: unknown;
57
58
  }
58
59
  /** Gutter manifest structure */
59
60
  export interface GutterManifest {