@autumnsgrove/groveengine 0.8.5 → 0.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.
- package/dist/components/WispPanel.svelte +0 -1
- package/dist/components/admin/GutterManager.svelte +213 -101
- package/dist/components/admin/MarkdownEditor.svelte +6 -3
- package/dist/components/custom/ContentWithGutter.svelte +7 -13
- package/dist/components/custom/GutterItem.svelte +8 -2
- package/dist/components/quota/UpgradePrompt.svelte +1 -0
- package/dist/config/domain-blocklist.d.ts +59 -0
- package/dist/config/domain-blocklist.js +731 -0
- package/dist/config/index.d.ts +3 -1
- package/dist/config/index.js +2 -1
- package/dist/config/offensive-blocklist.d.ts +44 -0
- package/dist/config/offensive-blocklist.js +751 -0
- package/dist/config/terrarium.d.ts +109 -0
- package/dist/config/terrarium.js +125 -0
- package/dist/styles/tokens.css +90 -0
- package/dist/types/dom-to-image-more.d.ts +39 -0
- package/dist/ui/components/chrome/Footer.svelte +137 -0
- package/dist/ui/components/chrome/Footer.svelte.d.ts +11 -0
- package/dist/ui/components/chrome/FooterMinimal.svelte +75 -0
- package/dist/ui/components/chrome/FooterMinimal.svelte.d.ts +10 -0
- package/dist/ui/components/chrome/Header.svelte +113 -0
- package/dist/ui/components/chrome/Header.svelte.d.ts +11 -0
- package/dist/ui/components/chrome/HeaderMinimal.svelte +68 -0
- package/dist/ui/components/chrome/HeaderMinimal.svelte.d.ts +9 -0
- package/dist/ui/components/chrome/MobileMenu.svelte +145 -0
- package/dist/ui/components/chrome/MobileMenu.svelte.d.ts +9 -0
- package/dist/ui/components/chrome/ThemeToggle.svelte +34 -0
- package/dist/ui/components/chrome/ThemeToggle.svelte.d.ts +3 -0
- package/dist/ui/components/chrome/defaults.d.ts +6 -0
- package/dist/ui/components/chrome/defaults.js +65 -0
- package/dist/ui/components/chrome/index.d.ts +13 -0
- package/dist/ui/components/chrome/index.js +14 -0
- package/dist/ui/components/chrome/types.d.ts +19 -0
- package/dist/ui/components/chrome/types.js +8 -0
- package/dist/ui/components/content/RoadmapPreview.svelte +2 -1
- package/dist/ui/components/forms/ContentSearch.svelte +406 -0
- package/dist/ui/components/forms/ContentSearch.svelte.d.ts +71 -0
- package/dist/ui/components/forms/SearchInput.svelte +0 -1
- package/dist/ui/components/forms/filterUtils.d.ts +138 -0
- package/dist/ui/components/forms/filterUtils.js +240 -0
- package/dist/ui/components/forms/index.d.ts +2 -0
- package/dist/ui/components/forms/index.js +5 -1
- package/dist/ui/components/gallery/ImageGallery.svelte +17 -3
- package/dist/ui/components/gallery/Lightbox.svelte +11 -3
- package/dist/ui/components/gallery/ZoomableImage.svelte +13 -2
- package/dist/ui/components/icons/index.d.ts +2 -1
- package/dist/ui/components/icons/index.js +14 -3
- package/dist/ui/components/icons/lucide.d.ts +213 -0
- package/dist/ui/components/icons/lucide.js +224 -0
- package/dist/ui/components/terrarium/AssetPalette.svelte +207 -0
- package/dist/ui/components/terrarium/AssetPalette.svelte.d.ts +7 -0
- package/dist/ui/components/terrarium/Canvas.svelte +231 -0
- package/dist/ui/components/terrarium/Canvas.svelte.d.ts +14 -0
- package/dist/ui/components/terrarium/ExportDialog.svelte +307 -0
- package/dist/ui/components/terrarium/ExportDialog.svelte.d.ts +18 -0
- package/dist/ui/components/terrarium/PaletteItem.svelte +169 -0
- package/dist/ui/components/terrarium/PaletteItem.svelte.d.ts +9 -0
- package/dist/ui/components/terrarium/PlacedAsset.svelte +222 -0
- package/dist/ui/components/terrarium/PlacedAsset.svelte.d.ts +11 -0
- package/dist/ui/components/terrarium/Terrarium.svelte +266 -0
- package/dist/ui/components/terrarium/Terrarium.svelte.d.ts +3 -0
- package/dist/ui/components/terrarium/Toolbar.svelte +299 -0
- package/dist/ui/components/terrarium/Toolbar.svelte.d.ts +24 -0
- package/dist/ui/components/terrarium/index.d.ts +31 -0
- package/dist/ui/components/terrarium/index.js +33 -0
- package/dist/ui/components/terrarium/terrariumState.svelte.d.ts +45 -0
- package/dist/ui/components/terrarium/terrariumState.svelte.js +291 -0
- package/dist/ui/components/terrarium/types.d.ts +139 -0
- package/dist/ui/components/terrarium/types.js +43 -0
- package/dist/ui/components/terrarium/utils/export.d.ts +48 -0
- package/dist/ui/components/terrarium/utils/export.js +148 -0
- package/dist/ui/components/typography/index.d.ts +0 -10
- package/dist/ui/components/typography/index.js +1 -12
- package/dist/ui/components/ui/CollapsibleSection.svelte +12 -0
- package/dist/ui/components/ui/GlassConfirmDialog.svelte +9 -0
- package/dist/ui/components/ui/GlassOverlay.svelte +2 -1
- package/dist/ui/components/ui/Input.svelte +9 -1
- package/dist/ui/components/ui/Input.svelte.d.ts +2 -0
- package/dist/ui/components/ui/Textarea.svelte +9 -1
- package/dist/ui/components/ui/Textarea.svelte.d.ts +2 -0
- package/dist/ui/stores/index.d.ts +6 -0
- package/dist/ui/stores/index.js +6 -0
- package/dist/ui/stores/season.d.ts +14 -0
- package/dist/ui/stores/season.js +65 -0
- package/dist/ui/tokens/fonts.d.ts +1 -1
- package/dist/ui/tokens/fonts.js +0 -126
- package/package.json +46 -22
- package/static/fonts/alagard.ttf +0 -0
- package/LICENSE +0 -378
- package/dist/ui/components/typography/BodoniModa.svelte +0 -17
- package/dist/ui/components/typography/BodoniModa.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Cormorant.svelte +0 -17
- package/dist/ui/components/typography/Cormorant.svelte.d.ts +0 -10
- package/dist/ui/components/typography/EBGaramond.svelte +0 -17
- package/dist/ui/components/typography/EBGaramond.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Fraunces.svelte +0 -17
- package/dist/ui/components/typography/Fraunces.svelte.d.ts +0 -10
- package/dist/ui/components/typography/InstrumentSans.svelte +0 -17
- package/dist/ui/components/typography/InstrumentSans.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Lora.svelte +0 -17
- package/dist/ui/components/typography/Lora.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Luciole.svelte +0 -17
- package/dist/ui/components/typography/Luciole.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Manrope.svelte +0 -17
- package/dist/ui/components/typography/Manrope.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Merriweather.svelte +0 -17
- package/dist/ui/components/typography/Merriweather.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Nunito.svelte +0 -17
- package/dist/ui/components/typography/Nunito.svelte.d.ts +0 -10
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { page } from '$app/stores';
|
|
3
|
+
import { Logo } from '../nature';
|
|
4
|
+
import ThemeToggle from './ThemeToggle.svelte';
|
|
5
|
+
import MobileMenu from './MobileMenu.svelte';
|
|
6
|
+
import { seasonStore } from '../../stores/season';
|
|
7
|
+
import { Menu } from 'lucide-svelte';
|
|
8
|
+
import type { NavItem, MaxWidth, Season } from './types';
|
|
9
|
+
import { isActivePath } from './types';
|
|
10
|
+
import { DEFAULT_NAV_ITEMS } from './defaults';
|
|
11
|
+
|
|
12
|
+
// Determine current page for highlighting
|
|
13
|
+
let currentPath = $derived($page.url.pathname);
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
navItems?: NavItem[];
|
|
17
|
+
maxWidth?: MaxWidth;
|
|
18
|
+
brandTitle?: string;
|
|
19
|
+
season?: Season;
|
|
20
|
+
onSeasonChange?: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
navItems,
|
|
25
|
+
maxWidth = 'default',
|
|
26
|
+
brandTitle,
|
|
27
|
+
season,
|
|
28
|
+
onSeasonChange
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
const maxWidthClass = {
|
|
32
|
+
narrow: 'max-w-3xl',
|
|
33
|
+
default: 'max-w-4xl',
|
|
34
|
+
wide: 'max-w-5xl'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Mobile menu state
|
|
38
|
+
let mobileMenuOpen = $state(false);
|
|
39
|
+
|
|
40
|
+
// Toggle season on logo click
|
|
41
|
+
function handleLogoClick() {
|
|
42
|
+
if (onSeasonChange) {
|
|
43
|
+
onSeasonChange();
|
|
44
|
+
} else {
|
|
45
|
+
seasonStore.cycle();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const items = navItems || DEFAULT_NAV_ITEMS;
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<header class="sticky top-0 z-40 py-6 px-6 border-b border-default bg-surface/95 backdrop-blur-sm">
|
|
53
|
+
<div class="{maxWidthClass[maxWidth]} mx-auto flex items-center justify-between">
|
|
54
|
+
<!-- Logo area -->
|
|
55
|
+
<div class="flex items-center gap-2">
|
|
56
|
+
<!-- Logo icon - clickable to toggle season -->
|
|
57
|
+
<button
|
|
58
|
+
onclick={handleLogoClick}
|
|
59
|
+
class="flex-shrink-0 transition-transform hover:scale-110 active:scale-95"
|
|
60
|
+
aria-label="Toggle season theme"
|
|
61
|
+
title="Click to change season"
|
|
62
|
+
>
|
|
63
|
+
<Logo class="w-6 h-6" season={season || $seasonStore} />
|
|
64
|
+
</button>
|
|
65
|
+
|
|
66
|
+
<!-- Brand title or "Grove" text - home link, hidden on mobile -->
|
|
67
|
+
{#if brandTitle}
|
|
68
|
+
<span class="hidden sm:block text-xl font-serif text-foreground">
|
|
69
|
+
{brandTitle}
|
|
70
|
+
</span>
|
|
71
|
+
{:else}
|
|
72
|
+
<a
|
|
73
|
+
href="/"
|
|
74
|
+
class="hidden sm:block text-xl font-serif text-foreground hover:text-accent-muted transition-colors"
|
|
75
|
+
>
|
|
76
|
+
Grove
|
|
77
|
+
</a>
|
|
78
|
+
{/if}
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<!-- Desktop navigation -->
|
|
82
|
+
<nav class="hidden md:flex items-center gap-4 lg:gap-6 text-sm font-sans">
|
|
83
|
+
{#each items as item}
|
|
84
|
+
<a
|
|
85
|
+
href={item.href}
|
|
86
|
+
target={item.external ? '_blank' : undefined}
|
|
87
|
+
rel={item.external ? 'noopener noreferrer' : undefined}
|
|
88
|
+
class="transition-colors whitespace-nowrap {isActivePath(item.href, currentPath)
|
|
89
|
+
? 'text-accent-muted'
|
|
90
|
+
: 'text-foreground-subtle hover:text-accent-muted'}"
|
|
91
|
+
>
|
|
92
|
+
<span>{item.label}</span>
|
|
93
|
+
</a>
|
|
94
|
+
{/each}
|
|
95
|
+
<ThemeToggle />
|
|
96
|
+
</nav>
|
|
97
|
+
|
|
98
|
+
<!-- Mobile: Theme toggle + hamburger -->
|
|
99
|
+
<div class="flex md:hidden items-center gap-2">
|
|
100
|
+
<ThemeToggle />
|
|
101
|
+
<button
|
|
102
|
+
onclick={() => (mobileMenuOpen = true)}
|
|
103
|
+
class="p-2 -mr-2 text-foreground-subtle hover:text-foreground transition-colors rounded-lg hover:bg-surface-hover"
|
|
104
|
+
aria-label="Open menu"
|
|
105
|
+
>
|
|
106
|
+
<Menu class="w-5 h-5" />
|
|
107
|
+
</button>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</header>
|
|
111
|
+
|
|
112
|
+
<!-- Mobile menu overlay -->
|
|
113
|
+
<MobileMenu bind:open={mobileMenuOpen} onClose={() => (mobileMenuOpen = false)} {navItems} />
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NavItem, MaxWidth, Season } from './types';
|
|
2
|
+
interface Props {
|
|
3
|
+
navItems?: NavItem[];
|
|
4
|
+
maxWidth?: MaxWidth;
|
|
5
|
+
brandTitle?: string;
|
|
6
|
+
season?: Season;
|
|
7
|
+
onSeasonChange?: () => void;
|
|
8
|
+
}
|
|
9
|
+
declare const Header: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Header = ReturnType<typeof Header>;
|
|
11
|
+
export default Header;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Header - Status page navigation header
|
|
4
|
+
*
|
|
5
|
+
* Clean, minimal header with Grove branding and theme toggle.
|
|
6
|
+
*/
|
|
7
|
+
import { Trees, Sun, Moon, Rss } from 'lucide-svelte';
|
|
8
|
+
import ThemeToggle from './ThemeToggle.svelte';
|
|
9
|
+
import { cn } from '../../../utils/cn';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
class?: string;
|
|
13
|
+
brandTitle?: string;
|
|
14
|
+
subtitle?: string;
|
|
15
|
+
maxWidth?: 'narrow' | 'default' | 'wide';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
class: className,
|
|
20
|
+
brandTitle = 'Grove',
|
|
21
|
+
subtitle = 'Status',
|
|
22
|
+
maxWidth = 'default'
|
|
23
|
+
}: Props = $props();
|
|
24
|
+
|
|
25
|
+
const maxWidthClass = {
|
|
26
|
+
narrow: 'max-w-2xl',
|
|
27
|
+
default: 'max-w-4xl',
|
|
28
|
+
wide: 'max-w-5xl'
|
|
29
|
+
};
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<header
|
|
33
|
+
class={cn(
|
|
34
|
+
'sticky top-0 z-40 py-4 px-6',
|
|
35
|
+
'bg-white/60 dark:bg-slate-900/60 backdrop-blur-md',
|
|
36
|
+
'border-b border-white/40 dark:border-slate-700/40',
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
>
|
|
40
|
+
<div class="{maxWidthClass[maxWidth]} mx-auto flex items-center justify-between">
|
|
41
|
+
<!-- Logo and title -->
|
|
42
|
+
<a href="/" class="flex items-center gap-2.5 group">
|
|
43
|
+
<div class="p-1.5 rounded-lg bg-grove-500/10 group-hover:bg-grove-500/20 transition-colors">
|
|
44
|
+
<Trees class="w-6 h-6 text-grove-600 dark:text-grove-400" />
|
|
45
|
+
</div>
|
|
46
|
+
<div>
|
|
47
|
+
<span class="font-semibold text-foreground">{brandTitle}</span>
|
|
48
|
+
<span class="text-foreground-muted ml-1">{subtitle}</span>
|
|
49
|
+
</div>
|
|
50
|
+
</a>
|
|
51
|
+
|
|
52
|
+
<!-- Actions -->
|
|
53
|
+
<div class="flex items-center gap-2">
|
|
54
|
+
<!-- RSS Feed link -->
|
|
55
|
+
<a
|
|
56
|
+
href="/feed"
|
|
57
|
+
class="p-2 rounded-lg text-foreground-muted hover:text-foreground hover:bg-white/50 dark:hover:bg-slate-800/50 transition-colors"
|
|
58
|
+
aria-label="RSS Feed"
|
|
59
|
+
title="Subscribe via RSS"
|
|
60
|
+
>
|
|
61
|
+
<Rss class="w-5 h-5" />
|
|
62
|
+
</a>
|
|
63
|
+
|
|
64
|
+
<!-- Theme toggle -->
|
|
65
|
+
<ThemeToggle />
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</header>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
class?: string;
|
|
3
|
+
brandTitle?: string;
|
|
4
|
+
subtitle?: string;
|
|
5
|
+
maxWidth?: 'narrow' | 'default' | 'wide';
|
|
6
|
+
}
|
|
7
|
+
declare const HeaderMinimal: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type HeaderMinimal = ReturnType<typeof HeaderMinimal>;
|
|
9
|
+
export default HeaderMinimal;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { page } from '$app/stores';
|
|
3
|
+
import { X } from 'lucide-svelte';
|
|
4
|
+
import type { NavItem } from './types';
|
|
5
|
+
import { isActivePath } from './types';
|
|
6
|
+
import { DEFAULT_MOBILE_NAV_ITEMS } from './defaults';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
open: boolean;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
navItems?: NavItem[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let { open = $bindable(), onClose, navItems }: Props = $props();
|
|
15
|
+
|
|
16
|
+
let currentPath = $derived($page.url.pathname);
|
|
17
|
+
|
|
18
|
+
// References for focus management
|
|
19
|
+
let closeButtonRef: HTMLButtonElement | undefined = $state();
|
|
20
|
+
let menuPanelRef: HTMLDivElement | undefined = $state();
|
|
21
|
+
let previouslyFocusedElement: HTMLElement | null = null;
|
|
22
|
+
|
|
23
|
+
// Handle close action
|
|
24
|
+
function handleClose() {
|
|
25
|
+
open = false;
|
|
26
|
+
onClose();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Close on escape key
|
|
30
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
31
|
+
if (event.key === 'Escape' && open) {
|
|
32
|
+
handleClose();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Focus trap: Tab key cycles within menu
|
|
36
|
+
if (event.key === 'Tab' && open && menuPanelRef) {
|
|
37
|
+
const focusableElements = menuPanelRef.querySelectorAll<HTMLElement>(
|
|
38
|
+
'button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
39
|
+
);
|
|
40
|
+
const firstElement = focusableElements[0];
|
|
41
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
42
|
+
|
|
43
|
+
if (event.shiftKey && document.activeElement === firstElement) {
|
|
44
|
+
// Shift+Tab on first element: wrap to last
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
lastElement?.focus();
|
|
47
|
+
} else if (!event.shiftKey && document.activeElement === lastElement) {
|
|
48
|
+
// Tab on last element: wrap to first
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
firstElement?.focus();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Focus management and body scroll lock when menu opens/closes
|
|
56
|
+
$effect(() => {
|
|
57
|
+
if (open) {
|
|
58
|
+
// Store the previously focused element to restore later
|
|
59
|
+
previouslyFocusedElement = document.activeElement as HTMLElement;
|
|
60
|
+
// Focus the close button when menu opens
|
|
61
|
+
requestAnimationFrame(() => {
|
|
62
|
+
closeButtonRef?.focus();
|
|
63
|
+
});
|
|
64
|
+
// Prevent body scroll
|
|
65
|
+
document.body.style.overflow = 'hidden';
|
|
66
|
+
} else {
|
|
67
|
+
if (previouslyFocusedElement) {
|
|
68
|
+
// Restore focus when menu closes
|
|
69
|
+
previouslyFocusedElement.focus();
|
|
70
|
+
previouslyFocusedElement = null;
|
|
71
|
+
}
|
|
72
|
+
// Restore body scroll
|
|
73
|
+
document.body.style.overflow = '';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Cleanup on unmount: ensure body scroll is always restored
|
|
77
|
+
return () => {
|
|
78
|
+
document.body.style.overflow = '';
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const items = navItems || DEFAULT_MOBILE_NAV_ITEMS;
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<svelte:window on:keydown={handleKeydown} />
|
|
86
|
+
|
|
87
|
+
<!-- Backdrop -->
|
|
88
|
+
{#if open}
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
class="fixed inset-0 z-[100] bg-black/50 backdrop-blur-sm transition-opacity"
|
|
92
|
+
onclick={handleClose}
|
|
93
|
+
aria-label="Close menu"
|
|
94
|
+
></button>
|
|
95
|
+
{/if}
|
|
96
|
+
|
|
97
|
+
<!-- Slide-out panel -->
|
|
98
|
+
<div
|
|
99
|
+
bind:this={menuPanelRef}
|
|
100
|
+
class="fixed top-0 right-0 z-[110] h-full w-64 transform bg-surface border-l border-default shadow-xl transition-transform duration-300 ease-out {open
|
|
101
|
+
? 'translate-x-0'
|
|
102
|
+
: 'translate-x-full'}"
|
|
103
|
+
role="dialog"
|
|
104
|
+
aria-modal="true"
|
|
105
|
+
aria-label="Navigation menu"
|
|
106
|
+
aria-hidden={!open}
|
|
107
|
+
>
|
|
108
|
+
<!-- Header -->
|
|
109
|
+
<div class="flex items-center justify-between p-4 border-b border-default">
|
|
110
|
+
<span class="text-sm font-medium text-foreground-subtle">Menu</span>
|
|
111
|
+
<button
|
|
112
|
+
bind:this={closeButtonRef}
|
|
113
|
+
type="button"
|
|
114
|
+
onclick={handleClose}
|
|
115
|
+
class="p-2 -mr-2 text-foreground-subtle hover:text-foreground transition-colors rounded-lg hover:bg-surface-hover"
|
|
116
|
+
aria-label="Close menu"
|
|
117
|
+
>
|
|
118
|
+
<X class="w-5 h-5" />
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<!-- Navigation -->
|
|
123
|
+
<nav class="p-2">
|
|
124
|
+
{#each items as item}
|
|
125
|
+
{@const Icon = item.icon}
|
|
126
|
+
{@const active = isActivePath(item.href, currentPath)}
|
|
127
|
+
<a
|
|
128
|
+
href={item.href}
|
|
129
|
+
target={item.external ? '_blank' : undefined}
|
|
130
|
+
rel={item.external ? 'noopener noreferrer' : undefined}
|
|
131
|
+
onclick={handleClose}
|
|
132
|
+
class="flex items-center gap-3 px-3 py-3 rounded-lg transition-colors
|
|
133
|
+
{active
|
|
134
|
+
? 'bg-accent/10 text-accent-muted'
|
|
135
|
+
: 'text-foreground hover:bg-surface-hover hover:text-accent-muted'}"
|
|
136
|
+
>
|
|
137
|
+
<Icon class="w-5 h-5 flex-shrink-0" />
|
|
138
|
+
<span class="text-sm font-medium">{item.label}</span>
|
|
139
|
+
{#if item.external}
|
|
140
|
+
<span class="text-xs text-foreground-subtle ml-auto">External</span>
|
|
141
|
+
{/if}
|
|
142
|
+
</a>
|
|
143
|
+
{/each}
|
|
144
|
+
</nav>
|
|
145
|
+
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NavItem } from './types';
|
|
2
|
+
interface Props {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
navItems?: NavItem[];
|
|
6
|
+
}
|
|
7
|
+
declare const MobileMenu: import("svelte").Component<Props, {}, "open">;
|
|
8
|
+
type MobileMenu = ReturnType<typeof MobileMenu>;
|
|
9
|
+
export default MobileMenu;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { themeStore } from '../../stores/theme';
|
|
3
|
+
|
|
4
|
+
// Extract the resolvedTheme store and toggle function
|
|
5
|
+
const { resolvedTheme, toggle } = themeStore;
|
|
6
|
+
let isDark = $derived($resolvedTheme === 'dark');
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<button
|
|
10
|
+
onclick={() => toggle()}
|
|
11
|
+
class="p-2 rounded-lg text-foreground-subtle hover:text-accent-muted hover:bg-surface transition-colors"
|
|
12
|
+
aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
|
|
13
|
+
title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
|
|
14
|
+
>
|
|
15
|
+
{#if isDark}
|
|
16
|
+
<!-- Sun icon - shown in dark mode -->
|
|
17
|
+
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
18
|
+
<circle cx="12" cy="12" r="5" />
|
|
19
|
+
<line x1="12" y1="1" x2="12" y2="3" />
|
|
20
|
+
<line x1="12" y1="21" x2="12" y2="23" />
|
|
21
|
+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
|
22
|
+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
|
23
|
+
<line x1="1" y1="12" x2="3" y2="12" />
|
|
24
|
+
<line x1="21" y1="12" x2="23" y2="12" />
|
|
25
|
+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
|
26
|
+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
|
27
|
+
</svg>
|
|
28
|
+
{:else}
|
|
29
|
+
<!-- Moon icon - shown in light mode -->
|
|
30
|
+
<svg class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
31
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
32
|
+
</svg>
|
|
33
|
+
{/if}
|
|
34
|
+
</button>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { NavItem, FooterLink } from "./types";
|
|
2
|
+
export declare const DEFAULT_NAV_ITEMS: NavItem[];
|
|
3
|
+
export declare const DEFAULT_MOBILE_NAV_ITEMS: NavItem[];
|
|
4
|
+
export declare const DEFAULT_RESOURCE_LINKS: FooterLink[];
|
|
5
|
+
export declare const DEFAULT_CONNECT_LINKS: FooterLink[];
|
|
6
|
+
export declare const DEFAULT_LEGAL_LINKS: FooterLink[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Scroll, Telescope, MapPin, Tag, BookOpen, Trees, PenLine, Home, CircleDollarSign, Send, Github, ExternalLink, Mail, Hammer, Grape, } from "lucide-svelte";
|
|
2
|
+
// Default navigation items (desktop)
|
|
3
|
+
export const DEFAULT_NAV_ITEMS = [
|
|
4
|
+
{ href: "/manifesto", label: "Manifesto", icon: Scroll },
|
|
5
|
+
{ href: "/vision", label: "Vision", icon: Telescope },
|
|
6
|
+
{ href: "/roadmap", label: "Roadmap", icon: MapPin },
|
|
7
|
+
{ href: "/pricing", label: "Pricing", icon: Tag },
|
|
8
|
+
{ href: "/knowledge", label: "Knowledge", icon: BookOpen },
|
|
9
|
+
{ href: "/forest", label: "Forest", icon: Trees },
|
|
10
|
+
{
|
|
11
|
+
href: "https://autumnsgrove.com/blog",
|
|
12
|
+
label: "Blog",
|
|
13
|
+
icon: PenLine,
|
|
14
|
+
external: true,
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
// Default mobile navigation items (includes Home and Contact)
|
|
18
|
+
export const DEFAULT_MOBILE_NAV_ITEMS = [
|
|
19
|
+
{ href: "/", label: "Home", icon: Home },
|
|
20
|
+
{ href: "/manifesto", label: "Manifesto", icon: Scroll },
|
|
21
|
+
{ href: "/vision", label: "Vision", icon: Telescope },
|
|
22
|
+
{ href: "/roadmap", label: "Roadmap", icon: MapPin },
|
|
23
|
+
{ href: "/pricing", label: "Pricing", icon: CircleDollarSign },
|
|
24
|
+
{ href: "/knowledge", label: "Knowledge", icon: BookOpen },
|
|
25
|
+
{ href: "/forest", label: "Forest", icon: Trees },
|
|
26
|
+
{ href: "/contact", label: "Contact", icon: Send },
|
|
27
|
+
{
|
|
28
|
+
href: "https://autumnsgrove.com/blog",
|
|
29
|
+
label: "Blog",
|
|
30
|
+
icon: PenLine,
|
|
31
|
+
external: true,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
// Default resource links (for footer)
|
|
35
|
+
export const DEFAULT_RESOURCE_LINKS = [
|
|
36
|
+
{ href: "/knowledge", label: "Knowledge Base", icon: BookOpen },
|
|
37
|
+
{ href: "/roadmap", label: "Roadmap", icon: MapPin },
|
|
38
|
+
{ href: "/roadmap/workshop", label: "Workshop", icon: Hammer },
|
|
39
|
+
{ href: "/forest", label: "Forest", icon: Trees },
|
|
40
|
+
{ href: "/vineyard", label: "Vineyard", icon: Grape },
|
|
41
|
+
{ href: "/pricing", label: "Pricing", icon: Tag },
|
|
42
|
+
{ href: "/manifesto", label: "Manifesto", icon: Scroll },
|
|
43
|
+
{ href: "/vision", label: "Our Vision", icon: Telescope },
|
|
44
|
+
];
|
|
45
|
+
// Default connect links (for footer)
|
|
46
|
+
export const DEFAULT_CONNECT_LINKS = [
|
|
47
|
+
{ href: "/contact", label: "Contact", icon: Mail },
|
|
48
|
+
{
|
|
49
|
+
href: "https://autumnsgrove.com/blog",
|
|
50
|
+
label: "Blog",
|
|
51
|
+
icon: PenLine,
|
|
52
|
+
external: true,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
href: "https://github.com/AutumnsGrove/GroveEngine",
|
|
56
|
+
label: "GitHub",
|
|
57
|
+
icon: Github,
|
|
58
|
+
external: true,
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
// Default legal links (for footer)
|
|
62
|
+
export const DEFAULT_LEGAL_LINKS = [
|
|
63
|
+
{ href: "/legal/privacy", label: "Privacy" },
|
|
64
|
+
{ href: "/legal/terms", label: "Terms" },
|
|
65
|
+
];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome Components Index
|
|
3
|
+
* Re-exports all shared chrome/navigation components from the engine package
|
|
4
|
+
*/
|
|
5
|
+
export { default as ThemeToggle } from "./ThemeToggle.svelte";
|
|
6
|
+
export { default as MobileMenu } from "./MobileMenu.svelte";
|
|
7
|
+
export { default as Header } from "./Header.svelte";
|
|
8
|
+
export { default as HeaderMinimal } from "./HeaderMinimal.svelte";
|
|
9
|
+
export { default as Footer } from "./Footer.svelte";
|
|
10
|
+
export { default as FooterMinimal } from "./FooterMinimal.svelte";
|
|
11
|
+
export * from "./types";
|
|
12
|
+
export * from "./defaults";
|
|
13
|
+
export { seasonStore, themeStore } from "../../stores";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome Components Index
|
|
3
|
+
* Re-exports all shared chrome/navigation components from the engine package
|
|
4
|
+
*/
|
|
5
|
+
export { default as ThemeToggle } from "./ThemeToggle.svelte";
|
|
6
|
+
export { default as MobileMenu } from "./MobileMenu.svelte";
|
|
7
|
+
export { default as Header } from "./Header.svelte";
|
|
8
|
+
export { default as HeaderMinimal } from "./HeaderMinimal.svelte";
|
|
9
|
+
export { default as Footer } from "./Footer.svelte";
|
|
10
|
+
export { default as FooterMinimal } from "./FooterMinimal.svelte";
|
|
11
|
+
export * from "./types";
|
|
12
|
+
export * from "./defaults";
|
|
13
|
+
// Re-export stores for convenient access
|
|
14
|
+
export { seasonStore, themeStore } from "../../stores";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
export type { Season } from '../nature/palette';
|
|
3
|
+
export interface NavItem {
|
|
4
|
+
href: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: Component;
|
|
7
|
+
external?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface FooterLink {
|
|
10
|
+
href: string;
|
|
11
|
+
label: string;
|
|
12
|
+
icon?: Component;
|
|
13
|
+
external?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export type MaxWidth = "narrow" | "default" | "wide";
|
|
16
|
+
/**
|
|
17
|
+
* Check if a navigation item is active based on the current path
|
|
18
|
+
*/
|
|
19
|
+
export declare function isActivePath(href: string, currentPath: string): boolean;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import { cn } from "../../utils";
|
|
4
|
-
|
|
4
|
+
// Use centralized icon registry instead of direct lucide-svelte imports
|
|
5
|
+
import { MapPin, ArrowRight } from "../icons";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* RoadmapPreview - A glass card showing current development phase
|