@autumnsgrove/groveengine 0.7.0 → 0.8.5
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/OnboardingChecklist.svelte +2 -2
- package/dist/components/WispButton.svelte +83 -0
- package/dist/components/WispButton.svelte.d.ts +49 -0
- package/dist/components/WispPanel.svelte +1093 -0
- package/dist/components/WispPanel.svelte.d.ts +49 -0
- package/dist/components/custom/TableOfContents.svelte +12 -1
- package/dist/components/quota/UpgradePrompt.svelte +1 -0
- package/dist/config/wisp.d.ts +145 -0
- package/dist/config/wisp.js +175 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/server/inference-client.d.ts +139 -0
- package/dist/server/inference-client.js +294 -0
- package/dist/ui/components/content/RoadmapPreview.svelte +91 -0
- package/dist/ui/components/content/RoadmapPreview.svelte.d.ts +36 -0
- package/dist/ui/components/content/index.d.ts +1 -0
- package/dist/ui/components/content/index.js +1 -0
- package/dist/ui/components/nature/Logo.svelte +260 -0
- package/dist/ui/components/nature/Logo.svelte.d.ts +14 -0
- package/dist/ui/components/nature/botanical/Acorn.svelte +48 -0
- package/dist/ui/components/nature/botanical/Acorn.svelte.d.ts +8 -0
- package/dist/ui/components/nature/botanical/Berry.svelte +67 -0
- package/dist/ui/components/nature/botanical/Berry.svelte.d.ts +8 -0
- package/dist/ui/components/nature/botanical/DandelionPuff.svelte +98 -0
- package/dist/ui/components/nature/botanical/DandelionPuff.svelte.d.ts +8 -0
- package/dist/ui/components/nature/botanical/FallingLeavesLayer.svelte +170 -0
- package/dist/ui/components/nature/botanical/FallingLeavesLayer.svelte.d.ts +35 -0
- package/dist/ui/components/nature/botanical/FallingPetalsLayer.svelte +174 -0
- package/dist/ui/components/nature/botanical/FallingPetalsLayer.svelte.d.ts +25 -0
- package/dist/ui/components/nature/botanical/Leaf.svelte +77 -0
- package/dist/ui/components/nature/botanical/Leaf.svelte.d.ts +10 -0
- package/dist/ui/components/nature/botanical/LeafFalling.svelte +186 -0
- package/dist/ui/components/nature/botanical/LeafFalling.svelte.d.ts +22 -0
- package/dist/ui/components/nature/botanical/PetalFalling.svelte +266 -0
- package/dist/ui/components/nature/botanical/PetalFalling.svelte.d.ts +25 -0
- package/dist/ui/components/nature/botanical/PineCone.svelte +61 -0
- package/dist/ui/components/nature/botanical/PineCone.svelte.d.ts +7 -0
- package/dist/ui/components/nature/botanical/Vine.svelte +102 -0
- package/dist/ui/components/nature/botanical/Vine.svelte.d.ts +11 -0
- package/dist/ui/components/nature/botanical/index.d.ts +10 -0
- package/dist/ui/components/nature/botanical/index.js +11 -0
- package/dist/ui/components/nature/creatures/Bee.svelte +78 -0
- package/dist/ui/components/nature/creatures/Bee.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Bird.svelte +94 -0
- package/dist/ui/components/nature/creatures/Bird.svelte.d.ts +11 -0
- package/dist/ui/components/nature/creatures/BirdFlying.svelte +83 -0
- package/dist/ui/components/nature/creatures/BirdFlying.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Bluebird.svelte +95 -0
- package/dist/ui/components/nature/creatures/Bluebird.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Butterfly.svelte +87 -0
- package/dist/ui/components/nature/creatures/Butterfly.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Cardinal.svelte +95 -0
- package/dist/ui/components/nature/creatures/Cardinal.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Chickadee.svelte +97 -0
- package/dist/ui/components/nature/creatures/Chickadee.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Deer.svelte +95 -0
- package/dist/ui/components/nature/creatures/Deer.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Firefly.svelte +111 -0
- package/dist/ui/components/nature/creatures/Firefly.svelte.d.ts +10 -0
- package/dist/ui/components/nature/creatures/Owl.svelte +91 -0
- package/dist/ui/components/nature/creatures/Owl.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Rabbit.svelte +90 -0
- package/dist/ui/components/nature/creatures/Rabbit.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/Robin.svelte +98 -0
- package/dist/ui/components/nature/creatures/Robin.svelte.d.ts +12 -0
- package/dist/ui/components/nature/creatures/Squirrel.svelte +97 -0
- package/dist/ui/components/nature/creatures/Squirrel.svelte.d.ts +9 -0
- package/dist/ui/components/nature/creatures/index.d.ts +13 -0
- package/dist/ui/components/nature/creatures/index.js +14 -0
- package/dist/ui/components/nature/ground/Bush.svelte +57 -0
- package/dist/ui/components/nature/ground/Bush.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/Crocus.svelte +83 -0
- package/dist/ui/components/nature/ground/Crocus.svelte.d.ts +12 -0
- package/dist/ui/components/nature/ground/Daffodil.svelte +75 -0
- package/dist/ui/components/nature/ground/Daffodil.svelte.d.ts +11 -0
- package/dist/ui/components/nature/ground/Fern.svelte +72 -0
- package/dist/ui/components/nature/ground/Fern.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/FlowerWild.svelte +60 -0
- package/dist/ui/components/nature/ground/FlowerWild.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/GrassTuft.svelte +49 -0
- package/dist/ui/components/nature/ground/GrassTuft.svelte.d.ts +10 -0
- package/dist/ui/components/nature/ground/Log.svelte +42 -0
- package/dist/ui/components/nature/ground/Log.svelte.d.ts +7 -0
- package/dist/ui/components/nature/ground/Mushroom.svelte +48 -0
- package/dist/ui/components/nature/ground/Mushroom.svelte.d.ts +9 -0
- package/dist/ui/components/nature/ground/MushroomCluster.svelte +41 -0
- package/dist/ui/components/nature/ground/MushroomCluster.svelte.d.ts +8 -0
- package/dist/ui/components/nature/ground/Rock.svelte +59 -0
- package/dist/ui/components/nature/ground/Rock.svelte.d.ts +8 -0
- package/dist/ui/components/nature/ground/Stump.svelte +44 -0
- package/dist/ui/components/nature/ground/Stump.svelte.d.ts +8 -0
- package/dist/ui/components/nature/ground/Tulip.svelte +79 -0
- package/dist/ui/components/nature/ground/Tulip.svelte.d.ts +11 -0
- package/dist/ui/components/nature/ground/index.d.ts +12 -0
- package/dist/ui/components/nature/ground/index.js +13 -0
- package/dist/ui/components/nature/index.d.ts +28 -0
- package/dist/ui/components/nature/index.js +38 -0
- package/dist/ui/components/nature/palette.d.ts +602 -0
- package/dist/ui/components/nature/palette.js +472 -0
- package/dist/ui/components/nature/sky/Cloud.svelte +122 -0
- package/dist/ui/components/nature/sky/Cloud.svelte.d.ts +11 -0
- package/dist/ui/components/nature/sky/CloudWispy.svelte +79 -0
- package/dist/ui/components/nature/sky/CloudWispy.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/Moon.svelte +60 -0
- package/dist/ui/components/nature/sky/Moon.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/Rainbow.svelte +101 -0
- package/dist/ui/components/nature/sky/Rainbow.svelte.d.ts +8 -0
- package/dist/ui/components/nature/sky/Star.svelte +84 -0
- package/dist/ui/components/nature/sky/Star.svelte.d.ts +10 -0
- package/dist/ui/components/nature/sky/StarCluster.svelte +85 -0
- package/dist/ui/components/nature/sky/StarCluster.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/StarShooting.svelte +90 -0
- package/dist/ui/components/nature/sky/StarShooting.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/Sun.svelte +70 -0
- package/dist/ui/components/nature/sky/Sun.svelte.d.ts +9 -0
- package/dist/ui/components/nature/sky/index.d.ts +8 -0
- package/dist/ui/components/nature/sky/index.js +9 -0
- package/dist/ui/components/nature/structural/Birdhouse.svelte +53 -0
- package/dist/ui/components/nature/structural/Birdhouse.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/Bridge.svelte +65 -0
- package/dist/ui/components/nature/structural/Bridge.svelte.d.ts +7 -0
- package/dist/ui/components/nature/structural/FencePost.svelte +54 -0
- package/dist/ui/components/nature/structural/FencePost.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/GardenGate.svelte +70 -0
- package/dist/ui/components/nature/structural/GardenGate.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/Lantern.svelte +113 -0
- package/dist/ui/components/nature/structural/Lantern.svelte.d.ts +10 -0
- package/dist/ui/components/nature/structural/Lattice.svelte +89 -0
- package/dist/ui/components/nature/structural/Lattice.svelte.d.ts +8 -0
- package/dist/ui/components/nature/structural/LatticeWithVine.svelte +89 -0
- package/dist/ui/components/nature/structural/LatticeWithVine.svelte.d.ts +11 -0
- package/dist/ui/components/nature/structural/StonePath.svelte +48 -0
- package/dist/ui/components/nature/structural/StonePath.svelte.d.ts +7 -0
- package/dist/ui/components/nature/structural/index.d.ts +8 -0
- package/dist/ui/components/nature/structural/index.js +9 -0
- package/dist/ui/components/nature/trees/TreeAspen.svelte +163 -0
- package/dist/ui/components/nature/trees/TreeAspen.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/TreeBirch.svelte +186 -0
- package/dist/ui/components/nature/trees/TreeBirch.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/TreeCherry.svelte +108 -0
- package/dist/ui/components/nature/trees/TreeCherry.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/TreePine.svelte +79 -0
- package/dist/ui/components/nature/trees/TreePine.svelte.d.ts +11 -0
- package/dist/ui/components/nature/trees/index.d.ts +4 -0
- package/dist/ui/components/nature/trees/index.js +5 -0
- package/dist/ui/components/nature/water/LilyPad.svelte +99 -0
- package/dist/ui/components/nature/water/LilyPad.svelte.d.ts +10 -0
- package/dist/ui/components/nature/water/Pond.svelte +104 -0
- package/dist/ui/components/nature/water/Pond.svelte.d.ts +8 -0
- package/dist/ui/components/nature/water/Reeds.svelte +85 -0
- package/dist/ui/components/nature/water/Reeds.svelte.d.ts +11 -0
- package/dist/ui/components/nature/water/Stream.svelte +98 -0
- package/dist/ui/components/nature/water/Stream.svelte.d.ts +8 -0
- package/dist/ui/components/nature/water/index.d.ts +4 -0
- package/dist/ui/components/nature/water/index.js +5 -0
- package/dist/ui/components/nature/weather/SnowfallLayer.svelte +175 -0
- package/dist/ui/components/nature/weather/SnowfallLayer.svelte.d.ts +25 -0
- package/dist/ui/components/nature/weather/Snowflake.svelte +99 -0
- package/dist/ui/components/nature/weather/Snowflake.svelte.d.ts +11 -0
- package/dist/ui/components/nature/weather/SnowflakeFalling.svelte +162 -0
- package/dist/ui/components/nature/weather/SnowflakeFalling.svelte.d.ts +23 -0
- package/dist/ui/components/nature/weather/index.d.ts +3 -0
- package/dist/ui/components/nature/weather/index.js +4 -0
- package/dist/ui/components/primitives/textarea/textarea.svelte +1 -1
- package/dist/ui/components/typography/Alagard.svelte +17 -0
- package/dist/ui/components/typography/Alagard.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Atkinson.svelte +17 -0
- package/dist/ui/components/typography/Atkinson.svelte.d.ts +10 -0
- package/dist/ui/components/typography/BodoniModa.svelte +17 -0
- package/dist/ui/components/typography/BodoniModa.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Calistoga.svelte +17 -0
- package/dist/ui/components/typography/Calistoga.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Caveat.svelte +17 -0
- package/dist/ui/components/typography/Caveat.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Cormorant.svelte +17 -0
- package/dist/ui/components/typography/Cormorant.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Cozette.svelte +17 -0
- package/dist/ui/components/typography/Cozette.svelte.d.ts +10 -0
- package/dist/ui/components/typography/EBGaramond.svelte +17 -0
- package/dist/ui/components/typography/EBGaramond.svelte.d.ts +10 -0
- package/dist/ui/components/typography/FontProvider.svelte +98 -0
- package/dist/ui/components/typography/FontProvider.svelte.d.ts +17 -0
- package/dist/ui/components/typography/Fraunces.svelte +17 -0
- package/dist/ui/components/typography/Fraunces.svelte.d.ts +10 -0
- package/dist/ui/components/typography/IBMPlexMono.svelte +17 -0
- package/dist/ui/components/typography/IBMPlexMono.svelte.d.ts +10 -0
- package/dist/ui/components/typography/InstrumentSans.svelte +17 -0
- package/dist/ui/components/typography/InstrumentSans.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Lexend.svelte +17 -0
- package/dist/ui/components/typography/Lexend.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Lora.svelte +17 -0
- package/dist/ui/components/typography/Lora.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Luciole.svelte +17 -0
- package/dist/ui/components/typography/Luciole.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Manrope.svelte +17 -0
- package/dist/ui/components/typography/Manrope.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Merriweather.svelte +17 -0
- package/dist/ui/components/typography/Merriweather.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Nunito.svelte +17 -0
- package/dist/ui/components/typography/Nunito.svelte.d.ts +10 -0
- package/dist/ui/components/typography/OpenDyslexic.svelte +17 -0
- package/dist/ui/components/typography/OpenDyslexic.svelte.d.ts +10 -0
- package/dist/ui/components/typography/PlusJakartaSans.svelte +17 -0
- package/dist/ui/components/typography/PlusJakartaSans.svelte.d.ts +10 -0
- package/dist/ui/components/typography/Quicksand.svelte +17 -0
- package/dist/ui/components/typography/Quicksand.svelte.d.ts +10 -0
- package/dist/ui/components/typography/README.md +153 -0
- package/dist/ui/components/typography/index.d.ts +23 -0
- package/dist/ui/components/typography/index.js +42 -0
- package/dist/ui/components/ui/GlassCarousel.svelte +446 -0
- package/dist/ui/components/ui/GlassCarousel.svelte.d.ts +57 -0
- package/dist/ui/components/ui/GlassConfirmDialog.svelte +2 -1
- package/dist/ui/components/ui/GlassLogo.svelte +423 -0
- package/dist/ui/components/ui/GlassLogo.svelte.d.ts +23 -0
- package/dist/ui/components/ui/GlassNavbar.svelte +120 -0
- package/dist/ui/components/ui/GlassNavbar.svelte.d.ts +42 -0
- package/dist/ui/components/ui/GlassOverlay.svelte +1 -1
- package/dist/ui/components/ui/Logo.svelte +47 -52
- package/dist/ui/components/ui/Logo.svelte.d.ts +4 -3
- package/dist/ui/components/ui/index.d.ts +3 -0
- package/dist/ui/components/ui/index.js +3 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +2 -0
- package/dist/ui/styles/grove.css +15 -1
- package/dist/ui/vineyard/index.d.ts +9 -0
- package/dist/ui/vineyard/index.js +8 -0
- package/dist/utils/csrf.js +5 -2
- package/dist/utils/readability.d.ts +89 -0
- package/dist/utils/readability.js +204 -0
- package/package.json +27 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Grove — A place to Be
|
|
3
|
+
Copyright (c) 2025 Autumn Brown
|
|
4
|
+
Licensed under AGPL-3.0
|
|
5
|
+
-->
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import type { Season } from '../palette';
|
|
8
|
+
import { greens, autumn } from '../palette';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
class?: string;
|
|
12
|
+
color?: string;
|
|
13
|
+
season?: Season;
|
|
14
|
+
variant?: 'oak' | 'maple' | 'simple' | 'aspen';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
class: className = 'w-4 h-4',
|
|
19
|
+
color,
|
|
20
|
+
season = 'summer',
|
|
21
|
+
variant = 'simple'
|
|
22
|
+
}: Props = $props();
|
|
23
|
+
|
|
24
|
+
// Leaves change color in autumn
|
|
25
|
+
const defaultColor = $derived(season === 'autumn' ? autumn.amber : greens.grove);
|
|
26
|
+
const leafColor = $derived(color ?? defaultColor);
|
|
27
|
+
const veinColor = $derived(season === 'autumn' ? autumn.rust : greens.deepGreen);
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<!-- Leaf - various shapes -->
|
|
31
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 50">
|
|
32
|
+
{#if variant === 'simple'}
|
|
33
|
+
<!-- Simple oval leaf -->
|
|
34
|
+
<ellipse fill={leafColor} cx="20" cy="22" rx="15" ry="20" />
|
|
35
|
+
<path fill="none" stroke={veinColor} stroke-width="1" d="M20 5 L20 42" opacity="0.4" />
|
|
36
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 15 Q10 18 8 22" opacity="0.3" />
|
|
37
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 15 Q30 18 32 22" opacity="0.3" />
|
|
38
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 25 Q12 28 8 32" opacity="0.3" />
|
|
39
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 25 Q28 28 32 32" opacity="0.3" />
|
|
40
|
+
<!-- Stem -->
|
|
41
|
+
<path fill="none" stroke={veinColor} stroke-width="2" d="M20 42 L20 50" />
|
|
42
|
+
{:else if variant === 'maple'}
|
|
43
|
+
<!-- Maple leaf (iconic shape) -->
|
|
44
|
+
<path
|
|
45
|
+
fill={leafColor}
|
|
46
|
+
d="M20 2
|
|
47
|
+
L22 10 L30 5 L26 14 L38 12 L28 20 L38 25 L26 26 L32 38 L20 30 L8 38 L14 26 L2 25 L12 20 L2 12 L14 14 L10 5 L18 10 Z"
|
|
48
|
+
/>
|
|
49
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 10 L20 30" opacity="0.4" />
|
|
50
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 18 L10 12" opacity="0.3" />
|
|
51
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 18 L30 12" opacity="0.3" />
|
|
52
|
+
<!-- Stem -->
|
|
53
|
+
<path fill="none" stroke={veinColor} stroke-width="2" d="M20 30 L20 50" />
|
|
54
|
+
{:else if variant === 'oak'}
|
|
55
|
+
<!-- Oak leaf (lobed) -->
|
|
56
|
+
<path
|
|
57
|
+
fill={leafColor}
|
|
58
|
+
d="M20 3
|
|
59
|
+
Q25 5 28 10 Q32 8 34 12 Q30 14 32 18 Q36 18 36 23 Q32 24 33 28 Q36 30 34 35 Q30 34 28 38 Q25 42 20 42
|
|
60
|
+
Q15 42 12 38 Q10 34 6 35 Q4 30 7 28 Q8 24 4 23 Q4 18 8 18 Q10 14 6 12 Q8 8 12 10 Q15 5 20 3"
|
|
61
|
+
/>
|
|
62
|
+
<path fill="none" stroke={veinColor} stroke-width="1" d="M20 5 L20 42" opacity="0.4" />
|
|
63
|
+
<!-- Stem -->
|
|
64
|
+
<path fill="none" stroke={veinColor} stroke-width="2" d="M20 42 L20 50" />
|
|
65
|
+
{:else if variant === 'aspen'}
|
|
66
|
+
<!-- Aspen leaf (round with pointed tip) -->
|
|
67
|
+
<path
|
|
68
|
+
fill={leafColor}
|
|
69
|
+
d="M20 3 Q35 15 35 28 Q35 42 20 42 Q5 42 5 28 Q5 15 20 3"
|
|
70
|
+
/>
|
|
71
|
+
<path fill="none" stroke={veinColor} stroke-width="0.8" d="M20 5 L20 42" opacity="0.4" />
|
|
72
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 15 Q12 20 8 28" opacity="0.3" />
|
|
73
|
+
<path fill="none" stroke={veinColor} stroke-width="0.5" d="M20 15 Q28 20 32 28" opacity="0.3" />
|
|
74
|
+
<!-- Stem -->
|
|
75
|
+
<path fill="none" stroke={veinColor} stroke-width="2" d="M20 42 L20 50" />
|
|
76
|
+
{/if}
|
|
77
|
+
</svg>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Season } from '../palette';
|
|
2
|
+
interface Props {
|
|
3
|
+
class?: string;
|
|
4
|
+
color?: string;
|
|
5
|
+
season?: Season;
|
|
6
|
+
variant?: 'oak' | 'maple' | 'simple' | 'aspen';
|
|
7
|
+
}
|
|
8
|
+
declare const Leaf: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type Leaf = ReturnType<typeof Leaf>;
|
|
10
|
+
export default Leaf;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Grove — A place to Be
|
|
3
|
+
Copyright (c) 2025 Autumn Brown
|
|
4
|
+
Licensed under AGPL-3.0
|
|
5
|
+
-->
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import type { Season } from '../palette';
|
|
8
|
+
import { autumn, greens, cherryBlossoms, autumnReds } from '../palette';
|
|
9
|
+
|
|
10
|
+
type LeafVariant = 'simple' | 'maple' | 'cherry' | 'aspen' | 'pine';
|
|
11
|
+
|
|
12
|
+
// Animation constants
|
|
13
|
+
// Starting offset ensures leaves begin above viewport for natural entrance
|
|
14
|
+
const SPAWN_OFFSET_PX = 50;
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
class?: string;
|
|
18
|
+
color?: string;
|
|
19
|
+
season?: Season;
|
|
20
|
+
animate?: boolean;
|
|
21
|
+
variant?: LeafVariant;
|
|
22
|
+
/** Animation duration in seconds */
|
|
23
|
+
duration?: number;
|
|
24
|
+
/** Animation delay in seconds */
|
|
25
|
+
delay?: number;
|
|
26
|
+
/** Horizontal drift amount (positive = right, negative = left) */
|
|
27
|
+
drift?: number;
|
|
28
|
+
/** Fall distance in vh units (how far the leaf travels) */
|
|
29
|
+
fallDistance?: number;
|
|
30
|
+
/** Seed for deterministic color selection */
|
|
31
|
+
seed?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
class: className = 'w-4 h-4',
|
|
36
|
+
color,
|
|
37
|
+
season = 'autumn',
|
|
38
|
+
animate = true,
|
|
39
|
+
variant = 'simple',
|
|
40
|
+
duration = 5,
|
|
41
|
+
delay = 0,
|
|
42
|
+
drift = 30,
|
|
43
|
+
fallDistance = 40,
|
|
44
|
+
seed = 0
|
|
45
|
+
}: Props = $props();
|
|
46
|
+
|
|
47
|
+
// Color palettes for different leaf types
|
|
48
|
+
const autumnColors = [autumn.rust, autumn.amber, autumn.gold, autumn.pumpkin, autumn.ember];
|
|
49
|
+
const summerColors = [greens.grove, greens.meadow, greens.spring, greens.deepGreen];
|
|
50
|
+
const cherryAutumnColors = [autumnReds.crimson, autumnReds.scarlet, autumnReds.rose];
|
|
51
|
+
const cherrySpringColors = [cherryBlossoms.standard, cherryBlossoms.light, cherryBlossoms.pale, cherryBlossoms.falling];
|
|
52
|
+
const aspenAutumnColors = [autumn.gold, autumn.honey, autumn.straw, autumn.amber];
|
|
53
|
+
|
|
54
|
+
// Deterministic color selection using pseudo-random distribution
|
|
55
|
+
// Uses sine-based hash to avoid visible patterns from sequential IDs
|
|
56
|
+
function pickFromArray<T>(arr: T[]): T {
|
|
57
|
+
const hash = Math.abs(Math.sin(seed * 12.9898) * 43758.5453);
|
|
58
|
+
return arr[Math.floor(hash) % arr.length];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Get default color based on variant and season (deterministic)
|
|
62
|
+
function getDefaultColor(): string {
|
|
63
|
+
if (variant === 'cherry') {
|
|
64
|
+
// Cherry trees: pink blossoms in spring, green in summer, red/orange in autumn
|
|
65
|
+
if (season === 'spring') {
|
|
66
|
+
return pickFromArray(cherrySpringColors);
|
|
67
|
+
} else if (season === 'autumn') {
|
|
68
|
+
return pickFromArray(cherryAutumnColors);
|
|
69
|
+
} else {
|
|
70
|
+
// Summer - cherry trees have regular green leaves
|
|
71
|
+
return pickFromArray(summerColors);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (variant === 'aspen') {
|
|
75
|
+
const colors = season === 'autumn' ? aspenAutumnColors : summerColors;
|
|
76
|
+
return pickFromArray(colors);
|
|
77
|
+
}
|
|
78
|
+
if (variant === 'pine') {
|
|
79
|
+
// Pine stays green year-round (evergreen)
|
|
80
|
+
return pickFromArray(summerColors);
|
|
81
|
+
}
|
|
82
|
+
// Default (simple, maple)
|
|
83
|
+
const colors = season === 'autumn' ? autumnColors : summerColors;
|
|
84
|
+
return pickFromArray(colors);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const leafColor = $derived(color ?? getDefaultColor());
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<!-- Falling leaf with spin/flutter animation -->
|
|
91
|
+
<svg
|
|
92
|
+
class="{className} {animate ? 'fall' : ''}"
|
|
93
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
94
|
+
viewBox="0 0 30 35"
|
|
95
|
+
style="--fall-duration: {duration}s; --fall-delay: {delay}s; --fall-drift: {drift}px; --fall-distance: {fallDistance}vh; --spawn-offset: {SPAWN_OFFSET_PX}px;"
|
|
96
|
+
>
|
|
97
|
+
<g class={animate ? 'spin' : ''}>
|
|
98
|
+
{#if variant === 'simple'}
|
|
99
|
+
<!-- Simple oval leaf -->
|
|
100
|
+
<ellipse fill={leafColor} cx="15" cy="15" rx="12" ry="14" />
|
|
101
|
+
<path fill="none" stroke="rgba(0,0,0,0.2)" stroke-width="0.8" d="M15 3 L15 29" />
|
|
102
|
+
{:else if variant === 'maple'}
|
|
103
|
+
<!-- Maple shape -->
|
|
104
|
+
<path
|
|
105
|
+
fill={leafColor}
|
|
106
|
+
d="M15 2
|
|
107
|
+
L16 7 L22 4 L19 10 L28 9 L21 15 L28 18 L19 19 L24 28 L15 22 L6 28 L11 19 L2 18 L9 15 L2 9 L11 10 L8 4 L14 7 Z"
|
|
108
|
+
/>
|
|
109
|
+
{:else if variant === 'cherry'}
|
|
110
|
+
<!-- Cherry blossom petal - soft rounded oval -->
|
|
111
|
+
<ellipse fill={leafColor} cx="15" cy="13" rx="10" ry="12" />
|
|
112
|
+
<!-- Subtle petal notch at tip -->
|
|
113
|
+
<path fill={leafColor} d="M15 2 Q12 6 15 8 Q18 6 15 2" />
|
|
114
|
+
{:else if variant === 'aspen'}
|
|
115
|
+
<!-- Aspen/Birch - rounded heart shape -->
|
|
116
|
+
<path
|
|
117
|
+
fill={leafColor}
|
|
118
|
+
d="M15 4 Q8 4 6 12 Q4 20 15 28 Q26 20 24 12 Q22 4 15 4"
|
|
119
|
+
/>
|
|
120
|
+
<path fill="none" stroke="rgba(0,0,0,0.15)" stroke-width="0.6" d="M15 6 L15 26" />
|
|
121
|
+
{:else if variant === 'pine'}
|
|
122
|
+
<!-- Pine needle cluster - thin elongated shapes -->
|
|
123
|
+
<path fill={leafColor} d="M15 2 L16 28 L14 28 Z" />
|
|
124
|
+
<path fill={leafColor} d="M10 4 L16 26 L14 27 Z" opacity="0.8" />
|
|
125
|
+
<path fill={leafColor} d="M20 4 L14 26 L16 27 Z" opacity="0.8" />
|
|
126
|
+
{/if}
|
|
127
|
+
|
|
128
|
+
<!-- Stem (not for pine needles) -->
|
|
129
|
+
{#if variant !== 'pine'}
|
|
130
|
+
<path fill="none" stroke="rgba(0,0,0,0.3)" stroke-width="1.5" d="M15 28 L15 35" />
|
|
131
|
+
{/if}
|
|
132
|
+
</g>
|
|
133
|
+
</svg>
|
|
134
|
+
|
|
135
|
+
<style>
|
|
136
|
+
@keyframes fall {
|
|
137
|
+
0% {
|
|
138
|
+
/* Start transparent to avoid "stuck in place" look for leaves with no delay */
|
|
139
|
+
transform: translateY(0) translateX(0);
|
|
140
|
+
opacity: 0;
|
|
141
|
+
}
|
|
142
|
+
5% {
|
|
143
|
+
/* Fade in quickly as falling begins */
|
|
144
|
+
opacity: 0.5;
|
|
145
|
+
}
|
|
146
|
+
10% {
|
|
147
|
+
opacity: 0.85;
|
|
148
|
+
}
|
|
149
|
+
70% {
|
|
150
|
+
opacity: 0.7;
|
|
151
|
+
}
|
|
152
|
+
90% {
|
|
153
|
+
opacity: 0.3;
|
|
154
|
+
}
|
|
155
|
+
100% {
|
|
156
|
+
transform: translateY(var(--fall-distance, 40vh)) translateX(var(--fall-drift, 30px));
|
|
157
|
+
opacity: 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@keyframes spin {
|
|
162
|
+
0% { transform: rotate(0deg) rotateY(0deg); }
|
|
163
|
+
25% { transform: rotate(20deg) rotateY(90deg); }
|
|
164
|
+
50% { transform: rotate(-15deg) rotateY(180deg); }
|
|
165
|
+
75% { transform: rotate(25deg) rotateY(270deg); }
|
|
166
|
+
100% { transform: rotate(0deg) rotateY(360deg); }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.fall {
|
|
170
|
+
animation: fall var(--fall-duration, 5s) ease-in-out infinite;
|
|
171
|
+
animation-delay: var(--fall-delay, 0s);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.spin {
|
|
175
|
+
transform-origin: center center;
|
|
176
|
+
animation: spin calc(var(--fall-duration, 5s) * 0.6) ease-in-out infinite;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Respect user's motion preferences */
|
|
180
|
+
@media (prefers-reduced-motion: reduce) {
|
|
181
|
+
.fall,
|
|
182
|
+
.spin {
|
|
183
|
+
animation: none;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Season } from '../palette';
|
|
2
|
+
type LeafVariant = 'simple' | 'maple' | 'cherry' | 'aspen' | 'pine';
|
|
3
|
+
interface Props {
|
|
4
|
+
class?: string;
|
|
5
|
+
color?: string;
|
|
6
|
+
season?: Season;
|
|
7
|
+
animate?: boolean;
|
|
8
|
+
variant?: LeafVariant;
|
|
9
|
+
/** Animation duration in seconds */
|
|
10
|
+
duration?: number;
|
|
11
|
+
/** Animation delay in seconds */
|
|
12
|
+
delay?: number;
|
|
13
|
+
/** Horizontal drift amount (positive = right, negative = left) */
|
|
14
|
+
drift?: number;
|
|
15
|
+
/** Fall distance in vh units (how far the leaf travels) */
|
|
16
|
+
fallDistance?: number;
|
|
17
|
+
/** Seed for deterministic color selection */
|
|
18
|
+
seed?: number;
|
|
19
|
+
}
|
|
20
|
+
declare const LeafFalling: import("svelte").Component<Props, {}, "">;
|
|
21
|
+
type LeafFalling = ReturnType<typeof LeafFalling>;
|
|
22
|
+
export default LeafFalling;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Grove — A place to Be
|
|
3
|
+
Copyright (c) 2025 Autumn Brown
|
|
4
|
+
Licensed under AGPL-3.0
|
|
5
|
+
-->
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import { cherryBlossomsPeak } from '../palette';
|
|
8
|
+
import { browser } from '$app/environment';
|
|
9
|
+
|
|
10
|
+
type PetalVariant = 'round' | 'pointed' | 'heart' | 'curled' | 'tiny';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
class?: string;
|
|
14
|
+
/** Petal shape variant */
|
|
15
|
+
variant?: PetalVariant;
|
|
16
|
+
/** Override petal color (defaults to spring blossom pink) */
|
|
17
|
+
color?: string;
|
|
18
|
+
/** Fall animation duration in seconds */
|
|
19
|
+
duration?: number;
|
|
20
|
+
/** Animation delay in seconds */
|
|
21
|
+
delay?: number;
|
|
22
|
+
/** Horizontal drift amount in pixels (can be negative) */
|
|
23
|
+
drift?: number;
|
|
24
|
+
/** Fall distance in vh units */
|
|
25
|
+
fallDistance?: number;
|
|
26
|
+
/** Enable falling animation */
|
|
27
|
+
animate?: boolean;
|
|
28
|
+
/** Opacity for depth effect */
|
|
29
|
+
opacity?: number;
|
|
30
|
+
/** Seed for deterministic rotation patterns */
|
|
31
|
+
seed?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
class: className = 'w-3 h-3',
|
|
36
|
+
variant = 'round',
|
|
37
|
+
color,
|
|
38
|
+
duration = 12,
|
|
39
|
+
delay = 0,
|
|
40
|
+
drift = 0,
|
|
41
|
+
fallDistance = 100,
|
|
42
|
+
animate = true,
|
|
43
|
+
opacity = 0.85,
|
|
44
|
+
seed = 0
|
|
45
|
+
}: Props = $props();
|
|
46
|
+
|
|
47
|
+
// Use spring blossom colors with slight variation based on variant
|
|
48
|
+
const petalColor = $derived(color ?? (
|
|
49
|
+
variant === 'tiny' ? cherryBlossomsPeak.falling :
|
|
50
|
+
variant === 'curled' ? cherryBlossomsPeak.light :
|
|
51
|
+
cherryBlossomsPeak.pale
|
|
52
|
+
));
|
|
53
|
+
|
|
54
|
+
// Secondary color for gradient effect
|
|
55
|
+
const highlightColor = $derived(cherryBlossomsPeak.falling);
|
|
56
|
+
|
|
57
|
+
// Deterministic rotation based on seed - more dramatic for dancing effect
|
|
58
|
+
const initialRotation = $derived((seed * 37) % 360);
|
|
59
|
+
const rotationDirection = $derived(seed % 2 === 0 ? 1 : -1);
|
|
60
|
+
const rotationAmount = $derived(360 + (seed % 360)); // 360-720 degrees total rotation - more twirling!
|
|
61
|
+
|
|
62
|
+
// Dancing sway parameters - create organic horizontal movement
|
|
63
|
+
// Dramatically increased for true gliding, dancing motion!
|
|
64
|
+
const swayAmplitude = $derived(80 + (seed % 100)); // 80-180px horizontal sway - GLIDE!
|
|
65
|
+
const swayFrequency = $derived(2 + (seed % 4)); // 2-5 complete waves during fall
|
|
66
|
+
|
|
67
|
+
// Unique animation name to prevent conflicts
|
|
68
|
+
const animId = $derived(`petal-${seed}`);
|
|
69
|
+
|
|
70
|
+
// Inject dynamic keyframes at runtime to avoid PostCSS parsing issues
|
|
71
|
+
$effect(() => {
|
|
72
|
+
if (!browser || !animate) return;
|
|
73
|
+
|
|
74
|
+
const styleId = `petal-style-${seed}`;
|
|
75
|
+
// Check if already injected
|
|
76
|
+
if (document.getElementById(styleId)) return;
|
|
77
|
+
|
|
78
|
+
const style = document.createElement('style');
|
|
79
|
+
style.id = styleId;
|
|
80
|
+
// Create dancing, swaying motion with sine wave horizontal movement
|
|
81
|
+
// Opacity fade-in for gentle appearance (0 → target opacity)
|
|
82
|
+
style.textContent = `
|
|
83
|
+
@keyframes ${animId}-fall {
|
|
84
|
+
0% {
|
|
85
|
+
transform: translateY(0) translateX(0) rotate(${initialRotation}deg);
|
|
86
|
+
opacity: 0;
|
|
87
|
+
}
|
|
88
|
+
8% {
|
|
89
|
+
opacity: ${opacity};
|
|
90
|
+
}
|
|
91
|
+
12.5% {
|
|
92
|
+
transform: translateY(${fallDistance * 0.125}vh) translateX(${Math.sin(Math.PI * 0.25 * swayFrequency) * swayAmplitude + drift * 0.125}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.125}deg);
|
|
93
|
+
}
|
|
94
|
+
25% {
|
|
95
|
+
transform: translateY(${fallDistance * 0.25}vh) translateX(${Math.sin(Math.PI * 0.5 * swayFrequency) * swayAmplitude + drift * 0.25}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.25}deg);
|
|
96
|
+
}
|
|
97
|
+
37.5% {
|
|
98
|
+
transform: translateY(${fallDistance * 0.375}vh) translateX(${Math.sin(Math.PI * 0.75 * swayFrequency) * swayAmplitude + drift * 0.375}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.375}deg);
|
|
99
|
+
}
|
|
100
|
+
50% {
|
|
101
|
+
transform: translateY(${fallDistance * 0.5}vh) translateX(${Math.sin(Math.PI * swayFrequency) * swayAmplitude + drift * 0.5}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.5}deg);
|
|
102
|
+
}
|
|
103
|
+
62.5% {
|
|
104
|
+
transform: translateY(${fallDistance * 0.625}vh) translateX(${Math.sin(Math.PI * 1.25 * swayFrequency) * swayAmplitude + drift * 0.625}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.625}deg);
|
|
105
|
+
}
|
|
106
|
+
75% {
|
|
107
|
+
transform: translateY(${fallDistance * 0.75}vh) translateX(${Math.sin(Math.PI * 1.5 * swayFrequency) * swayAmplitude + drift * 0.75}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.75}deg);
|
|
108
|
+
}
|
|
109
|
+
87.5% {
|
|
110
|
+
transform: translateY(${fallDistance * 0.875}vh) translateX(${Math.sin(Math.PI * 1.75 * swayFrequency) * swayAmplitude + drift * 0.875}px) rotate(${initialRotation + rotationAmount * rotationDirection * 0.875}deg);
|
|
111
|
+
}
|
|
112
|
+
100% {
|
|
113
|
+
transform: translateY(${fallDistance}vh) translateX(${Math.sin(Math.PI * 2 * swayFrequency) * swayAmplitude + drift}px) rotate(${initialRotation + rotationAmount * rotationDirection}deg);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
@keyframes ${animId}-flutter {
|
|
117
|
+
0%, 100% {
|
|
118
|
+
transform: rotateX(0deg) rotateY(0deg) scale(1);
|
|
119
|
+
}
|
|
120
|
+
16% {
|
|
121
|
+
transform: rotateX(30deg) rotateY(40deg) scale(0.9);
|
|
122
|
+
}
|
|
123
|
+
33% {
|
|
124
|
+
transform: rotateX(-25deg) rotateY(-35deg) scale(1.08);
|
|
125
|
+
}
|
|
126
|
+
50% {
|
|
127
|
+
transform: rotateX(35deg) rotateY(30deg) scale(0.95);
|
|
128
|
+
}
|
|
129
|
+
66% {
|
|
130
|
+
transform: rotateX(-30deg) rotateY(-40deg) scale(1.05);
|
|
131
|
+
}
|
|
132
|
+
83% {
|
|
133
|
+
transform: rotateX(25deg) rotateY(35deg) scale(0.92);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
document.head.appendChild(style);
|
|
138
|
+
|
|
139
|
+
return () => {
|
|
140
|
+
// Cleanup on component destroy
|
|
141
|
+
const el = document.getElementById(styleId);
|
|
142
|
+
if (el) el.remove();
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<!-- Cherry blossom petal - delicate and flutter-y -->
|
|
148
|
+
<!-- Wrapper div for fall animation (translateY/X), SVG for flutter (3D rotations) -->
|
|
149
|
+
<div
|
|
150
|
+
class="petal-wrapper"
|
|
151
|
+
style="{animate ? `animation: ${animId}-fall ${duration}s linear ${delay}s infinite;` : `opacity: ${opacity};`}"
|
|
152
|
+
>
|
|
153
|
+
<svg
|
|
154
|
+
class="{className}"
|
|
155
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
156
|
+
viewBox="0 0 20 20"
|
|
157
|
+
style="{animate ? `animation: ${animId}-flutter ${duration / 4}s ease-in-out ${delay}s infinite;` : ''}"
|
|
158
|
+
>
|
|
159
|
+
<defs>
|
|
160
|
+
<!-- Gradient for depth -->
|
|
161
|
+
<radialGradient id="petal-grad-{seed}" cx="30%" cy="30%" r="70%">
|
|
162
|
+
<stop offset="0%" stop-color={highlightColor} />
|
|
163
|
+
<stop offset="100%" stop-color={petalColor} />
|
|
164
|
+
</radialGradient>
|
|
165
|
+
</defs>
|
|
166
|
+
|
|
167
|
+
{#if variant === 'round'}
|
|
168
|
+
<!-- Classic round cherry blossom petal -->
|
|
169
|
+
<ellipse
|
|
170
|
+
cx="10"
|
|
171
|
+
cy="10"
|
|
172
|
+
rx="8"
|
|
173
|
+
ry="9"
|
|
174
|
+
fill="url(#petal-grad-{seed})"
|
|
175
|
+
transform="rotate({initialRotation} 10 10)"
|
|
176
|
+
/>
|
|
177
|
+
<!-- Subtle vein -->
|
|
178
|
+
<path
|
|
179
|
+
d="M10 3 Q10 10 10 18"
|
|
180
|
+
fill="none"
|
|
181
|
+
stroke={cherryBlossomsPeak.light}
|
|
182
|
+
stroke-width="0.5"
|
|
183
|
+
opacity="0.3"
|
|
184
|
+
transform="rotate({initialRotation} 10 10)"
|
|
185
|
+
/>
|
|
186
|
+
{:else if variant === 'pointed'}
|
|
187
|
+
<!-- Pointed petal - slightly more dramatic -->
|
|
188
|
+
<path
|
|
189
|
+
d="M10 2 Q18 8 16 14 Q12 20 10 18 Q8 20 4 14 Q2 8 10 2"
|
|
190
|
+
fill="url(#petal-grad-{seed})"
|
|
191
|
+
transform="rotate({initialRotation} 10 10)"
|
|
192
|
+
/>
|
|
193
|
+
{:else if variant === 'heart'}
|
|
194
|
+
<!-- Heart-shaped petal with notch at top -->
|
|
195
|
+
<path
|
|
196
|
+
d="M10 18 Q4 12 4 8 Q4 4 8 4 Q10 4 10 6 Q10 4 12 4 Q16 4 16 8 Q16 12 10 18"
|
|
197
|
+
fill="url(#petal-grad-{seed})"
|
|
198
|
+
transform="rotate({initialRotation} 10 10)"
|
|
199
|
+
/>
|
|
200
|
+
{:else if variant === 'curled'}
|
|
201
|
+
<!-- Curled petal - caught by the wind -->
|
|
202
|
+
<path
|
|
203
|
+
d="M6 4 Q14 2 16 8 Q18 14 14 18 Q8 18 6 14 Q4 10 6 4"
|
|
204
|
+
fill="url(#petal-grad-{seed})"
|
|
205
|
+
transform="rotate({initialRotation} 10 10)"
|
|
206
|
+
/>
|
|
207
|
+
<!-- Curl shadow -->
|
|
208
|
+
<path
|
|
209
|
+
d="M8 6 Q12 5 14 8"
|
|
210
|
+
fill="none"
|
|
211
|
+
stroke={cherryBlossomsPeak.light}
|
|
212
|
+
stroke-width="0.5"
|
|
213
|
+
opacity="0.4"
|
|
214
|
+
transform="rotate({initialRotation} 10 10)"
|
|
215
|
+
/>
|
|
216
|
+
{:else}
|
|
217
|
+
<!-- Tiny - simple dot for distant petals -->
|
|
218
|
+
<circle
|
|
219
|
+
cx="10"
|
|
220
|
+
cy="10"
|
|
221
|
+
r="6"
|
|
222
|
+
fill={petalColor}
|
|
223
|
+
transform="rotate({initialRotation} 10 10)"
|
|
224
|
+
/>
|
|
225
|
+
{/if}
|
|
226
|
+
</svg>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<style>
|
|
230
|
+
/* Dynamic keyframes are injected via style attribute */
|
|
231
|
+
/* These are fallback static versions */
|
|
232
|
+
@keyframes petal-fall-base {
|
|
233
|
+
0% {
|
|
234
|
+
transform: translateY(0) translateX(0);
|
|
235
|
+
}
|
|
236
|
+
100% {
|
|
237
|
+
transform: translateY(100vh) translateX(var(--drift, 30px));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@keyframes petal-flutter-base {
|
|
242
|
+
0%, 100% {
|
|
243
|
+
transform: rotateX(0deg) rotateY(0deg) rotateZ(0deg);
|
|
244
|
+
}
|
|
245
|
+
25% {
|
|
246
|
+
transform: rotateX(15deg) rotateY(30deg) rotateZ(10deg);
|
|
247
|
+
}
|
|
248
|
+
50% {
|
|
249
|
+
transform: rotateX(-10deg) rotateY(-20deg) rotateZ(-5deg);
|
|
250
|
+
}
|
|
251
|
+
75% {
|
|
252
|
+
transform: rotateX(20deg) rotateY(10deg) rotateZ(15deg);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.petal-wrapper {
|
|
257
|
+
display: inline-block;
|
|
258
|
+
will-change: transform, opacity;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
svg {
|
|
262
|
+
display: block;
|
|
263
|
+
transform-style: preserve-3d;
|
|
264
|
+
will-change: transform;
|
|
265
|
+
}
|
|
266
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type PetalVariant = 'round' | 'pointed' | 'heart' | 'curled' | 'tiny';
|
|
2
|
+
interface Props {
|
|
3
|
+
class?: string;
|
|
4
|
+
/** Petal shape variant */
|
|
5
|
+
variant?: PetalVariant;
|
|
6
|
+
/** Override petal color (defaults to spring blossom pink) */
|
|
7
|
+
color?: string;
|
|
8
|
+
/** Fall animation duration in seconds */
|
|
9
|
+
duration?: number;
|
|
10
|
+
/** Animation delay in seconds */
|
|
11
|
+
delay?: number;
|
|
12
|
+
/** Horizontal drift amount in pixels (can be negative) */
|
|
13
|
+
drift?: number;
|
|
14
|
+
/** Fall distance in vh units */
|
|
15
|
+
fallDistance?: number;
|
|
16
|
+
/** Enable falling animation */
|
|
17
|
+
animate?: boolean;
|
|
18
|
+
/** Opacity for depth effect */
|
|
19
|
+
opacity?: number;
|
|
20
|
+
/** Seed for deterministic rotation patterns */
|
|
21
|
+
seed?: number;
|
|
22
|
+
}
|
|
23
|
+
declare const PetalFalling: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type PetalFalling = ReturnType<typeof PetalFalling>;
|
|
25
|
+
export default PetalFalling;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Grove — A place to Be
|
|
3
|
+
Copyright (c) 2025 Autumn Brown
|
|
4
|
+
Licensed under AGPL-3.0
|
|
5
|
+
-->
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import { bark } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
color?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
class: className = 'w-5 h-8',
|
|
16
|
+
color
|
|
17
|
+
}: Props = $props();
|
|
18
|
+
|
|
19
|
+
const coneColor = $derived(color ?? bark.warmBark);
|
|
20
|
+
const scaleLight = bark.lightBark;
|
|
21
|
+
const scaleDark = bark.darkBark;
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<!-- Pine cone -->
|
|
25
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 55">
|
|
26
|
+
<!-- Stem -->
|
|
27
|
+
<rect fill={scaleDark} x="15" y="0" width="5" height="6" rx="1" />
|
|
28
|
+
|
|
29
|
+
<!-- Pine cone body (overlapping scales) -->
|
|
30
|
+
<!-- Top scales -->
|
|
31
|
+
<ellipse fill={coneColor} cx="17.5" cy="12" rx="6" ry="4" />
|
|
32
|
+
<path fill={scaleDark} d="M12 12 Q17.5 10 23 12" stroke-width="0" opacity="0.3" />
|
|
33
|
+
|
|
34
|
+
<!-- Upper-mid scales -->
|
|
35
|
+
<ellipse fill={coneColor} cx="12" cy="18" rx="7" ry="5" />
|
|
36
|
+
<ellipse fill={coneColor} cx="23" cy="18" rx="7" ry="5" />
|
|
37
|
+
<path fill={scaleLight} d="M6 18 Q12 15 18 18" opacity="0.2" />
|
|
38
|
+
<path fill={scaleLight} d="M17 18 Q23 15 29 18" opacity="0.2" />
|
|
39
|
+
|
|
40
|
+
<!-- Middle scales -->
|
|
41
|
+
<ellipse fill={coneColor} cx="17.5" cy="24" rx="8" ry="5" />
|
|
42
|
+
<ellipse fill={coneColor} cx="8" cy="26" rx="7" ry="5" />
|
|
43
|
+
<ellipse fill={coneColor} cx="27" cy="26" rx="7" ry="5" />
|
|
44
|
+
<path fill={scaleDark} d="M10 24 Q17.5 21 25 24" opacity="0.3" />
|
|
45
|
+
|
|
46
|
+
<!-- Lower-mid scales -->
|
|
47
|
+
<ellipse fill={coneColor} cx="12" cy="33" rx="8" ry="5" />
|
|
48
|
+
<ellipse fill={coneColor} cx="23" cy="33" rx="8" ry="5" />
|
|
49
|
+
<path fill={scaleLight} d="M5 33 Q12 30 19 33" opacity="0.2" />
|
|
50
|
+
<path fill={scaleLight} d="M16 33 Q23 30 30 33" opacity="0.2" />
|
|
51
|
+
|
|
52
|
+
<!-- Bottom scales -->
|
|
53
|
+
<ellipse fill={coneColor} cx="17.5" cy="40" rx="9" ry="5" />
|
|
54
|
+
<ellipse fill={coneColor} cx="10" cy="42" rx="6" ry="4" />
|
|
55
|
+
<ellipse fill={coneColor} cx="25" cy="42" rx="6" ry="4" />
|
|
56
|
+
<path fill={scaleDark} d="M9 40 Q17.5 37 26 40" opacity="0.3" />
|
|
57
|
+
|
|
58
|
+
<!-- Tip scales -->
|
|
59
|
+
<ellipse fill={coneColor} cx="17.5" cy="48" rx="7" ry="4" />
|
|
60
|
+
<ellipse fill={coneColor} cx="17.5" cy="52" rx="4" ry="3" />
|
|
61
|
+
</svg>
|