@homefile/components-v2 2.57.0 → 2.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,9 @@
15
15
  "terms": "Criando uma conta Homefile você concorda com os nossos ",
16
16
  "signupBt": "Criar conta",
17
17
  "signin": "Já possui uma conta Homefile?",
18
- "signinBt": "Log in"
18
+ "signinBt": "Log in",
19
+ "continueGoogle": "Continuar com o Google",
20
+ "or": "ou"
19
21
  },
20
22
  "partner": {
21
23
  "wizardSteps": {
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Birds — self-contained animated flock overlay for the onboarding sky.
3
+ *
4
+ * Renders SVG birds onto a fixed, non-interactive layer that sits over the
5
+ * CloudsAnimation sky (CloudsAnimation supplies the gradient, clouds and trees).
6
+ * Two archetypes: small fast "flappers" (short wings, quick eased beat) ride low;
7
+ * wide-winged "soarers" ride high and slow. Flight paths curve, wingbeats are
8
+ * eased (fast down / slow up with a glide hold), and flight undulates — flap to
9
+ * climb, glide to sink. Honors prefers-reduced-motion.
10
+ */
11
+ export declare const Birds: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,196 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ /**
4
+ * Birds — self-contained animated flock overlay for the onboarding sky.
5
+ *
6
+ * Renders SVG birds onto a fixed, non-interactive layer that sits over the
7
+ * CloudsAnimation sky (CloudsAnimation supplies the gradient, clouds and trees).
8
+ * Two archetypes: small fast "flappers" (short wings, quick eased beat) ride low;
9
+ * wide-winged "soarers" ride high and slow. Flight paths curve, wingbeats are
10
+ * eased (fast down / slow up with a glide hold), and flight undulates — flap to
11
+ * climb, glide to sink. Honors prefers-reduced-motion.
12
+ */
13
+ export const Birds = () => {
14
+ const layerRef = useRef(null);
15
+ useEffect(() => {
16
+ const layer = layerRef.current;
17
+ if (!layer)
18
+ return;
19
+ const NS = 'http://www.w3.org/2000/svg';
20
+ const rand = (a, b) => a + Math.random() * (b - a);
21
+ const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
22
+ const f = (n) => +(+n).toFixed(2);
23
+ // Eased wing-flap splines: fast down-stroke, slow up-stroke, then a held glide.
24
+ const S_DOWN = '0.2 0.75 0.35 1';
25
+ const S_UP = '0.4 0 0.28 1';
26
+ const S_GIN = '0.35 0 0.6 1';
27
+ const S_HOLD = '0.5 0 0.5 1';
28
+ const S_GOUT = '0.45 0 0.5 1';
29
+ const buildFlap = (nBeats, glideFrac, vUp, vDown, vGlide) => {
30
+ const flap = 1 - glideFrac;
31
+ const seg = flap / (nBeats * 2);
32
+ const values = [vUp];
33
+ const times = [0];
34
+ const splines = [];
35
+ let t = 0;
36
+ for (let b = 0; b < nBeats; b++) {
37
+ t += seg;
38
+ values.push(vDown);
39
+ times.push(t);
40
+ splines.push(S_DOWN);
41
+ t += seg;
42
+ values.push(vUp);
43
+ times.push(t);
44
+ splines.push(S_UP);
45
+ }
46
+ const gin = flap + (1 - flap) * 0.25;
47
+ const hold = gin + (1 - gin) * 0.5;
48
+ values.push(vGlide);
49
+ times.push(gin);
50
+ splines.push(S_GIN);
51
+ values.push(vGlide);
52
+ times.push(hold);
53
+ splines.push(S_HOLD);
54
+ values.push(vUp);
55
+ times.push(1);
56
+ splines.push(S_GOUT);
57
+ return {
58
+ values: values.join('; '),
59
+ keyTimes: times.map(f).join('; '),
60
+ keySplines: splines.join('; '),
61
+ };
62
+ };
63
+ const makeBird = (W, H) => {
64
+ const depth = Math.random(); // haze/opacity/blur depth (independent of size)
65
+ const soarer = Math.random() < 0.42;
66
+ const size = soarer ? rand(22, 46) : rand(9, 22);
67
+ const driftSpeed = (soarer ? rand(30, 52) : rand(13, 30)) + rand(-3, 3);
68
+ const nBeats = soarer ? (Math.random() < 0.5 ? 1 : 2) : Math.round(rand(3, 6));
69
+ const glideFrac = soarer ? rand(0.55, 0.8) : rand(0.04, 0.2);
70
+ const P = soarer ? rand(3.4, 5.4) : rand(0.5, 0.95); // flappers beat much quicker
71
+ // flapper bounce dampened (was 7–17) so a couple don't over-bob on small bodies
72
+ const boundAmp = (soarer ? rand(3, 8) : rand(6, 11)) * (0.6 + depth * 0.7);
73
+ const wingAmp = soarer ? rand(0.55, 0.9) : rand(0.9, 1.4);
74
+ const upTip = f(15 - 9 * wingAmp);
75
+ const upBody = f(15 + 2 * wingAmp);
76
+ const dnTip = f(15 + 5 * wingAmp);
77
+ const dnBody = f(15 - 1.5 * wingAmp);
78
+ const glTip = f(15 - 3 * wingAmp);
79
+ // small fast birds get shorter (stubbier) wingspans
80
+ const wx0 = soarer ? 5 : 10;
81
+ const wx1 = soarer ? 35 : 30;
82
+ const vUp = `M ${wx0},${upTip} L 20,${upBody} L ${wx1},${upTip}`;
83
+ const vDown = `M ${wx0},${dnTip} L 20,${dnBody} L ${wx1},${dnTip}`;
84
+ const vGlide = `M ${wx0},${glTip} L 20,16 L ${wx1},${glTip}`;
85
+ const flapAnim = buildFlap(nBeats, glideFrac, vUp, vDown, vGlide);
86
+ const phase = -rand(0, P);
87
+ const ltr = Math.random() < 0.6;
88
+ const x0 = ltr ? -80 : W + 80;
89
+ const x1 = ltr ? W + 80 : -80;
90
+ let pathStr;
91
+ if (soarer) {
92
+ // soarers ride thermals: glide in, circle once (a banking turn), drift out
93
+ const cx = rand(0.25, 0.75) * W;
94
+ const cy = rand(0.06, 0.3) * H; // high band
95
+ const r = rand(45, 95);
96
+ const k = 0.5523 * r; // cubic-bezier circle constant
97
+ const eY = clamp(cy + rand(-0.12, 0.12) * H, 0.03 * H, 0.4 * H);
98
+ const xY = clamp(cy + rand(-0.12, 0.12) * H, 0.03 * H, 0.4 * H);
99
+ const rx = cx + r; // circle entry point (right of center)
100
+ const loop = `C ${f(x0 + (rx - x0) * 0.5)},${f(eY)} ${f(rx)},${f(cy - r)} ${f(rx)},${f(cy)} ` + // glide in to circle
101
+ `C ${f(rx)},${f(cy - k)} ${f(cx + k)},${f(cy - r)} ${f(cx)},${f(cy - r)} ` + // right → top
102
+ `C ${f(cx - k)},${f(cy - r)} ${f(cx - r)},${f(cy - k)} ${f(cx - r)},${f(cy)} ` + // top → left
103
+ `C ${f(cx - r)},${f(cy + k)} ${f(cx - k)},${f(cy + r)} ${f(cx)},${f(cy + r)} ` + // left → bottom
104
+ `C ${f(cx + k)},${f(cy + r)} ${f(rx)},${f(cy + k)} ${f(rx)},${f(cy)} ` + // bottom → right (loop closed)
105
+ `C ${f(rx)},${f(cy - r)} ${f(x1 + (rx - x1) * 0.5)},${f(xY)} ${f(x1)},${f(xY)}`; // drift out
106
+ pathStr = `path("M ${f(x0)},${f(eY)} ${loop}")`;
107
+ }
108
+ else {
109
+ const y0 = rand(0.42, 0.8) * H; // flappers low
110
+ const y1 = clamp(y0 + rand(-0.2, 0.2) * H, 0.03 * H, 0.86 * H);
111
+ const cx1 = x0 + (x1 - x0) * rand(0.2, 0.42);
112
+ const cy1 = clamp(y0 + rand(-0.32, 0.32) * H, 0, H);
113
+ const cx2 = x0 + (x1 - x0) * rand(0.6, 0.82);
114
+ const cy2 = clamp(y1 + rand(-0.32, 0.32) * H, 0, H);
115
+ pathStr = `path("M ${f(x0)},${f(y0)} C ${f(cx1)},${f(cy1)} ${f(cx2)},${f(cy2)} ${f(x1)},${f(y1)}")`;
116
+ }
117
+ const bird = document.createElement('div');
118
+ bird.className = 'hf-bird';
119
+ bird.style.width = `${size}px`;
120
+ bird.style.height = `${f(size * 0.77)}px`;
121
+ bird.style.offsetPath = pathStr;
122
+ bird.style.animationDuration = `${driftSpeed}s`;
123
+ bird.style.animationDelay = `${-rand(0, driftSpeed)}s`;
124
+ // darker/more-present background birds: higher opacity floor + lighter haze so the far ones don't disappear
125
+ bird.style.opacity = String(f(0.34 + depth * 0.54));
126
+ bird.style.zIndex = String(Math.round(depth * 10));
127
+ if (depth < 0.32)
128
+ bird.style.filter = `blur(${((0.32 - depth) * 2).toFixed(1)}px)`;
129
+ const bob = document.createElement('div');
130
+ bob.className = 'hf-bob';
131
+ bob.style.setProperty('--p', `${P}s`);
132
+ bob.style.setProperty('--amp', `${f(boundAmp)}px`);
133
+ bob.style.animationDelay = `${phase}s`;
134
+ const svg = document.createElementNS(NS, 'svg');
135
+ svg.setAttribute('viewBox', '0 0 40 30');
136
+ svg.setAttribute('fill', 'none');
137
+ svg.setAttribute('stroke', '#4e2d68');
138
+ svg.setAttribute('stroke-width', soarer ? '2.4' : '3');
139
+ svg.setAttribute('stroke-linecap', 'round');
140
+ svg.setAttribute('stroke-linejoin', 'round');
141
+ const path = document.createElementNS(NS, 'path');
142
+ path.setAttribute('d', vUp);
143
+ const anim = document.createElementNS(NS, 'animate');
144
+ anim.setAttribute('attributeName', 'd');
145
+ anim.setAttribute('dur', `${P}s`);
146
+ anim.setAttribute('repeatCount', 'indefinite');
147
+ anim.setAttribute('calcMode', 'spline');
148
+ anim.setAttribute('values', flapAnim.values);
149
+ anim.setAttribute('keyTimes', flapAnim.keyTimes);
150
+ anim.setAttribute('keySplines', flapAnim.keySplines);
151
+ anim.setAttribute('begin', `${phase}s`);
152
+ path.appendChild(anim);
153
+ svg.appendChild(path);
154
+ bob.appendChild(svg);
155
+ bird.appendChild(bob);
156
+ return bird;
157
+ };
158
+ const makeFlock = () => {
159
+ layer.innerHTML = '';
160
+ const W = window.innerWidth;
161
+ const H = window.innerHeight;
162
+ // a few at a time, not a constant flock — sparse, so they come and go
163
+ const count = Math.round(clamp(W / 340, 3, 6));
164
+ for (let i = 0; i < count; i++)
165
+ layer.appendChild(makeBird(W, H));
166
+ };
167
+ makeFlock();
168
+ let rt;
169
+ const onResize = () => {
170
+ clearTimeout(rt);
171
+ rt = setTimeout(makeFlock, 300);
172
+ };
173
+ window.addEventListener('resize', onResize);
174
+ return () => {
175
+ window.removeEventListener('resize', onResize);
176
+ clearTimeout(rt);
177
+ layer.innerHTML = '';
178
+ };
179
+ }, []);
180
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: BIRDS_CSS }), _jsx("div", { className: "hf-birds", ref: layerRef, "aria-hidden": "true" })] }));
181
+ };
182
+ const BIRDS_CSS = `
183
+ .hf-birds{position:fixed; inset:0; z-index:1; pointer-events:none; overflow:hidden;}
184
+ .hf-bird{position:absolute; top:0; left:0; will-change:offset-distance; offset-rotate:0deg;
185
+ animation-name:hf-glide-path; animation-timing-function:linear; animation-iteration-count:infinite;}
186
+ .hf-bird svg{display:block; width:100%; height:100%;}
187
+ @keyframes hf-glide-path{ from{offset-distance:0%;} to{offset-distance:100%;} }
188
+ .hf-bob{width:100%; height:100%; will-change:transform; animation-name:hf-bound;
189
+ animation-duration:var(--p,3s); animation-iteration-count:infinite;}
190
+ @keyframes hf-bound{
191
+ 0%{transform:translateY(0); animation-timing-function:cubic-bezier(.25,.7,.35,1);}
192
+ 40%{transform:translateY(calc(-1 * var(--amp,10px))); animation-timing-function:cubic-bezier(.55,0,.75,1);}
193
+ 100%{transform:translateY(0);}
194
+ }
195
+ @media (prefers-reduced-motion:reduce){ .hf-bird,.hf-bob{animation:none !important;} }
196
+ `;
@@ -1,5 +1,4 @@
1
1
  import { PropsWithChildren } from 'react';
2
- export declare const moveLeftToRightAirplane: import("@emotion/serialize").Keyframes;
3
2
  export declare const moveRightToLeft: import("@emotion/serialize").Keyframes;
4
3
  export declare const moveRightToLeftWithFade: import("@emotion/serialize").Keyframes;
5
4
  export declare const moveRightToLeftWithFade2: import("@emotion/serialize").Keyframes;
@@ -1,20 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Fragment, useEffect, useState } from 'react';
3
- import { Airplane, Cloud1, Cloud2, Cloud3 } from '../../assets/images';
2
+ import { useEffect, useState } from 'react';
3
+ import { Cloud1, Cloud2, Cloud3 } from '../../assets/images';
4
4
  import { useWindowDimensions } from '../../hooks';
5
5
  import { colors } from '../../theme/colors';
6
- import { getTimeOfDay, getTimeOfDaySeason, } from '../../utils';
6
+ import { getTimeOfDay, } from '../../utils';
7
7
  import { Box, Image } from '@chakra-ui/react';
8
8
  import { keyframes } from '@emotion/react';
9
9
  import { WeatherTrees } from './WeatherTrees';
10
- export const moveLeftToRightAirplane = keyframes `
11
- 0% {
12
- transform: translateX(-100%);
13
- }
14
- 100% {
15
- transform: translateX(500%);
16
- }
17
- `;
10
+ import { Birds } from './Birds';
11
+ import { Stars } from './Stars';
12
+ import { TimeOfDayContext } from './timeOfDayContext';
18
13
  export const moveRightToLeft = keyframes `
19
14
  0% {
20
15
  transform: translateX(250%);
@@ -69,22 +64,62 @@ export const moveRightToLeftWithFade3 = keyframes `
69
64
  `;
70
65
  export const CloudsAnimation = ({ children }) => {
71
66
  const [timeOfDay, setTimeOfDay] = useState(getTimeOfDay());
72
- const timeOfDaySeason = getTimeOfDaySeason();
67
+ // Preview toggle: flip to true to run a fast day→night loop locally. MUST be false in shipped code.
68
+ const DEMO = false;
69
+ const CYCLE = 40000; // demo loop length (ms), only used when DEMO is true
73
70
  const gradients = {
74
71
  day: colors.dayGradient,
75
72
  evening: colors.eveningGradient,
76
73
  morning: colors.morningGradient,
77
- night: colors.nightGradient,
74
+ // dark night sky for the login background only; the shared nightGradient token is left as-is for other surfaces
75
+ night: 'linear(to-b, #080c22, #1c2347)',
78
76
  };
77
+ // clouds are pushed back (faint) by day; at night they are not rendered at all (clear night sky)
78
+ // clouds are faint by day, and very transparent at dusk (evening sky is richer); hidden at night
79
+ const cloudOpacity = timeOfDay === 'night' ? 0 : timeOfDay === 'evening' ? 0.08 : 0.4;
80
+ const showClouds = timeOfDay !== 'night';
81
+ // each corner stacks the time-of-day tree variants and crossfades between them:
82
+ // morning = yellow, day = green, evening = pink (bespoke art); night reuses the day
83
+ // shape tinted dark purple so it matches day. Opacity crossfade = smooth color change.
84
+ const treeLayers = [
85
+ { key: 'morning', season: 'morning-summer' },
86
+ { key: 'day', season: 'day-summer' },
87
+ { key: 'evening', season: 'evening-summer' },
88
+ {
89
+ key: 'night',
90
+ season: 'day-summer',
91
+ filter: 'brightness(0.36) saturate(1.65) hue-rotate(158deg)',
92
+ },
93
+ ];
79
94
  useEffect(() => {
80
- const interval = setInterval(() => {
81
- setTimeOfDay(getTimeOfDay());
82
- }, 60000);
95
+ if (DEMO) {
96
+ const order = ['night', 'morning', 'day', 'evening'];
97
+ let i = 0;
98
+ setTimeOfDay(order[0]);
99
+ const id = setInterval(() => {
100
+ i = (i + 1) % order.length;
101
+ setTimeOfDay(order[i]);
102
+ }, CYCLE / 4);
103
+ return () => clearInterval(id);
104
+ }
105
+ const interval = setInterval(() => setTimeOfDay(getTimeOfDay()), 60000);
83
106
  return () => clearInterval(interval);
84
- }, []);
107
+ }, [DEMO, CYCLE]);
85
108
  const { windowDimensions: { width }, } = useWindowDimensions();
86
109
  const isMobile = width < 768;
87
110
  if (isMobile)
88
- return _jsx(Fragment, { children: children });
89
- return (_jsxs(Box, { minH: "100vh", w: "full", position: "relative", bgGradient: gradients[timeOfDay], overflowX: "hidden", overflowY: "scroll", children: [children, _jsx(Image, { src: Cloud1, position: "fixed", w: "auto", h: "80px", top: "50px", animation: `${moveRightToLeftWithFade3} 400s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud2, position: "fixed", w: "auto", h: "120px", top: "80px", animation: `${moveRightToLeftWithFade3} 200s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud3, position: "fixed", w: "auto", h: "160px", top: "120px", animation: `${moveRightToLeftWithFade3} 130s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud1, position: "fixed", w: "auto", h: "100px", top: "160px", animation: `${moveRightToLeftWithFade2} 400s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud2, position: "fixed", w: "auto", h: "112px", top: "220px", animation: `${moveRightToLeftWithFade2} 200s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud3, position: "fixed", w: "auto", h: "160px", top: "300px", animation: `${moveRightToLeftWithFade2} 130s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Airplane, position: "fixed", w: "auto", h: "40px", top: "85px", animation: `${moveLeftToRightAirplane} 500s cubic-bezier(1, 1, 1, 500) infinite`, zIndex: "1" }), _jsx(WeatherTrees, { timeOfDaySeason: timeOfDaySeason, position: "fixed", bottom: "0", right: "0", w: "auto", h: "100px", zIndex: "1" }), _jsx(WeatherTrees, { timeOfDaySeason: timeOfDaySeason, position: "fixed", bottom: "0", left: "2", w: "auto", transform: "rotateY(180deg)", h: "100px", zIndex: "1" })] }));
111
+ return (_jsx(TimeOfDayContext.Provider, { value: timeOfDay, children: children }));
112
+ return (_jsx(TimeOfDayContext.Provider, { value: timeOfDay, children: _jsxs(Box, { minH: "100vh", w: "full", position: "relative", overflowX: "hidden", overflowY: "scroll", children: [Object.keys(gradients).map((key) => (_jsx(Box, { position: "fixed", inset: 0, zIndex: 0, pointerEvents: "none", bgGradient: gradients[key], opacity: timeOfDay === key ? 1 : 0, transition: timeOfDay === key
113
+ ? 'opacity 1.6s ease-in' // incoming sky fades in first
114
+ : 'opacity 1.6s ease-out 1.4s' // outgoing waits, then fades — no transparent (white) gap
115
+ }, key))), _jsx(Box, { position: "fixed", inset: 0, zIndex: 0, pointerEvents: "none", bgGradient: "linear(to-b, #060a1f 0%, #080c22 28%, rgba(8,12,34,0) 66%)", opacity: timeOfDay === 'night' ? 1 : 0, transition: "opacity 0.9s ease-in" }), children, (timeOfDay === 'night' || timeOfDay === 'evening') && (_jsx(Box, { style: {
116
+ opacity: timeOfDay === 'night' ? 1 : 0,
117
+ transition: 'opacity 2s ease-in',
118
+ }, children: _jsx(Stars, {}) })), _jsxs(Box, { position: "fixed", inset: 0, zIndex: "1", pointerEvents: "none", style: { opacity: cloudOpacity, transition: 'opacity 1.8s ease-in 1.2s' }, children: [_jsx(Image, { src: Cloud1, position: "fixed", w: "auto", h: "80px", top: "50px", animation: `${moveRightToLeftWithFade3} 400s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud2, position: "fixed", w: "auto", h: "120px", top: "80px", animation: `${moveRightToLeftWithFade3} 200s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud3, position: "fixed", w: "auto", h: "160px", top: "120px", animation: `${moveRightToLeftWithFade3} 130s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud1, position: "fixed", w: "auto", h: "100px", top: "160px", animation: `${moveRightToLeftWithFade2} 400s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud2, position: "fixed", w: "auto", h: "112px", top: "220px", animation: `${moveRightToLeftWithFade2} 200s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud3, position: "fixed", w: "auto", h: "160px", top: "300px", animation: `${moveRightToLeftWithFade2} 130s linear infinite`, zIndex: "1" })] }), showClouds && _jsx(Birds, {}), treeLayers.map((t) => (_jsx(WeatherTrees, { timeOfDaySeason: t.season, filter: t.filter, position: "fixed", bottom: "0", right: "0", w: "auto", h: "100px", zIndex: "1", style: {
119
+ opacity: timeOfDay === t.key ? 1 : 0,
120
+ transition: 'opacity 1.8s ease-in-out',
121
+ } }, `tree-r-${t.key}`))), treeLayers.map((t) => (_jsx(WeatherTrees, { timeOfDaySeason: t.season, filter: t.filter, position: "fixed", bottom: "0", left: "2", w: "auto", transform: "rotateY(180deg)", h: "100px", zIndex: "1", style: {
122
+ opacity: timeOfDay === t.key ? 1 : 0,
123
+ transition: 'opacity 1.8s ease-in-out',
124
+ } }, `tree-l-${t.key}`)))] }) }));
90
125
  };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Stars — a faint twinkling starfield for the night sky. Rendered only at night
3
+ * by CloudsAnimation. Sits behind the clouds/birds layer; concentrated in the
4
+ * upper sky. Honors prefers-reduced-motion.
5
+ */
6
+ export declare const Stars: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,88 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ /**
4
+ * Stars — a faint twinkling starfield for the night sky. Rendered only at night
5
+ * by CloudsAnimation. Sits behind the clouds/birds layer; concentrated in the
6
+ * upper sky. Honors prefers-reduced-motion.
7
+ */
8
+ export const Stars = () => {
9
+ const stars = useMemo(() => {
10
+ const n = 80;
11
+ return Array.from({ length: n }, () => ({
12
+ left: Math.random() * 100,
13
+ top: Math.random() * 70, // keep to the upper sky
14
+ // weighted toward tiny — mostly pinpricks, a few slightly larger, none big
15
+ size: 0.6 + Math.pow(Math.random(), 2) * 2.1,
16
+ delay: -Math.random() * 5,
17
+ dur: 2.5 + Math.random() * 3.5,
18
+ base: 0.4 + Math.random() * 0.55,
19
+ }));
20
+ }, []);
21
+ // a couple of shooting stars that streak across on a diagonal, then fade — rare, staggered
22
+ const shooters = useMemo(() => Array.from({ length: 2 }, () => {
23
+ const dx = (Math.random() < 0.5 ? 1 : -1) * (380 + Math.random() * 320);
24
+ const dy = 150 + Math.random() * 190;
25
+ return {
26
+ left: 10 + Math.random() * 55,
27
+ top: 2 + Math.random() * 22,
28
+ dx,
29
+ dy,
30
+ rot: (Math.atan2(dy, dx) * 180) / Math.PI,
31
+ delay: -Math.random() * 25,
32
+ dur: 18 + Math.random() * 22,
33
+ };
34
+ }), []);
35
+ // a few small accent stars with a stronger, faster twinkle for a bit of sparkle
36
+ const twinklers = useMemo(() => Array.from({ length: 14 }, () => ({
37
+ left: Math.random() * 100,
38
+ top: Math.random() * 62,
39
+ size: 0.8 + Math.random() * 1.3,
40
+ delay: -Math.random() * 4,
41
+ dur: 1.4 + Math.random() * 2.2,
42
+ })), []);
43
+ return (_jsxs(_Fragment, { children: [_jsx("style", { children: STARS_CSS }), _jsxs("div", { className: "hf-stars", "aria-hidden": "true", children: [stars.map((s, i) => (_jsx("span", { className: "hf-star", style: {
44
+ left: `${s.left}%`,
45
+ top: `${s.top}%`,
46
+ width: `${s.size}px`,
47
+ height: `${s.size}px`,
48
+ animationDelay: `${s.delay}s`,
49
+ animationDuration: `${s.dur}s`,
50
+ '--b': s.base,
51
+ } }, i))), twinklers.map((s, i) => (_jsx("span", { className: "hf-star hf-twinkler", style: {
52
+ left: `${s.left}%`,
53
+ top: `${s.top}%`,
54
+ width: `${s.size}px`,
55
+ height: `${s.size}px`,
56
+ animationDelay: `${s.delay}s`,
57
+ animationDuration: `${s.dur}s`,
58
+ } }, `tw-${i}`))), shooters.map((s, i) => (_jsx("span", { className: "hf-shooting", style: {
59
+ left: `${s.left}%`,
60
+ top: `${s.top}%`,
61
+ animationDelay: `${s.delay}s`,
62
+ animationDuration: `${s.dur}s`,
63
+ '--dx': `${s.dx}px`,
64
+ '--dy': `${s.dy}px`,
65
+ '--rot': `${s.rot}deg`,
66
+ } }, `shoot-${i}`)))] })] }));
67
+ };
68
+ const STARS_CSS = `
69
+ .hf-stars{position:fixed; inset:0; z-index:0; pointer-events:none; overflow:hidden;}
70
+ .hf-star{position:absolute; border-radius:50%; background:#fdfdff;
71
+ box-shadow:0 0 4px rgba(255,255,255,.7); opacity:var(--b,.6);
72
+ animation-name:hf-twinkle; animation-iteration-count:infinite; animation-timing-function:ease-in-out;}
73
+ @keyframes hf-twinkle{ 0%,100%{opacity:calc(var(--b,.6) * .3);} 50%{opacity:var(--b,.6);} }
74
+ .hf-twinkler{animation-name:hf-sparkle;}
75
+ @keyframes hf-sparkle{ 0%,100%{opacity:.12; transform:scale(.6);} 50%{opacity:1; transform:scale(1.15);} }
76
+ .hf-shooting{position:absolute; width:100px; height:2px; border-radius:2px;
77
+ background:linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.95));
78
+ box-shadow:0 0 6px 1px rgba(200,220,255,.6); opacity:0; will-change:transform,opacity;
79
+ transform:translate3d(0,0,0) rotate(var(--rot,25deg));
80
+ animation-name:hf-shoot; animation-iteration-count:infinite; animation-timing-function:linear;}
81
+ @keyframes hf-shoot{
82
+ 0%{ transform:translate3d(0,0,0) rotate(var(--rot,25deg)); opacity:0; }
83
+ 1%{ opacity:1; }
84
+ 7%{ transform:translate3d(var(--dx,600px),var(--dy,260px),0) rotate(var(--rot,25deg)); opacity:0; }
85
+ 100%{ transform:translate3d(var(--dx,600px),var(--dy,260px),0) rotate(var(--rot,25deg)); opacity:0; }
86
+ }
87
+ @media (prefers-reduced-motion:reduce){ .hf-star,.hf-shooting{animation:none !important;} }
88
+ `;
@@ -1,3 +1,5 @@
1
+ export * from './Birds';
1
2
  export * from './CloudsAnimation';
3
+ export * from './Stars';
2
4
  export * from './HomieAnimation';
3
5
  export * from './WeatherTrees';
@@ -1,3 +1,5 @@
1
+ export * from './Birds';
1
2
  export * from './CloudsAnimation';
3
+ export * from './Stars';
2
4
  export * from './HomieAnimation';
3
5
  export * from './WeatherTrees';
@@ -0,0 +1,9 @@
1
+ import { TimeOfDay } from '../../utils';
2
+ /**
3
+ * Shares the CloudsAnimation background's current time-of-day with anything
4
+ * rendered inside it (e.g. the onboarding Footer), so text/colors can adapt to
5
+ * the sky actually on screen rather than reading the clock independently.
6
+ * Consumers get `null` when rendered outside a provider and should fall back.
7
+ */
8
+ export declare const TimeOfDayContext: import("react").Context<TimeOfDay | null>;
9
+ export declare const useTimeOfDay: () => TimeOfDay | null;
@@ -0,0 +1,9 @@
1
+ import { createContext, useContext } from 'react';
2
+ /**
3
+ * Shares the CloudsAnimation background's current time-of-day with anything
4
+ * rendered inside it (e.g. the onboarding Footer), so text/colors can adapt to
5
+ * the sky actually on screen rather than reading the clock independently.
6
+ * Consumers get `null` when rendered outside a provider and should fall back.
7
+ */
8
+ export const TimeOfDayContext = createContext(null);
9
+ export const useTimeOfDay = () => useContext(TimeOfDayContext);
@@ -1,7 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { t } from 'i18next';
3
3
  import { Flex, Text } from '@chakra-ui/react';
4
+ import { getTimeOfDay } from '../../utils';
5
+ import { useTimeOfDay } from '../animations/timeOfDayContext';
4
6
  export const Footer = () => {
7
+ var _a;
5
8
  const currentYear = new Date().getFullYear();
6
- return (_jsx(Flex, { justifyContent: "center", px: 4, my: 4, children: _jsx(Text, { variant: "label", textAlign: "center", children: `©${currentYear} ${t('footer.copyright')}` }) }));
9
+ // follow the background's actual time-of-day (falls back to the clock when outside CloudsAnimation)
10
+ const isNight = ((_a = useTimeOfDay()) !== null && _a !== void 0 ? _a : getTimeOfDay()) === 'night';
11
+ return (_jsx(Flex, { justifyContent: "center", px: 4, my: 4, children: _jsx(Text, { variant: "label", textAlign: "center", color: isNight ? 'rgba(226,232,248,0.6)' : 'rgba(55,50,62,0.8)', children: `©${currentYear} ${t('footer.copyright')}` }) }));
7
12
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@homefile/components-v2",
3
- "version": "2.57.0",
3
+ "version": "2.59.0",
4
4
  "author": "Homefile",
5
5
  "license": "UNLICENSED",
6
6
  "typings": "dist/index.d.ts",