@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,14 @@
|
|
|
1
|
+
import type { Season } from './palette';
|
|
2
|
+
interface Props {
|
|
3
|
+
class?: string;
|
|
4
|
+
color?: string;
|
|
5
|
+
trunkColor?: string;
|
|
6
|
+
season?: Season;
|
|
7
|
+
animate?: boolean;
|
|
8
|
+
animateEntrance?: boolean;
|
|
9
|
+
/** Add breathing animation (subtle pulse for loading states) */
|
|
10
|
+
breathing?: boolean;
|
|
11
|
+
}
|
|
12
|
+
declare const Logo: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Logo = ReturnType<typeof Logo>;
|
|
14
|
+
export default Logo;
|
|
@@ -0,0 +1,48 @@
|
|
|
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, earth } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
capColor?: string;
|
|
12
|
+
nutColor?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
class: className = 'w-4 h-5',
|
|
17
|
+
capColor,
|
|
18
|
+
nutColor
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
const cap = $derived(capColor ?? bark.bark);
|
|
22
|
+
const nut = $derived(nutColor ?? earth.clay);
|
|
23
|
+
const capDetail = bark.darkBark;
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<!-- Acorn -->
|
|
27
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 40">
|
|
28
|
+
<!-- Stem -->
|
|
29
|
+
<path fill={capDetail} d="M15 0 Q18 0 18 4 L15 6 L12 4 Q12 0 15 0" />
|
|
30
|
+
|
|
31
|
+
<!-- Cap -->
|
|
32
|
+
<ellipse fill={cap} cx="15" cy="12" rx="13" ry="8" />
|
|
33
|
+
<!-- Cap texture (crosshatch pattern suggestion) -->
|
|
34
|
+
<path fill="none" stroke={capDetail} stroke-width="0.5" d="M5 10 Q15 14 25 10" opacity="0.4" />
|
|
35
|
+
<path fill="none" stroke={capDetail} stroke-width="0.5" d="M4 13 Q15 17 26 13" opacity="0.3" />
|
|
36
|
+
<path fill="none" stroke={capDetail} stroke-width="0.5" d="M8 8 L8 16" opacity="0.2" />
|
|
37
|
+
<path fill="none" stroke={capDetail} stroke-width="0.5" d="M15 6 L15 16" opacity="0.2" />
|
|
38
|
+
<path fill="none" stroke={capDetail} stroke-width="0.5" d="M22 8 L22 16" opacity="0.2" />
|
|
39
|
+
|
|
40
|
+
<!-- Nut body -->
|
|
41
|
+
<ellipse fill={nut} cx="15" cy="26" rx="11" ry="14" />
|
|
42
|
+
|
|
43
|
+
<!-- Nut highlight -->
|
|
44
|
+
<ellipse fill="white" cx="12" cy="22" rx="4" ry="5" opacity="0.15" />
|
|
45
|
+
|
|
46
|
+
<!-- Bottom point -->
|
|
47
|
+
<path fill={nut} d="M10 38 Q15 42 20 38 L15 40 Z" />
|
|
48
|
+
</svg>
|
|
@@ -0,0 +1,67 @@
|
|
|
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, greens } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
berryColor?: string;
|
|
12
|
+
variant?: 'cluster' | 'single' | 'branch';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
class: className = 'w-6 h-6',
|
|
17
|
+
berryColor,
|
|
18
|
+
variant = 'cluster'
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
const berry = $derived(berryColor ?? accents.berry.ripe);
|
|
22
|
+
const stem = greens.deepGreen;
|
|
23
|
+
const highlight = 'white';
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<!-- Berries -->
|
|
27
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
|
|
28
|
+
{#if variant === 'single'}
|
|
29
|
+
<!-- Single berry -->
|
|
30
|
+
<circle fill={berry} cx="25" cy="28" r="15" />
|
|
31
|
+
<circle fill={highlight} cx="20" cy="23" r="4" opacity="0.3" />
|
|
32
|
+
<path fill="none" stroke={stem} stroke-width="2" d="M25 13 Q25 5 20 2" />
|
|
33
|
+
<!-- Calyx -->
|
|
34
|
+
<path fill={stem} d="M20 14 L25 18 L30 14 L28 16 L25 14 L22 16 Z" />
|
|
35
|
+
{:else if variant === 'cluster'}
|
|
36
|
+
<!-- Berry cluster -->
|
|
37
|
+
<path fill="none" stroke={stem} stroke-width="1.5" d="M25 5 L25 15 M25 15 L18 22 M25 15 L32 22 M25 15 L25 25" />
|
|
38
|
+
|
|
39
|
+
<circle fill={berry} cx="18" cy="28" r="10" />
|
|
40
|
+
<circle fill={highlight} cx="14" cy="24" r="2.5" opacity="0.3" />
|
|
41
|
+
|
|
42
|
+
<circle fill={berry} cx="32" cy="28" r="10" />
|
|
43
|
+
<circle fill={highlight} cx="28" cy="24" r="2.5" opacity="0.3" />
|
|
44
|
+
|
|
45
|
+
<circle fill={berry} cx="25" cy="38" r="10" />
|
|
46
|
+
<circle fill={highlight} cx="21" cy="34" r="2.5" opacity="0.3" />
|
|
47
|
+
{:else}
|
|
48
|
+
<!-- Berry branch -->
|
|
49
|
+
<path fill="none" stroke={stem} stroke-width="2" d="M5 25 Q15 20 25 22 Q35 24 45 20" />
|
|
50
|
+
<!-- Leaves -->
|
|
51
|
+
<ellipse fill={stem} cx="12" cy="22" rx="5" ry="3" transform="rotate(-20 12 22)" />
|
|
52
|
+
<ellipse fill={stem} cx="38" cy="20" rx="5" ry="3" transform="rotate(15 38 20)" />
|
|
53
|
+
|
|
54
|
+
<!-- Berries along branch -->
|
|
55
|
+
<circle fill={berry} cx="18" cy="28" r="6" />
|
|
56
|
+
<circle fill={highlight} cx="16" cy="26" r="1.5" opacity="0.3" />
|
|
57
|
+
|
|
58
|
+
<circle fill={berry} cx="28" cy="30" r="6" />
|
|
59
|
+
<circle fill={highlight} cx="26" cy="28" r="1.5" opacity="0.3" />
|
|
60
|
+
|
|
61
|
+
<circle fill={berry} cx="22" cy="36" r="5" />
|
|
62
|
+
<circle fill={highlight} cx="20" cy="34" r="1.2" opacity="0.3" />
|
|
63
|
+
|
|
64
|
+
<circle fill={berry} cx="32" cy="38" r="5" />
|
|
65
|
+
<circle fill={highlight} cx="30" cy="36" r="1.2" opacity="0.3" />
|
|
66
|
+
{/if}
|
|
67
|
+
</svg>
|
|
@@ -0,0 +1,98 @@
|
|
|
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 { greens, natural } from '../palette';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
class?: string;
|
|
11
|
+
seedColor?: string;
|
|
12
|
+
animate?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
class: className = 'w-8 h-10',
|
|
17
|
+
seedColor,
|
|
18
|
+
animate = true
|
|
19
|
+
}: Props = $props();
|
|
20
|
+
|
|
21
|
+
const seed = $derived(seedColor ?? natural.cream);
|
|
22
|
+
const stem = $derived(greens.deepGreen);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<!-- Dandelion seed head (puff) -->
|
|
26
|
+
<svg class={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 80">
|
|
27
|
+
<!-- Stem -->
|
|
28
|
+
<path fill="none" stroke={stem} stroke-width="2" d="M30 80 Q28 60 30 45" />
|
|
29
|
+
|
|
30
|
+
<!-- Central seed head -->
|
|
31
|
+
<circle fill={seed} cx="30" cy="30" r="8" opacity="0.5" />
|
|
32
|
+
|
|
33
|
+
<!-- Seeds with parachutes radiating outward -->
|
|
34
|
+
<!-- Top seeds -->
|
|
35
|
+
<g class={animate ? 'drift drift-1' : ''}>
|
|
36
|
+
<path fill={seed} d="M30 5 L28 12 L30 10 L32 12 Z" opacity="0.7" />
|
|
37
|
+
<line x1="30" y1="10" x2="30" y2="22" stroke={seed} stroke-width="0.5" opacity="0.5" />
|
|
38
|
+
</g>
|
|
39
|
+
|
|
40
|
+
<g class={animate ? 'drift drift-2' : ''}>
|
|
41
|
+
<path fill={seed} d="M45 10 L40 15 L42 14 L42 17 Z" opacity="0.7" transform="rotate(30 45 10)" />
|
|
42
|
+
<line x1="42" y1="14" x2="35" y2="24" stroke={seed} stroke-width="0.5" opacity="0.5" />
|
|
43
|
+
</g>
|
|
44
|
+
|
|
45
|
+
<g class={animate ? 'drift drift-3' : ''}>
|
|
46
|
+
<path fill={seed} d="M15 10 L20 15 L18 14 L18 17 Z" opacity="0.7" transform="rotate(-30 15 10)" />
|
|
47
|
+
<line x1="18" y1="14" x2="25" y2="24" stroke={seed} stroke-width="0.5" opacity="0.5" />
|
|
48
|
+
</g>
|
|
49
|
+
|
|
50
|
+
<!-- Side seeds -->
|
|
51
|
+
<g class={animate ? 'drift drift-4' : ''}>
|
|
52
|
+
<path fill={seed} d="M55 25 L48 28 L50 27 L49 30 Z" opacity="0.6" />
|
|
53
|
+
<line x1="49" y1="28" x2="38" y2="28" stroke={seed} stroke-width="0.5" opacity="0.5" />
|
|
54
|
+
</g>
|
|
55
|
+
|
|
56
|
+
<g class={animate ? 'drift drift-5' : ''}>
|
|
57
|
+
<path fill={seed} d="M5 25 L12 28 L10 27 L11 30 Z" opacity="0.6" />
|
|
58
|
+
<line x1="11" y1="28" x2="22" y2="28" stroke={seed} stroke-width="0.5" opacity="0.5" />
|
|
59
|
+
</g>
|
|
60
|
+
|
|
61
|
+
<!-- Lower seeds -->
|
|
62
|
+
<g class={animate ? 'drift drift-6' : ''}>
|
|
63
|
+
<path fill={seed} d="M50 40 L44 40 L46 38 L45 42 Z" opacity="0.6" />
|
|
64
|
+
<line x1="45" y1="40" x2="38" y2="35" stroke={seed} stroke-width="0.5" opacity="0.5" />
|
|
65
|
+
</g>
|
|
66
|
+
|
|
67
|
+
<g class={animate ? 'drift drift-7' : ''}>
|
|
68
|
+
<path fill={seed} d="M10 40 L16 40 L14 38 L15 42 Z" opacity="0.6" />
|
|
69
|
+
<line x1="15" y1="40" x2="22" y2="35" stroke={seed} stroke-width="0.5" opacity="0.5" />
|
|
70
|
+
</g>
|
|
71
|
+
|
|
72
|
+
<!-- Central dense seeds -->
|
|
73
|
+
<circle fill={seed} cx="30" cy="22" r="2" opacity="0.8" />
|
|
74
|
+
<circle fill={seed} cx="25" cy="26" r="2" opacity="0.7" />
|
|
75
|
+
<circle fill={seed} cx="35" cy="26" r="2" opacity="0.7" />
|
|
76
|
+
<circle fill={seed} cx="30" cy="32" r="2" opacity="0.6" />
|
|
77
|
+
<circle fill={seed} cx="24" cy="34" r="1.5" opacity="0.5" />
|
|
78
|
+
<circle fill={seed} cx="36" cy="34" r="1.5" opacity="0.5" />
|
|
79
|
+
</svg>
|
|
80
|
+
|
|
81
|
+
<style>
|
|
82
|
+
@keyframes drift {
|
|
83
|
+
0%, 100% { transform: translate(0, 0); }
|
|
84
|
+
50% { transform: translate(var(--drift-x, 2px), var(--drift-y, -2px)); }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.drift {
|
|
88
|
+
animation: drift 3s ease-in-out infinite;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.drift-1 { --drift-x: 0px; --drift-y: -3px; animation-delay: 0s; }
|
|
92
|
+
.drift-2 { --drift-x: 3px; --drift-y: -2px; animation-delay: 0.3s; }
|
|
93
|
+
.drift-3 { --drift-x: -3px; --drift-y: -2px; animation-delay: 0.6s; }
|
|
94
|
+
.drift-4 { --drift-x: 4px; --drift-y: 0px; animation-delay: 0.2s; }
|
|
95
|
+
.drift-5 { --drift-x: -4px; --drift-y: 0px; animation-delay: 0.5s; }
|
|
96
|
+
.drift-6 { --drift-x: 3px; --drift-y: 2px; animation-delay: 0.4s; }
|
|
97
|
+
.drift-7 { --drift-x: -3px; --drift-y: 2px; animation-delay: 0.7s; }
|
|
98
|
+
</style>
|
|
@@ -0,0 +1,170 @@
|
|
|
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 LeafFalling from './LeafFalling.svelte';
|
|
9
|
+
|
|
10
|
+
type TreeType = 'logo' | 'pine' | 'aspen' | 'birch' | 'cherry';
|
|
11
|
+
type LeafVariant = 'simple' | 'maple' | 'cherry' | 'aspen' | 'pine';
|
|
12
|
+
|
|
13
|
+
// Animation constants (defaults)
|
|
14
|
+
const DEFAULT_LEAF_OPACITY = { min: 0.4, max: 0.75 } as const;
|
|
15
|
+
const DEFAULT_FALL_DURATION = { min: 8, max: 14 } as const;
|
|
16
|
+
const DEFAULT_FALL_DISTANCE = { min: 12, max: 20 } as const;
|
|
17
|
+
const DEFAULT_DRIFT_RANGE = 60; // -30 to +30
|
|
18
|
+
const DEFAULT_SPAWN_DELAY_MAX = 15;
|
|
19
|
+
|
|
20
|
+
interface Tree {
|
|
21
|
+
id: number;
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
size: number;
|
|
25
|
+
treeType: TreeType;
|
|
26
|
+
zIndex?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface FallingLeaf {
|
|
30
|
+
id: number;
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
size: number;
|
|
34
|
+
variant: LeafVariant;
|
|
35
|
+
duration: number;
|
|
36
|
+
delay: number;
|
|
37
|
+
drift: number;
|
|
38
|
+
opacity: number;
|
|
39
|
+
fallDistance: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface Props {
|
|
43
|
+
trees: Tree[];
|
|
44
|
+
season?: Season;
|
|
45
|
+
/** Minimum leaves per tree */
|
|
46
|
+
minLeavesPerTree?: number;
|
|
47
|
+
/** Maximum leaves per tree */
|
|
48
|
+
maxLeavesPerTree?: number;
|
|
49
|
+
/** Base z-index for the leaf layer (should be below trees) */
|
|
50
|
+
zIndex?: number;
|
|
51
|
+
/** Override fall distance range (in vh units) - useful for tall sections */
|
|
52
|
+
fallDistance?: { min: number; max: number };
|
|
53
|
+
/** Override fall duration range (in seconds) */
|
|
54
|
+
fallDuration?: { min: number; max: number };
|
|
55
|
+
/** Maximum spawn delay (in seconds) */
|
|
56
|
+
spawnDelayMax?: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let {
|
|
60
|
+
trees,
|
|
61
|
+
season = 'summer',
|
|
62
|
+
minLeavesPerTree = 2,
|
|
63
|
+
maxLeavesPerTree = 5,
|
|
64
|
+
zIndex = -1,
|
|
65
|
+
fallDistance = DEFAULT_FALL_DISTANCE,
|
|
66
|
+
fallDuration = DEFAULT_FALL_DURATION,
|
|
67
|
+
spawnDelayMax = DEFAULT_SPAWN_DELAY_MAX
|
|
68
|
+
}: Props = $props();
|
|
69
|
+
|
|
70
|
+
// Deterministic hash for pseudo-random distribution (avoids visible patterns)
|
|
71
|
+
function hashSeed(seed: number): number {
|
|
72
|
+
return Math.abs(Math.sin(seed * 12.9898) * 43758.5453);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Map tree types to appropriate leaf variants (deterministic based on leaf id)
|
|
76
|
+
function getLeafVariant(treeType: TreeType, leafId: number): LeafVariant {
|
|
77
|
+
switch (treeType) {
|
|
78
|
+
case 'cherry':
|
|
79
|
+
return 'cherry';
|
|
80
|
+
case 'aspen':
|
|
81
|
+
case 'birch':
|
|
82
|
+
return 'aspen';
|
|
83
|
+
case 'pine':
|
|
84
|
+
return 'pine';
|
|
85
|
+
case 'logo':
|
|
86
|
+
default:
|
|
87
|
+
// Logo and default get a mix of simple and maple (deterministic with natural distribution)
|
|
88
|
+
return Math.floor(hashSeed(leafId)) % 2 === 0 ? 'simple' : 'maple';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Generate falling leaves based on tree positions
|
|
93
|
+
function generateLeaves(treesData: Tree[]): FallingLeaf[] {
|
|
94
|
+
const leaves: FallingLeaf[] = [];
|
|
95
|
+
let leafId = 0;
|
|
96
|
+
|
|
97
|
+
for (const tree of treesData) {
|
|
98
|
+
// Random number of leaves per tree within the configured range
|
|
99
|
+
const baseLeafCount = minLeavesPerTree + Math.floor(Math.random() * (maxLeavesPerTree - minLeavesPerTree + 1));
|
|
100
|
+
|
|
101
|
+
// More leaves for bigger/closer trees, fewer for distant ones
|
|
102
|
+
const treeDepth = tree.zIndex ?? 1;
|
|
103
|
+
const depthMultiplier = 0.5 + (treeDepth / 5) * 0.8; // 0.5x for far trees, up to 1.3x for close ones
|
|
104
|
+
const leafCount = Math.ceil(baseLeafCount * depthMultiplier);
|
|
105
|
+
|
|
106
|
+
// Scale leaf size based on tree size (bigger trees = bigger leaves for perspective)
|
|
107
|
+
const treeSizeFactor = tree.size / 100; // Normalize around 100px tree size
|
|
108
|
+
const baseLeafSize = 6 + treeSizeFactor * 8; // 6-14px base depending on tree size
|
|
109
|
+
const leafSizeVariation = 6 + treeSizeFactor * 4; // Additional random variation
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < leafCount; i++) {
|
|
112
|
+
// Spawn leaves at or above tree canopy for better falling motion
|
|
113
|
+
// Leaves will animate downward from their spawn point
|
|
114
|
+
const xOffset = (Math.random() - 0.5) * (tree.size / 8); // Horizontal spread based on tree size
|
|
115
|
+
// Vertical variation: spawn leaves slightly above to at tree position
|
|
116
|
+
const yOffset = -2 - Math.random() * 3; // -2% to -5% above tree
|
|
117
|
+
|
|
118
|
+
const currentLeafId = leafId++;
|
|
119
|
+
leaves.push({
|
|
120
|
+
id: currentLeafId,
|
|
121
|
+
x: tree.x + xOffset,
|
|
122
|
+
y: Math.max(0, tree.y + yOffset), // Clamp to not go above viewport
|
|
123
|
+
size: baseLeafSize + Math.random() * leafSizeVariation,
|
|
124
|
+
variant: getLeafVariant(tree.treeType, currentLeafId),
|
|
125
|
+
duration: fallDuration.min + Math.random() * (fallDuration.max - fallDuration.min),
|
|
126
|
+
delay: Math.random() * spawnDelayMax,
|
|
127
|
+
drift: (Math.random() - 0.5) * DEFAULT_DRIFT_RANGE,
|
|
128
|
+
opacity: DEFAULT_LEAF_OPACITY.min + Math.random() * (DEFAULT_LEAF_OPACITY.max - DEFAULT_LEAF_OPACITY.min),
|
|
129
|
+
fallDistance: fallDistance.min + Math.random() * (fallDistance.max - fallDistance.min)
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return leaves;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Reactive leaves - regenerates when trees or season changes
|
|
138
|
+
let fallingLeaves = $derived(generateLeaves(trees));
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<!-- Falling leaves layer - positioned behind trees -->
|
|
142
|
+
<div
|
|
143
|
+
class="absolute inset-0 pointer-events-none overflow-hidden"
|
|
144
|
+
style="z-index: {zIndex};"
|
|
145
|
+
>
|
|
146
|
+
{#each fallingLeaves as leaf (leaf.id)}
|
|
147
|
+
<div
|
|
148
|
+
class="absolute"
|
|
149
|
+
style="
|
|
150
|
+
left: {leaf.x}%;
|
|
151
|
+
top: {leaf.y}%;
|
|
152
|
+
opacity: {leaf.opacity};
|
|
153
|
+
width: {leaf.size}px;
|
|
154
|
+
height: {leaf.size}px;
|
|
155
|
+
"
|
|
156
|
+
>
|
|
157
|
+
<LeafFalling
|
|
158
|
+
class="w-full h-full"
|
|
159
|
+
variant={leaf.variant}
|
|
160
|
+
{season}
|
|
161
|
+
duration={leaf.duration}
|
|
162
|
+
delay={leaf.delay}
|
|
163
|
+
drift={leaf.drift}
|
|
164
|
+
fallDistance={leaf.fallDistance}
|
|
165
|
+
seed={leaf.id}
|
|
166
|
+
animate={true}
|
|
167
|
+
/>
|
|
168
|
+
</div>
|
|
169
|
+
{/each}
|
|
170
|
+
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Season } from '../palette';
|
|
2
|
+
type TreeType = 'logo' | 'pine' | 'aspen' | 'birch' | 'cherry';
|
|
3
|
+
interface Tree {
|
|
4
|
+
id: number;
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
size: number;
|
|
8
|
+
treeType: TreeType;
|
|
9
|
+
zIndex?: number;
|
|
10
|
+
}
|
|
11
|
+
interface Props {
|
|
12
|
+
trees: Tree[];
|
|
13
|
+
season?: Season;
|
|
14
|
+
/** Minimum leaves per tree */
|
|
15
|
+
minLeavesPerTree?: number;
|
|
16
|
+
/** Maximum leaves per tree */
|
|
17
|
+
maxLeavesPerTree?: number;
|
|
18
|
+
/** Base z-index for the leaf layer (should be below trees) */
|
|
19
|
+
zIndex?: number;
|
|
20
|
+
/** Override fall distance range (in vh units) - useful for tall sections */
|
|
21
|
+
fallDistance?: {
|
|
22
|
+
min: number;
|
|
23
|
+
max: number;
|
|
24
|
+
};
|
|
25
|
+
/** Override fall duration range (in seconds) */
|
|
26
|
+
fallDuration?: {
|
|
27
|
+
min: number;
|
|
28
|
+
max: number;
|
|
29
|
+
};
|
|
30
|
+
/** Maximum spawn delay (in seconds) */
|
|
31
|
+
spawnDelayMax?: number;
|
|
32
|
+
}
|
|
33
|
+
declare const FallingLeavesLayer: import("svelte").Component<Props, {}, "">;
|
|
34
|
+
type FallingLeavesLayer = ReturnType<typeof FallingLeavesLayer>;
|
|
35
|
+
export default FallingLeavesLayer;
|
|
@@ -0,0 +1,174 @@
|
|
|
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 { browser } from '$app/environment';
|
|
8
|
+
import PetalFalling from './PetalFalling.svelte';
|
|
9
|
+
|
|
10
|
+
type PetalVariant = 'round' | 'pointed' | 'heart' | 'curled' | 'tiny';
|
|
11
|
+
|
|
12
|
+
// Check for reduced motion preference
|
|
13
|
+
const prefersReducedMotion = browser
|
|
14
|
+
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
15
|
+
: false;
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
/** Total number of petals */
|
|
19
|
+
count?: number;
|
|
20
|
+
/** Base z-index for the petal layer */
|
|
21
|
+
zIndex?: number;
|
|
22
|
+
/** Enable petal animation */
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
/** Opacity range for petals (depth affects final value) */
|
|
25
|
+
opacity?: { min: number; max: number };
|
|
26
|
+
/** Fall duration range in seconds (slower = more dreamy) */
|
|
27
|
+
fallDuration?: { min: number; max: number };
|
|
28
|
+
/** Horizontal drift range in pixels (petals flutter more than snow) */
|
|
29
|
+
driftRange?: number;
|
|
30
|
+
/** Maximum spawn delay in seconds */
|
|
31
|
+
spawnDelay?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
count = 80,
|
|
36
|
+
zIndex = 50,
|
|
37
|
+
enabled = true,
|
|
38
|
+
opacity = { min: 0.5, max: 0.9 },
|
|
39
|
+
fallDuration = { min: 12, max: 20 },
|
|
40
|
+
driftRange = 80,
|
|
41
|
+
spawnDelay = 15
|
|
42
|
+
}: Props = $props();
|
|
43
|
+
|
|
44
|
+
// Petals fall gently and drift more than snow
|
|
45
|
+
// Increased distance to ensure they traverse the full viewport
|
|
46
|
+
const FALL_DISTANCE = { min: 120, max: 150 } as const;
|
|
47
|
+
|
|
48
|
+
// Reduce petal count for reduced motion
|
|
49
|
+
const actualCount = $derived(prefersReducedMotion ? Math.floor(count / 4) : count);
|
|
50
|
+
|
|
51
|
+
// Variant distribution - more round and heart shapes for cherry blossoms
|
|
52
|
+
const petalVariants: PetalVariant[] = ['round', 'round', 'pointed', 'heart', 'curled', 'tiny'];
|
|
53
|
+
|
|
54
|
+
interface Petal {
|
|
55
|
+
id: number;
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
size: number;
|
|
59
|
+
variant: PetalVariant;
|
|
60
|
+
duration: number;
|
|
61
|
+
delay: number;
|
|
62
|
+
drift: number;
|
|
63
|
+
opacity: number;
|
|
64
|
+
fallDistance: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Deterministic hash for natural distribution
|
|
68
|
+
function hashRandom(seed: number): number {
|
|
69
|
+
const hash = Math.abs(Math.sin(seed * 12.9898) * 43758.5453);
|
|
70
|
+
return hash - Math.floor(hash);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Generate petals across the viewport
|
|
74
|
+
function generatePetals(): Petal[] {
|
|
75
|
+
const petals: Petal[] = [];
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < actualCount; i++) {
|
|
78
|
+
// Prime number multipliers ensure uncorrelated random distributions
|
|
79
|
+
// across different properties (avoids visible patterns in petal placement)
|
|
80
|
+
const xRand = hashRandom(i * 7);
|
|
81
|
+
const yRand = hashRandom(i * 11);
|
|
82
|
+
const depthRand = hashRandom(i * 13);
|
|
83
|
+
const durationRand = hashRandom(i * 17);
|
|
84
|
+
const delayRand = hashRandom(i * 19);
|
|
85
|
+
const driftRand = hashRandom(i * 23);
|
|
86
|
+
const distanceRand = hashRandom(i * 29);
|
|
87
|
+
const variantRand = hashRandom(i * 31);
|
|
88
|
+
|
|
89
|
+
// Distribute across full width
|
|
90
|
+
const x = (i / actualCount) * 100 + (xRand - 0.5) * 15;
|
|
91
|
+
|
|
92
|
+
// Start positions: Mix above viewport and within for continuous rain effect
|
|
93
|
+
// 50% start above for "raining from sky", 50% within for immediate visibility
|
|
94
|
+
const y = yRand < 0.5
|
|
95
|
+
? -5 - yRand * 15 // Above viewport: -5% to -20%
|
|
96
|
+
: yRand * 40; // Within viewport: 0% to 40%
|
|
97
|
+
|
|
98
|
+
// Depth-based sizing:
|
|
99
|
+
// Far petals are smaller and simpler
|
|
100
|
+
// Close petals are larger and more detailed
|
|
101
|
+
const depthFactor = depthRand;
|
|
102
|
+
const size = 10 + depthFactor * 18; // 10-28px - petals are delicate
|
|
103
|
+
|
|
104
|
+
// Variant based on depth
|
|
105
|
+
let variant: PetalVariant;
|
|
106
|
+
if (depthFactor < 0.25) {
|
|
107
|
+
variant = 'tiny';
|
|
108
|
+
} else {
|
|
109
|
+
const variantIndex = Math.floor(variantRand * (petalVariants.length - 1));
|
|
110
|
+
variant = petalVariants[variantIndex];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Since all petals now start above viewport, stagger delays for continuous rain
|
|
114
|
+
// Shorter delays create more immediate visual interest
|
|
115
|
+
const actualDelay = delayRand * spawnDelay * 0.6; // Reduce overall delay spread
|
|
116
|
+
|
|
117
|
+
// Petals drift more erratically than snow - flutter in the breeze
|
|
118
|
+
// Use sine wave for more organic drift pattern
|
|
119
|
+
const baseDrift = (driftRand - 0.5) * driftRange;
|
|
120
|
+
const driftVariation = Math.sin(i * 0.7) * 20;
|
|
121
|
+
|
|
122
|
+
petals.push({
|
|
123
|
+
id: i,
|
|
124
|
+
x,
|
|
125
|
+
y,
|
|
126
|
+
size,
|
|
127
|
+
variant,
|
|
128
|
+
duration: fallDuration.min + durationRand * (fallDuration.max - fallDuration.min),
|
|
129
|
+
delay: actualDelay,
|
|
130
|
+
drift: baseDrift + driftVariation,
|
|
131
|
+
opacity: opacity.min + depthFactor * (opacity.max - opacity.min),
|
|
132
|
+
fallDistance: FALL_DISTANCE.min + distanceRand * (FALL_DISTANCE.max - FALL_DISTANCE.min)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return petals;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Generate petals once
|
|
140
|
+
const petals = $derived(generatePetals());
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
{#if enabled}
|
|
144
|
+
<!-- Falling petals layer - cherry blossoms drifting on spring breeze -->
|
|
145
|
+
<div
|
|
146
|
+
class="absolute inset-0 pointer-events-none"
|
|
147
|
+
style="z-index: {zIndex};"
|
|
148
|
+
aria-hidden="true"
|
|
149
|
+
>
|
|
150
|
+
{#each petals as petal (petal.id)}
|
|
151
|
+
<div
|
|
152
|
+
class="absolute"
|
|
153
|
+
style="
|
|
154
|
+
left: {petal.x}%;
|
|
155
|
+
top: {petal.y}%;
|
|
156
|
+
width: {petal.size}px;
|
|
157
|
+
height: {petal.size}px;
|
|
158
|
+
"
|
|
159
|
+
>
|
|
160
|
+
<PetalFalling
|
|
161
|
+
class="w-full h-full"
|
|
162
|
+
variant={petal.variant}
|
|
163
|
+
duration={petal.duration}
|
|
164
|
+
delay={petal.delay}
|
|
165
|
+
drift={petal.drift}
|
|
166
|
+
fallDistance={petal.fallDistance}
|
|
167
|
+
opacity={petal.opacity}
|
|
168
|
+
seed={petal.id}
|
|
169
|
+
animate={true}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
{/each}
|
|
173
|
+
</div>
|
|
174
|
+
{/if}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Total number of petals */
|
|
3
|
+
count?: number;
|
|
4
|
+
/** Base z-index for the petal layer */
|
|
5
|
+
zIndex?: number;
|
|
6
|
+
/** Enable petal animation */
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
/** Opacity range for petals (depth affects final value) */
|
|
9
|
+
opacity?: {
|
|
10
|
+
min: number;
|
|
11
|
+
max: number;
|
|
12
|
+
};
|
|
13
|
+
/** Fall duration range in seconds (slower = more dreamy) */
|
|
14
|
+
fallDuration?: {
|
|
15
|
+
min: number;
|
|
16
|
+
max: number;
|
|
17
|
+
};
|
|
18
|
+
/** Horizontal drift range in pixels (petals flutter more than snow) */
|
|
19
|
+
driftRange?: number;
|
|
20
|
+
/** Maximum spawn delay in seconds */
|
|
21
|
+
spawnDelay?: number;
|
|
22
|
+
}
|
|
23
|
+
declare const FallingPetalsLayer: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type FallingPetalsLayer = ReturnType<typeof FallingPetalsLayer>;
|
|
25
|
+
export default FallingPetalsLayer;
|