@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,102 @@
|
|
|
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
|
+
animate?: boolean;
|
|
15
|
+
variant?: 'tendril' | 'ivy' | 'flowering';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
class: className = 'w-8 h-12',
|
|
20
|
+
color,
|
|
21
|
+
season = 'summer',
|
|
22
|
+
animate = false,
|
|
23
|
+
variant = 'tendril'
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
const defaultColor = $derived(season === 'autumn' ? autumn.rust : greens.grove);
|
|
27
|
+
const vineColor = $derived(color ?? defaultColor);
|
|
28
|
+
const leafColor = $derived(season === 'autumn' ? autumn.amber : greens.meadow);
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<!-- Vine/tendril -->
|
|
32
|
+
<svg class="{className} {animate ? 'grow' : ''}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 80">
|
|
33
|
+
{#if variant === 'tendril'}
|
|
34
|
+
<!-- Simple curling tendril -->
|
|
35
|
+
<path
|
|
36
|
+
fill="none"
|
|
37
|
+
stroke={vineColor}
|
|
38
|
+
stroke-width="2"
|
|
39
|
+
d="M25 80 Q20 60 25 50 Q30 40 25 30 Q20 20 30 15 Q40 10 45 5"
|
|
40
|
+
stroke-linecap="round"
|
|
41
|
+
/>
|
|
42
|
+
<!-- Curl at end -->
|
|
43
|
+
<path
|
|
44
|
+
fill="none"
|
|
45
|
+
stroke={vineColor}
|
|
46
|
+
stroke-width="1.5"
|
|
47
|
+
d="M45 5 Q50 8 48 12 Q45 15 42 12 Q40 8 45 5"
|
|
48
|
+
stroke-linecap="round"
|
|
49
|
+
/>
|
|
50
|
+
<!-- Small leaves -->
|
|
51
|
+
<ellipse fill={leafColor} cx="22" cy="55" rx="5" ry="3" transform="rotate(-30 22 55)" />
|
|
52
|
+
<ellipse fill={leafColor} cx="28" cy="35" rx="5" ry="3" transform="rotate(20 28 35)" />
|
|
53
|
+
{:else if variant === 'ivy'}
|
|
54
|
+
<!-- Ivy vine with leaves -->
|
|
55
|
+
<path
|
|
56
|
+
fill="none"
|
|
57
|
+
stroke={vineColor}
|
|
58
|
+
stroke-width="2"
|
|
59
|
+
d="M25 80 Q22 65 28 55 Q34 45 26 35 Q18 25 25 15 Q32 5 30 0"
|
|
60
|
+
stroke-linecap="round"
|
|
61
|
+
/>
|
|
62
|
+
<!-- Ivy leaves (3-lobed) -->
|
|
63
|
+
<path fill={leafColor} d="M18 60 Q12 55 10 60 Q8 65 12 68 Q16 70 18 65 Q20 62 18 60" />
|
|
64
|
+
<path fill={leafColor} d="M32 45 Q38 40 40 45 Q42 50 38 53 Q34 55 32 50 Q30 47 32 45" />
|
|
65
|
+
<path fill={leafColor} d="M20 30 Q14 25 12 30 Q10 35 14 38 Q18 40 20 35 Q22 32 20 30" />
|
|
66
|
+
<path fill={leafColor} d="M30 15 Q36 10 38 15 Q40 20 36 23 Q32 25 30 20 Q28 17 30 15" />
|
|
67
|
+
{:else}
|
|
68
|
+
<!-- Flowering vine -->
|
|
69
|
+
<path
|
|
70
|
+
fill="none"
|
|
71
|
+
stroke={vineColor}
|
|
72
|
+
stroke-width="2"
|
|
73
|
+
d="M25 80 Q20 60 30 45 Q40 30 30 15 Q20 0 25 -5"
|
|
74
|
+
stroke-linecap="round"
|
|
75
|
+
/>
|
|
76
|
+
<!-- Leaves -->
|
|
77
|
+
<ellipse fill={leafColor} cx="22" cy="58" rx="6" ry="4" transform="rotate(-20 22 58)" />
|
|
78
|
+
<ellipse fill={leafColor} cx="35" cy="38" rx="6" ry="4" transform="rotate(25 35 38)" />
|
|
79
|
+
<ellipse fill={leafColor} cx="25" cy="20" rx="5" ry="3" transform="rotate(-15 25 20)" />
|
|
80
|
+
<!-- Small flowers -->
|
|
81
|
+
<circle fill="#f9a8d4" cx="28" cy="50" r="4" />
|
|
82
|
+
<circle fill="#fbbf24" cx="28" cy="50" r="1.5" />
|
|
83
|
+
<circle fill="#f9a8d4" cx="32" cy="28" r="3" />
|
|
84
|
+
<circle fill="#fbbf24" cx="32" cy="28" r="1" />
|
|
85
|
+
{/if}
|
|
86
|
+
</svg>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
@keyframes grow {
|
|
90
|
+
0% {
|
|
91
|
+
stroke-dasharray: 200;
|
|
92
|
+
stroke-dashoffset: 200;
|
|
93
|
+
}
|
|
94
|
+
100% {
|
|
95
|
+
stroke-dashoffset: 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.grow path {
|
|
100
|
+
animation: grow 3s ease-out forwards;
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Season } from '../palette';
|
|
2
|
+
interface Props {
|
|
3
|
+
class?: string;
|
|
4
|
+
color?: string;
|
|
5
|
+
season?: Season;
|
|
6
|
+
animate?: boolean;
|
|
7
|
+
variant?: 'tendril' | 'ivy' | 'flowering';
|
|
8
|
+
}
|
|
9
|
+
declare const Vine: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Vine = ReturnType<typeof Vine>;
|
|
11
|
+
export default Vine;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as Acorn } from './Acorn.svelte';
|
|
2
|
+
export { default as Berry } from './Berry.svelte';
|
|
3
|
+
export { default as DandelionPuff } from './DandelionPuff.svelte';
|
|
4
|
+
export { default as FallingLeavesLayer } from './FallingLeavesLayer.svelte';
|
|
5
|
+
export { default as FallingPetalsLayer } from './FallingPetalsLayer.svelte';
|
|
6
|
+
export { default as Leaf } from './Leaf.svelte';
|
|
7
|
+
export { default as LeafFalling } from './LeafFalling.svelte';
|
|
8
|
+
export { default as PetalFalling } from './PetalFalling.svelte';
|
|
9
|
+
export { default as PineCone } from './PineCone.svelte';
|
|
10
|
+
export { default as Vine } from './Vine.svelte';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Botanical components - leaves, petals, and plant elements
|
|
2
|
+
export { default as Acorn } from './Acorn.svelte';
|
|
3
|
+
export { default as Berry } from './Berry.svelte';
|
|
4
|
+
export { default as DandelionPuff } from './DandelionPuff.svelte';
|
|
5
|
+
export { default as FallingLeavesLayer } from './FallingLeavesLayer.svelte';
|
|
6
|
+
export { default as FallingPetalsLayer } from './FallingPetalsLayer.svelte';
|
|
7
|
+
export { default as Leaf } from './Leaf.svelte';
|
|
8
|
+
export { default as LeafFalling } from './LeafFalling.svelte';
|
|
9
|
+
export { default as PetalFalling } from './PetalFalling.svelte';
|
|
10
|
+
export { default as PineCone } from './PineCone.svelte';
|
|
11
|
+
export { default as Vine } from './Vine.svelte';
|
|
@@ -0,0 +1,78 @@
|
|
|
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 { accents, bark } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
bodyColor?: string;
|
|
12
|
+
stripeColor?: string;
|
|
13
|
+
animate?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
class: className = 'w-5 h-5',
|
|
18
|
+
bodyColor,
|
|
19
|
+
stripeColor,
|
|
20
|
+
animate = true
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const body = $derived(bodyColor ?? accents.flower.yellow);
|
|
24
|
+
const stripes = $derived(stripeColor ?? bark.darkBark);
|
|
25
|
+
const wingColor = $derived('#e0f2fe'); // Light blue-ish transparent
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<!-- Bumble bee -->
|
|
29
|
+
<svg class="{className} {animate ? 'hover' : ''}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 40">
|
|
30
|
+
<!-- Wings -->
|
|
31
|
+
<g class={animate ? 'buzz' : ''}>
|
|
32
|
+
<ellipse fill={wingColor} cx="20" cy="12" rx="10" ry="6" opacity="0.6" />
|
|
33
|
+
<ellipse fill={wingColor} cx="30" cy="12" rx="10" ry="6" opacity="0.6" />
|
|
34
|
+
</g>
|
|
35
|
+
|
|
36
|
+
<!-- Body -->
|
|
37
|
+
<ellipse fill={body} cx="25" cy="24" rx="18" ry="12" />
|
|
38
|
+
|
|
39
|
+
<!-- Stripes -->
|
|
40
|
+
<path fill={stripes} d="M15 18 Q25 14 35 18 L35 22 Q25 18 15 22 Z" />
|
|
41
|
+
<path fill={stripes} d="M12 26 Q25 22 38 26 L38 30 Q25 26 12 30 Z" />
|
|
42
|
+
|
|
43
|
+
<!-- Head -->
|
|
44
|
+
<circle fill={stripes} cx="8" cy="24" r="6" />
|
|
45
|
+
|
|
46
|
+
<!-- Eyes -->
|
|
47
|
+
<circle fill="white" cx="5" cy="22" r="2" />
|
|
48
|
+
<circle fill={stripes} cx="5" cy="22" r="1" />
|
|
49
|
+
|
|
50
|
+
<!-- Antennae -->
|
|
51
|
+
<path fill="none" stroke={stripes} stroke-width="1" d="M6 18 Q4 14 2 12" />
|
|
52
|
+
<path fill="none" stroke={stripes} stroke-width="1" d="M10 18 Q8 14 6 12" />
|
|
53
|
+
|
|
54
|
+
<!-- Stinger -->
|
|
55
|
+
<path fill={stripes} d="M43 24 L48 24 L43 26 Z" />
|
|
56
|
+
</svg>
|
|
57
|
+
|
|
58
|
+
<style>
|
|
59
|
+
@keyframes hover {
|
|
60
|
+
0%, 100% { transform: translateY(0) translateX(0); }
|
|
61
|
+
25% { transform: translateY(-2px) translateX(1px); }
|
|
62
|
+
75% { transform: translateY(-1px) translateX(-1px); }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@keyframes buzz {
|
|
66
|
+
0%, 100% { transform: scaleY(1); }
|
|
67
|
+
50% { transform: scaleY(0.85); }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.hover {
|
|
71
|
+
animation: hover 1s ease-in-out infinite;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.buzz {
|
|
75
|
+
transform-origin: center bottom;
|
|
76
|
+
animation: buzz 0.05s linear infinite;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
@@ -0,0 +1,94 @@
|
|
|
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, accents } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
bodyColor?: string;
|
|
12
|
+
breastColor?: string;
|
|
13
|
+
beakColor?: string;
|
|
14
|
+
animate?: boolean;
|
|
15
|
+
facing?: 'left' | 'right';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
class: className = 'w-6 h-6',
|
|
20
|
+
bodyColor,
|
|
21
|
+
breastColor,
|
|
22
|
+
beakColor,
|
|
23
|
+
animate = true,
|
|
24
|
+
facing = 'right'
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
|
|
27
|
+
// American Robin colors - from palette
|
|
28
|
+
const body = $derived(bodyColor ?? accents.bird.robinBody);
|
|
29
|
+
const breast = $derived(breastColor ?? accents.bird.robinBreast);
|
|
30
|
+
const beak = $derived(beakColor ?? accents.bird.robinBeak);
|
|
31
|
+
const legColor = $derived(bark.darkBark);
|
|
32
|
+
|
|
33
|
+
const scaleX = $derived(facing === 'left' ? -1 : 1);
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<!-- American Robin - perched -->
|
|
37
|
+
<svg
|
|
38
|
+
class="{className} {animate ? 'bob' : ''}"
|
|
39
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
40
|
+
viewBox="0 0 50 55"
|
|
41
|
+
style="transform: scaleX({scaleX})"
|
|
42
|
+
>
|
|
43
|
+
<!-- Tail feathers - dark -->
|
|
44
|
+
<path fill={body} d="M2 30 Q0 34 3 40 Q10 38 14 32 Q8 30 2 30" />
|
|
45
|
+
|
|
46
|
+
<!-- Body - dark gray back -->
|
|
47
|
+
<ellipse fill={body} cx="22" cy="30" rx="14" ry="11" />
|
|
48
|
+
|
|
49
|
+
<!-- Wing detail - slightly darker -->
|
|
50
|
+
<path fill="#3a3a3a" d="M12 26 Q8 32 11 38 Q18 36 22 30 Q17 25 12 26" opacity="0.8" />
|
|
51
|
+
|
|
52
|
+
<!-- Orange-red breast - signature robin color -->
|
|
53
|
+
<ellipse fill={breast} cx="30" cy="33" rx="9" ry="10" />
|
|
54
|
+
<!-- Breast highlight -->
|
|
55
|
+
<ellipse fill={breast} cx="31" cy="31" rx="6" ry="6" opacity="0.9" />
|
|
56
|
+
|
|
57
|
+
<!-- White lower belly -->
|
|
58
|
+
<ellipse fill="#f5f5f5" cx="30" cy="42" rx="5" ry="3" opacity="0.8" />
|
|
59
|
+
|
|
60
|
+
<!-- Head - dark gray -->
|
|
61
|
+
<circle fill={body} cx="36" cy="20" r="10" />
|
|
62
|
+
|
|
63
|
+
<!-- White eye-ring (robin signature) -->
|
|
64
|
+
<circle fill="white" cx="40" cy="18" r="3.5" />
|
|
65
|
+
<!-- Eye -->
|
|
66
|
+
<circle fill="#1a1a1a" cx="40" cy="18" r="2" />
|
|
67
|
+
<!-- Eye highlight -->
|
|
68
|
+
<circle fill="white" cx="41" cy="17" r="0.7" />
|
|
69
|
+
|
|
70
|
+
<!-- White crescent below eye -->
|
|
71
|
+
<path fill="white" d="M38 22 Q40 23 42 22 Q41 24 39 24 Q38 23 38 22" opacity="0.6" />
|
|
72
|
+
|
|
73
|
+
<!-- Beak - yellow-orange -->
|
|
74
|
+
<path fill={beak} d="M45 20 L52 22 L45 24 Z" />
|
|
75
|
+
<!-- Beak detail line -->
|
|
76
|
+
<line x1="45" y1="22" x2="50" y2="22" stroke="#b45309" stroke-width="0.5" />
|
|
77
|
+
|
|
78
|
+
<!-- Legs - dark -->
|
|
79
|
+
<g fill="none" stroke={legColor} stroke-width="1.5">
|
|
80
|
+
<path d="M22 41 L22 50 M20 48 L24 48" />
|
|
81
|
+
<path d="M28 41 L28 50 M26 48 L30 48" />
|
|
82
|
+
</g>
|
|
83
|
+
</svg>
|
|
84
|
+
|
|
85
|
+
<style>
|
|
86
|
+
@keyframes bob {
|
|
87
|
+
0%, 100% { transform: translateY(0); }
|
|
88
|
+
50% { transform: translateY(-1.5px); }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.bob {
|
|
92
|
+
animation: bob 2s ease-in-out infinite;
|
|
93
|
+
}
|
|
94
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
class?: string;
|
|
3
|
+
bodyColor?: string;
|
|
4
|
+
breastColor?: string;
|
|
5
|
+
beakColor?: string;
|
|
6
|
+
animate?: boolean;
|
|
7
|
+
facing?: 'left' | 'right';
|
|
8
|
+
}
|
|
9
|
+
declare const Bird: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Bird = ReturnType<typeof Bird>;
|
|
11
|
+
export default Bird;
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
animate?: boolean;
|
|
13
|
+
facing?: 'left' | 'right';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
class: className = 'w-6 h-4',
|
|
18
|
+
color,
|
|
19
|
+
animate = true,
|
|
20
|
+
facing = 'right'
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const birdColor = $derived(color ?? bark.bark);
|
|
24
|
+
const scaleX = $derived(facing === 'left' ? -1 : 1);
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<!-- Bird in flight - simple silhouette -->
|
|
28
|
+
<svg
|
|
29
|
+
class={className}
|
|
30
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
31
|
+
viewBox="0 0 60 30"
|
|
32
|
+
style="transform: scaleX({scaleX})"
|
|
33
|
+
>
|
|
34
|
+
<!-- Wings and body as single flowing shape -->
|
|
35
|
+
<g class={animate ? 'fly' : ''}>
|
|
36
|
+
<!-- Left wing -->
|
|
37
|
+
<path
|
|
38
|
+
fill={birdColor}
|
|
39
|
+
d="M30 15 Q20 5 5 8 Q15 12 25 15"
|
|
40
|
+
class={animate ? 'wing-up' : ''}
|
|
41
|
+
/>
|
|
42
|
+
|
|
43
|
+
<!-- Right wing -->
|
|
44
|
+
<path
|
|
45
|
+
fill={birdColor}
|
|
46
|
+
d="M30 15 Q40 5 55 8 Q45 12 35 15"
|
|
47
|
+
class={animate ? 'wing-up' : ''}
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<!-- Body -->
|
|
51
|
+
<ellipse fill={birdColor} cx="30" cy="15" rx="8" ry="5" />
|
|
52
|
+
|
|
53
|
+
<!-- Head -->
|
|
54
|
+
<circle fill={birdColor} cx="40" cy="14" r="4" />
|
|
55
|
+
|
|
56
|
+
<!-- Beak -->
|
|
57
|
+
<path fill={birdColor} d="M44 14 L50 15 L44 16 Z" />
|
|
58
|
+
|
|
59
|
+
<!-- Tail -->
|
|
60
|
+
<path fill={birdColor} d="M22 15 Q15 12 10 15 Q15 18 22 15" />
|
|
61
|
+
</g>
|
|
62
|
+
</svg>
|
|
63
|
+
|
|
64
|
+
<style>
|
|
65
|
+
@keyframes fly {
|
|
66
|
+
0%, 100% { transform: translateY(0); }
|
|
67
|
+
50% { transform: translateY(-3px); }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@keyframes flap {
|
|
71
|
+
0%, 100% { transform: rotate(0deg) scaleY(1); }
|
|
72
|
+
50% { transform: rotate(-10deg) scaleY(0.8); }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.fly {
|
|
76
|
+
animation: fly 0.8s ease-in-out infinite;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.wing-up {
|
|
80
|
+
transform-origin: center bottom;
|
|
81
|
+
animation: flap 0.3s ease-in-out infinite;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
@@ -0,0 +1,95 @@
|
|
|
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, accents } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
style?: string;
|
|
12
|
+
bodyColor?: string;
|
|
13
|
+
breastColor?: string;
|
|
14
|
+
beakColor?: string;
|
|
15
|
+
animate?: boolean;
|
|
16
|
+
facing?: 'left' | 'right';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
class: className = 'w-6 h-6',
|
|
21
|
+
style,
|
|
22
|
+
bodyColor,
|
|
23
|
+
breastColor,
|
|
24
|
+
beakColor,
|
|
25
|
+
animate = true,
|
|
26
|
+
facing = 'right'
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
// Eastern Bluebird colors - from palette
|
|
30
|
+
const body = $derived(bodyColor ?? accents.bird.bluebirdBody);
|
|
31
|
+
const wing = $derived(accents.bird.bluebirdWing);
|
|
32
|
+
const breast = $derived(breastColor ?? accents.bird.bluebirdBreast);
|
|
33
|
+
const belly = $derived(accents.bird.chickadeeBelly); // Cream/white (shared with chickadee)
|
|
34
|
+
const beak = $derived(beakColor ?? '#1a1a1a');
|
|
35
|
+
const legColor = $derived(bark.darkBark);
|
|
36
|
+
|
|
37
|
+
const scaleX = $derived(facing === 'left' ? -1 : 1);
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<!-- Eastern Bluebird - symbol of happiness and spring! -->
|
|
41
|
+
<svg
|
|
42
|
+
class="{className} {animate ? 'flutter' : ''}"
|
|
43
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
44
|
+
viewBox="0 0 50 55"
|
|
45
|
+
style="transform: scaleX({scaleX}); {style ?? ''}"
|
|
46
|
+
>
|
|
47
|
+
<!-- Tail feathers - bright blue -->
|
|
48
|
+
<path fill={body} d="M6 30 Q3 36 5 44 Q11 42 15 36 Q16 32 12 29 Q8 28 6 30" />
|
|
49
|
+
<path fill={wing} d="M7 32 Q5 38 6 42 Q10 40 12 36" opacity="0.6" />
|
|
50
|
+
|
|
51
|
+
<!-- Body - blue upper -->
|
|
52
|
+
<ellipse fill={body} cx="22" cy="28" rx="13" ry="10" />
|
|
53
|
+
|
|
54
|
+
<!-- Wing - darker blue with detail -->
|
|
55
|
+
<path fill={wing} d="M10 24 Q6 30 9 38 Q16 36 19 30 Q17 23 10 24" />
|
|
56
|
+
<path fill="#1d4ed8" d="M12 28 Q10 32 12 36 Q15 34 16 31 Q14 28 12 28" opacity="0.5" />
|
|
57
|
+
|
|
58
|
+
<!-- Breast - rusty orange -->
|
|
59
|
+
<ellipse fill={breast} cx="28" cy="30" rx="7" ry="8" />
|
|
60
|
+
<!-- Breast highlight -->
|
|
61
|
+
<ellipse fill="#f97316" cx="30" cy="28" rx="4" ry="5" opacity="0.4" />
|
|
62
|
+
|
|
63
|
+
<!-- Belly - cream/white -->
|
|
64
|
+
<ellipse fill={belly} cx="28" cy="38" rx="5" ry="4" />
|
|
65
|
+
|
|
66
|
+
<!-- Head - bright blue -->
|
|
67
|
+
<circle fill={body} cx="35" cy="16" r="8" />
|
|
68
|
+
|
|
69
|
+
<!-- Eye -->
|
|
70
|
+
<circle fill="#1a1a1a" cx="38" cy="15" r="2" />
|
|
71
|
+
<!-- Eye highlight -->
|
|
72
|
+
<circle fill="white" cx="39" cy="14" r="0.6" />
|
|
73
|
+
|
|
74
|
+
<!-- Beak - small and dark -->
|
|
75
|
+
<path fill={beak} d="M42 16 L48 17 L42 19 Q41 17.5 42 16" />
|
|
76
|
+
|
|
77
|
+
<!-- Legs -->
|
|
78
|
+
<g fill="none" stroke={legColor} stroke-width="1.2">
|
|
79
|
+
<path d="M22 38 L22 48 M20 46 L24 46" />
|
|
80
|
+
<path d="M27 38 L27 48 M25 46 L29 46" />
|
|
81
|
+
</g>
|
|
82
|
+
</svg>
|
|
83
|
+
|
|
84
|
+
<style>
|
|
85
|
+
@keyframes flutter {
|
|
86
|
+
0%, 100% { transform: translateY(0) rotate(0deg); }
|
|
87
|
+
25% { transform: translateY(-2px) rotate(1deg); }
|
|
88
|
+
50% { transform: translateY(0) rotate(0deg); }
|
|
89
|
+
75% { transform: translateY(-1px) rotate(-1deg); }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.flutter {
|
|
93
|
+
animation: flutter 2s ease-in-out infinite;
|
|
94
|
+
}
|
|
95
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
class?: string;
|
|
3
|
+
style?: string;
|
|
4
|
+
bodyColor?: string;
|
|
5
|
+
breastColor?: string;
|
|
6
|
+
beakColor?: string;
|
|
7
|
+
animate?: boolean;
|
|
8
|
+
facing?: 'left' | 'right';
|
|
9
|
+
}
|
|
10
|
+
declare const Bluebird: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type Bluebird = ReturnType<typeof Bluebird>;
|
|
12
|
+
export default Bluebird;
|
|
@@ -0,0 +1,87 @@
|
|
|
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 { accents, bark } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
wingColor?: string;
|
|
12
|
+
accentColor?: string;
|
|
13
|
+
animate?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
class: className = 'w-6 h-6',
|
|
18
|
+
wingColor,
|
|
19
|
+
accentColor,
|
|
20
|
+
animate = true
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
const wing = $derived(wingColor ?? accents.flower.purple);
|
|
24
|
+
const accent = $derived(accentColor ?? accents.flower.yellow);
|
|
25
|
+
const bodyColor = $derived(bark.darkBark);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<!-- Butterfly with flapping wings -->
|
|
29
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 40">
|
|
30
|
+
<!-- Left wings -->
|
|
31
|
+
<g class={animate ? 'wing-left' : ''}>
|
|
32
|
+
<!-- Upper wing -->
|
|
33
|
+
<ellipse fill={wing} cx="12" cy="15" rx="12" ry="10" />
|
|
34
|
+
<circle fill={accent} cx="10" cy="13" r="3" opacity="0.7" />
|
|
35
|
+
<circle fill={accent} cx="16" cy="17" r="2" opacity="0.5" />
|
|
36
|
+
|
|
37
|
+
<!-- Lower wing -->
|
|
38
|
+
<ellipse fill={wing} cx="14" cy="30" rx="10" ry="8" />
|
|
39
|
+
<circle fill={accent} cx="12" cy="28" r="2" opacity="0.6" />
|
|
40
|
+
</g>
|
|
41
|
+
|
|
42
|
+
<!-- Right wings -->
|
|
43
|
+
<g class={animate ? 'wing-right' : ''}>
|
|
44
|
+
<!-- Upper wing -->
|
|
45
|
+
<ellipse fill={wing} cx="38" cy="15" rx="12" ry="10" />
|
|
46
|
+
<circle fill={accent} cx="40" cy="13" r="3" opacity="0.7" />
|
|
47
|
+
<circle fill={accent} cx="34" cy="17" r="2" opacity="0.5" />
|
|
48
|
+
|
|
49
|
+
<!-- Lower wing -->
|
|
50
|
+
<ellipse fill={wing} cx="36" cy="30" rx="10" ry="8" />
|
|
51
|
+
<circle fill={accent} cx="38" cy="28" r="2" opacity="0.6" />
|
|
52
|
+
</g>
|
|
53
|
+
|
|
54
|
+
<!-- Body -->
|
|
55
|
+
<ellipse fill={bodyColor} cx="25" cy="20" rx="3" ry="12" />
|
|
56
|
+
|
|
57
|
+
<!-- Head -->
|
|
58
|
+
<circle fill={bodyColor} cx="25" cy="6" r="3" />
|
|
59
|
+
|
|
60
|
+
<!-- Antennae -->
|
|
61
|
+
<path fill="none" stroke={bodyColor} stroke-width="1" d="M23 4 Q20 0 18 2" />
|
|
62
|
+
<path fill="none" stroke={bodyColor} stroke-width="1" d="M27 4 Q30 0 32 2" />
|
|
63
|
+
<circle fill={bodyColor} cx="18" cy="2" r="1" />
|
|
64
|
+
<circle fill={bodyColor} cx="32" cy="2" r="1" />
|
|
65
|
+
</svg>
|
|
66
|
+
|
|
67
|
+
<style>
|
|
68
|
+
@keyframes flutter-left {
|
|
69
|
+
0%, 100% { transform: scaleX(1) rotate(0deg); }
|
|
70
|
+
50% { transform: scaleX(0.7) rotate(-5deg); }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@keyframes flutter-right {
|
|
74
|
+
0%, 100% { transform: scaleX(1) rotate(0deg); }
|
|
75
|
+
50% { transform: scaleX(0.7) rotate(5deg); }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.wing-left {
|
|
79
|
+
transform-origin: right center;
|
|
80
|
+
animation: flutter-left 0.3s ease-in-out infinite;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.wing-right {
|
|
84
|
+
transform-origin: left center;
|
|
85
|
+
animation: flutter-right 0.3s ease-in-out infinite;
|
|
86
|
+
}
|
|
87
|
+
</style>
|