#10 WIP: year-wrapped

Open
DemiSel wants to merge 7 commits from feat/year-wrapped into master
  1. 93
    0
      million/analyze/filter/message.py
  2. 46
    0
      million/analyze/filter/participants.py
  3. 11
    5
      million/analyze/retain_counts.py
  4. 33
    0
      million/http/app.py
  5. 20
    0
      million/model/filter/MessageFilter.py
  6. 23
    0
      million/model/filter/ParticipantFilter.py
  7. 7
    1
      million/model/message.py
  8. 130
    2
      poetry.lock
  9. 2
    0
      pyproject.toml
  10. 7
    1
      readme.md
  11. 2
    1
      scripts/find_holes.py
  12. 28
    0
      scripts/wrap_year.py
  13. 25
    0
      vite-wrapped/.gitignore
  14. 3
    0
      vite-wrapped/.vscode/extensions.json
  15. 19
    0
      vite-wrapped/README.md
  16. 29
    0
      vite-wrapped/deno.json
  17. 394
    0
      vite-wrapped/deno.lock
  18. 13
    0
      vite-wrapped/index.html
  19. 12
    0
      vite-wrapped/public/square.svg
  20. 15
    0
      vite-wrapped/public/star.svg
  21. 36
    0
      vite-wrapped/public/vite-deno.svg
  22. 1
    0
      vite-wrapped/public/vite.svg
  23. 10
    0
      vite-wrapped/src/App.css
  24. 113
    0
      vite-wrapped/src/App.tsx
  25. 1
    0
      vite-wrapped/src/assets/react.svg
  26. 44
    0
      vite-wrapped/src/components/Progress.tsx
  27. 45
    0
      vite-wrapped/src/components/mask/Masked.tsx
  28. 69
    0
      vite-wrapped/src/index.css
  29. 20
    0
      vite-wrapped/src/layout/canvas.tsx
  30. 12
    0
      vite-wrapped/src/main.tsx
  31. 257
    0
      vite-wrapped/src/steps/GlobalTop.tsx
  32. 5
    0
      vite-wrapped/src/steps/stepProps.ts
  33. 208
    0
      vite-wrapped/src/steps/welcome.tsx
  34. 1
    0
      vite-wrapped/src/vite-env.d.ts
  35. 5
    0
      vite-wrapped/tsconfig.json
  36. 8
    0
      vite-wrapped/vite.config.ts

+ 93
- 0
million/analyze/filter/message.py View File

@@ -0,0 +1,93 @@
1
+from __future__ import annotations
2
+from datetime import datetime
3
+import functools
4
+from typing import Any, Callable, List
5
+from million.analyze.retain_counts import is_count
6
+from million.model.filter.MessageFilter import MessageFilter, MessageOrder
7
+from million.model.message import Message
8
+
9
+
10
+def before(message: Message, date: datetime):
11
+    return message.date < date
12
+
13
+
14
+def after(message: Message, date: datetime):
15
+    return message.date > date
16
+
17
+
18
+def contains(message: Message, content: str):
19
+    return message.content is not None and content.lower() in message.content.lower()
20
+
21
+
22
+def is_number(message: Message, is_number: bool):
23
+    return is_count(message) == is_number
24
+
25
+
26
+def sender_name(message: Message, sender_name: str):
27
+    return message.sender_name == sender_name
28
+
29
+
30
+def having_reactions(message: Message, having_reactions: bool):
31
+    return bool(message.reactions) == having_reactions
32
+
33
+
34
+def list_to_tuple(function: Callable) -> Any:
35
+    """Custom decorator function, to convert list to a tuple."""
36
+
37
+    def wrapper(*args, **kwargs) -> Any:
38
+        args = tuple(tuple(x) if isinstance(x, list) else x for x in args)
39
+        kwargs = {k: tuple(v) if isinstance(v, list)
40
+                  else v for k, v in kwargs.items()}
41
+        result = function(*args, **kwargs)
42
+        result = tuple(result) if isinstance(result, list) else result
43
+        return result
44
+
45
+    return wrapper
46
+
47
+
48
+def order_reaction(messages: List[Message]):
49
+    return sorted(
50
+        messages,
51
+        key=lambda x: len(x.reactions) if x.reactions else 0,
52
+        reverse=True)
53
+
54
+
55
+def order_date(messages: List[Message]):
56
+    return sorted(messages, key=lambda x: x.date, reverse=True)
57
+
58
+
59
+@list_to_tuple
60
+@functools.lru_cache(maxsize=10_000)
61
+def filter(messages: List[Message], criterias: MessageFilter = None, order: MessageOrder = None, limit: int = None) -> List[Message]:
62
+    print(criterias)
63
+    conditions = [
64
+        [sender_name, criterias.sender_name] if criterias.sender_name else None,
65
+        [after, criterias.after] if criterias.after else None,
66
+        [before, criterias.before] if criterias.before else None,
67
+        [contains, criterias.contains] if criterias.contains else None,
68
+        [is_number, criterias.is_number] if criterias.is_number is not None else None,
69
+        [having_reactions, criterias.having_reactions] if criterias.having_reactions is not None else None,
70
+    ]
71
+
72
+    filtered = [
73
+        message for message in messages
74
+        if all(
75
+            condition[0](message, condition[1])
76
+            for condition in conditions
77
+            if condition
78
+        )
79
+    ]
80
+
81
+    if order == MessageOrder.reactions:
82
+        filtered = order_reaction(filtered)
83
+    elif order == MessageOrder.date:
84
+        filtered = order_date(filtered)
85
+
86
+    if limit:
87
+        filtered = filtered[:limit]
88
+
89
+    return filtered
90
+
91
+
92
+def count(messages: List[Message], criterias: MessageFilter):
93
+    return len(filter(messages, criterias))

+ 46
- 0
million/analyze/filter/participants.py View File

@@ -0,0 +1,46 @@
1
+
2
+
3
+from typing import List
4
+
5
+from pydantic import BaseModel
6
+from million.model.filter.MessageFilter import MessageFilter
7
+from million.model.filter.ParticipantFilter import OrderDirection, ParticipantFilter
8
+from million.model.message import Message
9
+from million.model.participant import Participant
10
+from million.analyze.filter.message import filter as message_filter
11
+
12
+
13
+class ParticipantCount(BaseModel):
14
+    participant: Participant
15
+    count: int
16
+
17
+
18
+def order_count(participants: List[Participant], messages: List[Message], criterias: MessageFilter = None, direction: OrderDirection = OrderDirection.desc) -> ParticipantCount:
19
+    result = [
20
+        ParticipantCount(
21
+            participant=participant,
22
+            count=len(
23
+                message_filter(
24
+                    messages,
25
+                    criterias=MessageFilter(**{
26
+                        **(criterias.model_dump() if criterias else dict()),
27
+                        "sender_name": participant.name
28
+                    })
29
+                )
30
+            ))
31
+
32
+        for participant in participants
33
+    ]
34
+    result.sort(key=lambda p: p.count, reverse=True)
35
+    return [result for result in result]
36
+
37
+
38
+def filter(messages: List[Message], participants: List[Participant], criterias: ParticipantFilter) -> List[ParticipantCount]:
39
+    result = [
40
+        participant for participant in participants
41
+    ]
42
+
43
+    result = order_count(
44
+        result, messages, criterias=criterias, direction=criterias.order_dir)
45
+
46
+    return result

+ 11
- 5
million/analyze/retain_counts.py View File

@@ -4,12 +4,18 @@ from typing import List
4 4
 from million.model.message import Message
5 5
 
6 6
 
7
-def retain_counts(messages : List[Message])-> List[Message]:
7
+def is_count(message: Message) -> bool:
8
+    """
9
+    Check if the message is a count
10
+    """
11
+    return message.content is not None and bool(re.search('(\d{2,}|^\d$)', message.content))
12
+
13
+
14
+def retain_counts(messages: List[Message]) -> List[Message]:
8 15
     """
9 16
     Retain only the messages that have a content
10 17
     """
11 18
     return [
12
-        m for m in messages 
13
-        if m.content and
14
-        re.search('(\d{2,}|^\d$)', m.content)
15
-        ]
19
+        m for m in messages
20
+        if is_count(m)
21
+    ]

+ 33
- 0
million/http/app.py View File

@@ -0,0 +1,33 @@
1
+from typing import Annotated, List
2
+from fastapi import FastAPI, Query
3
+from million.model.filter.MessageFilter import MessageFilter, MessageOrder
4
+from million.model.filter.ParticipantFilter import ParticipantFilter
5
+from million.model.message import Message
6
+from million.analyze.filter.message import filter as message_filter
7
+from million.analyze.filter.participants import ParticipantCount, filter as participants_filter
8
+from million.model.participant import Participant
9
+import million.parse.fb_exports as fb
10
+
11
+
12
+DATA_PATH = './data/'
13
+
14
+export = fb.parse_dirfiles(DATA_PATH)
15
+
16
+app = FastAPI()
17
+
18
+
19
+class GetMessageForm(MessageFilter):
20
+    order: MessageOrder = None
21
+    limit: int = 100
22
+
23
+
24
+@app.get("/messages")
25
+def read_messages(
26
+    form: Annotated[GetMessageForm, Query()]
27
+) -> List[Message]:
28
+    return message_filter(export.messages, form, form.order, form.limit)
29
+
30
+
31
+@app.get("/participants")
32
+def read_participants(form: Annotated[ParticipantFilter, Query()]) -> List[ParticipantCount]:
33
+    return participants_filter(export.messages, export.participants, form)

+ 20
- 0
million/model/filter/MessageFilter.py View File

@@ -0,0 +1,20 @@
1
+from datetime import datetime
2
+from enum import Enum
3
+from pydantic import BaseModel
4
+
5
+
6
+class MessageOrder(str, Enum):
7
+    date = "date"
8
+    reactions = "reactions"
9
+
10
+
11
+class MessageFilter(BaseModel):
12
+    before: datetime | None = None
13
+    after: datetime | None = None
14
+    contains: str | None = None
15
+    is_number: bool | None = None
16
+    sender_name: str | None = None
17
+    having_reactions: bool | None = None
18
+
19
+    class Config:
20
+        frozen = True

+ 23
- 0
million/model/filter/ParticipantFilter.py View File

@@ -0,0 +1,23 @@
1
+from datetime import datetime
2
+from pydantic import BaseModel
3
+from enum import Enum
4
+
5
+from million.model.filter.MessageFilter import MessageFilter
6
+
7
+
8
+class OrderBy(str, Enum):
9
+    count = "count"
10
+
11
+
12
+class OrderDirection(str, Enum):
13
+    asc = "asc"
14
+    desc = "desc"
15
+
16
+
17
+class ParticipantFilter(MessageFilter):
18
+
19
+    order_by: OrderBy | None = OrderBy.count
20
+    order_dir: OrderDirection | None = OrderDirection.desc
21
+
22
+    def __hash__(self):
23
+        return hash(self.name)

+ 7
- 1
million/model/message.py View File

@@ -1,7 +1,9 @@
1 1
 from datetime import datetime
2
-from math import floor
3 2
 from typing import Any, List
4 3
 from pydantic import BaseModel
4
+import pytz
5
+
6
+utc=pytz.UTC
5 7
 
6 8
 class Reaction(BaseModel):
7 9
     reaction: str
@@ -45,6 +47,10 @@ class Message(BaseModel):
45 47
     is_unsent: bool | None = None
46 48
     is_geoblocked_for_viewer: bool
47 49
 
50
+    @property
51
+    def date(self) -> datetime:
52
+        return utc.localize(datetime.fromtimestamp(self.timestamp_ms / 1000))
53
+
48 54
     def __str__(self) -> str:
49 55
         dt = datetime.fromtimestamp(self.timestamp_ms / 1000)
50 56
         dt_str = dt.strftime("%d/%m/%Y, %H:%M:%S")

+ 130
- 2
poetry.lock View File

@@ -1,4 +1,4 @@
1
-# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
1
+# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
2 2
 
3 3
 [[package]]
4 4
 name = "annotated-types"
@@ -12,6 +12,42 @@ files = [
12 12
 ]
13 13
 
14 14
 [[package]]
15
+name = "anyio"
16
+version = "4.7.0"
17
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
18
+optional = false
19
+python-versions = ">=3.9"
20
+files = [
21
+    {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"},
22
+    {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"},
23
+]
24
+
25
+[package.dependencies]
26
+exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
27
+idna = ">=2.8"
28
+sniffio = ">=1.1"
29
+typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
30
+
31
+[package.extras]
32
+doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"]
33
+test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"]
34
+trio = ["trio (>=0.26.1)"]
35
+
36
+[[package]]
37
+name = "click"
38
+version = "8.1.8"
39
+description = "Composable command line interface toolkit"
40
+optional = false
41
+python-versions = ">=3.7"
42
+files = [
43
+    {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
44
+    {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
45
+]
46
+
47
+[package.dependencies]
48
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
49
+
50
+[[package]]
15 51
 name = "colorama"
16 52
 version = "0.4.6"
17 53
 description = "Cross-platform colored terminal text."
@@ -115,6 +151,26 @@ files = [
115 151
 test = ["pytest (>=6)"]
116 152
 
117 153
 [[package]]
154
+name = "fastapi"
155
+version = "0.115.6"
156
+description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production"
157
+optional = false
158
+python-versions = ">=3.8"
159
+files = [
160
+    {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"},
161
+    {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"},
162
+]
163
+
164
+[package.dependencies]
165
+pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0"
166
+starlette = ">=0.40.0,<0.42.0"
167
+typing-extensions = ">=4.8.0"
168
+
169
+[package.extras]
170
+all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
171
+standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"]
172
+
173
+[[package]]
118 174
 name = "fonttools"
119 175
 version = "4.49.0"
120 176
 description = "Tools to manipulate font files"
@@ -180,6 +236,31 @@ unicode = ["unicodedata2 (>=15.1.0)"]
180 236
 woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
181 237
 
182 238
 [[package]]
239
+name = "h11"
240
+version = "0.14.0"
241
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
242
+optional = false
243
+python-versions = ">=3.7"
244
+files = [
245
+    {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
246
+    {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
247
+]
248
+
249
+[[package]]
250
+name = "idna"
251
+version = "3.10"
252
+description = "Internationalized Domain Names in Applications (IDNA)"
253
+optional = false
254
+python-versions = ">=3.6"
255
+files = [
256
+    {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
257
+    {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
258
+]
259
+
260
+[package.extras]
261
+all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
262
+
263
+[[package]]
183 264
 name = "igraph"
184 265
 version = "0.11.6"
185 266
 description = "High performance graph data structures and algorithms"
@@ -866,6 +947,34 @@ files = [
866 947
 ]
867 948
 
868 949
 [[package]]
950
+name = "sniffio"
951
+version = "1.3.1"
952
+description = "Sniff out which async library your code is running under"
953
+optional = false
954
+python-versions = ">=3.7"
955
+files = [
956
+    {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
957
+    {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
958
+]
959
+
960
+[[package]]
961
+name = "starlette"
962
+version = "0.41.3"
963
+description = "The little ASGI library that shines."
964
+optional = false
965
+python-versions = ">=3.8"
966
+files = [
967
+    {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"},
968
+    {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"},
969
+]
970
+
971
+[package.dependencies]
972
+anyio = ">=3.4.0,<5"
973
+
974
+[package.extras]
975
+full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
976
+
977
+[[package]]
869 978
 name = "texttable"
870 979
 version = "1.7.0"
871 980
 description = "module to create simple ASCII tables"
@@ -909,7 +1018,26 @@ files = [
909 1018
     {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
910 1019
 ]
911 1020
 
1021
+[[package]]
1022
+name = "uvicorn"
1023
+version = "0.34.0"
1024
+description = "The lightning-fast ASGI server."
1025
+optional = false
1026
+python-versions = ">=3.9"
1027
+files = [
1028
+    {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"},
1029
+    {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"},
1030
+]
1031
+
1032
+[package.dependencies]
1033
+click = ">=7.0"
1034
+h11 = ">=0.8"
1035
+typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""}
1036
+
1037
+[package.extras]
1038
+standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
1039
+
912 1040
 [metadata]
913 1041
 lock-version = "2.0"
914 1042
 python-versions = "^3.10"
915
-content-hash = "cfd60af98dcef95ed9f592cd7c4d616a3763174bffb33a612bb537bbb89d928f"
1043
+content-hash = "f215b3ecfbef3d48a2a6f8b67c121456e867f46481f968e6503d6cf1e0767b85"

+ 2
- 0
pyproject.toml View File

@@ -13,6 +13,8 @@ pydantic = "^2.6.1"
13 13
 pandas = "^2.2.0"
14 14
 python-igraph = "^0.11.6"
15 15
 pycairo = "^1.26.1"
16
+fastapi = "^0.115.6"
17
+uvicorn = "^0.34.0"
16 18
 
17 19
 
18 20
 [tool.poetry.group.test.dependencies]

+ 7
- 1
readme.md View File

@@ -36,4 +36,10 @@ python -m scripts.find_holes
36 36
 
37 37
 ```bash
38 38
 python -m scripts.read_top
39
-```
39
+```
40
+
41
+## Run the backend
42
+
43
+```bash
44
+ uvicorn million.http.app:app --reload --host 0.0.0.0
45
+ ```

+ 2
- 1
scripts/find_holes.py View File

@@ -18,11 +18,12 @@ print(f"Actual counted: {actual_counted}")
18 18
 
19 19
 holes = find_holes(filtered)
20 20
 
21
-print(len(holes))
22 21
 
23 22
 for hole in holes:
24 23
     print(f"{hole.start() + 1} -> {hole.end() - 1} ({hole.length() - 2})")
25 24
 
25
+print(f"Total holes: {len(holes)}")
26
+print(f"Total holes size: {sum([h.length() for h in holes if h.length() < 10_000])}")
26 27
 
27 28
 # lets export a csv file of the holes and the people responsible for them
28 29
 with open('output/holes.csv', 'w') as f:

+ 28
- 0
scripts/wrap_year.py View File

@@ -0,0 +1,28 @@
1
+from million.view.bar_chart import plot as bar_chart
2
+from million.analyze.count_participations import count_participations
3
+from million.analyze.retain_counts import retain_counts
4
+import million.parse.fb_exports as fb
5
+
6
+DATA_PATH = './data/'
7
+
8
+export = fb.parse_dirfiles(DATA_PATH)
9
+
10
+filtered = retain_counts(export.messages)
11
+
12
+print(len(filtered))
13
+
14
+# split messages into months
15
+first = filtered[0].date.strftime("%Y-%m")
16
+last = filtered[-1].date.strftime("%Y-%m")
17
+months = {
18
+    f"{year}-{month:02}": []
19
+    for year in range(int(first[:4]), int(last[:4]) + 1)
20
+    for month in range(1, 13)
21
+}
22
+
23
+for message in filtered:
24
+    month = message.date.strftime("%Y-%m")
25
+    months[month].append(message)
26
+
27
+for month, messages in months.items():
28
+    print(f"{month}: {len(messages)}")

+ 25
- 0
vite-wrapped/.gitignore View File

@@ -0,0 +1,25 @@
1
+# Logs
2
+logs
3
+*.log
4
+npm-debug.log*
5
+yarn-debug.log*
6
+yarn-error.log*
7
+pnpm-debug.log*
8
+lerna-debug.log*
9
+
10
+node_modules
11
+dist
12
+dist-ssr
13
+.vite
14
+*.local
15
+
16
+# Editor directories and files
17
+.vscode/*
18
+!.vscode/extensions.json
19
+.idea
20
+.DS_Store
21
+*.suo
22
+*.ntvs*
23
+*.njsproj
24
+*.sln
25
+*.sw?

+ 3
- 0
vite-wrapped/.vscode/extensions.json View File

@@ -0,0 +1,3 @@
1
+{
2
+  "recommendations": ["denoland.vscode-deno"]
3
+}

+ 19
- 0
vite-wrapped/README.md View File

@@ -0,0 +1,19 @@
1
+# Vite + Deno + React + TypeScript
2
+
3
+## Running
4
+
5
+You need to have Deno v2.0.0 or later installed to run this repo.
6
+
7
+Start a dev server:
8
+
9
+```
10
+$ deno task dev
11
+```
12
+
13
+## Deploy
14
+
15
+Build production assets:
16
+
17
+```
18
+$ deno task build
19
+```

+ 29
- 0
vite-wrapped/deno.json View File

@@ -0,0 +1,29 @@
1
+{
2
+    "nodeModulesDir": "auto",
3
+    "tasks": {
4
+        "dev": "deno run -A --node-modules-dir npm:vite",
5
+        "build": "deno run -A --node-modules-dir npm:vite build",
6
+        "preview": "deno run -A --node-modules-dir npm:vite preview",
7
+        "serve": "deno run --allow-net --allow-read jsr:@std/http@1/file-server dist/"
8
+    },
9
+    "compilerOptions": {
10
+        "lib": [
11
+            "ES2020",
12
+            "DOM",
13
+            "DOM.Iterable"
14
+        ],
15
+        "jsx": "react-jsx",
16
+        "jsxImportSource": "react",
17
+        "jsxImportSourceTypes": "@types/react"
18
+    },
19
+    "imports": {
20
+        "@deno/vite-plugin": "npm:@deno/vite-plugin@^1.0.0",
21
+        "@types/react": "npm:@types/react@^18.3.12",
22
+        "@types/react-dom": "npm:@types/react-dom@^18.3.1",
23
+        "@vitejs/plugin-react-swc": "npm:@vitejs/plugin-react-swc@^3.7.1",
24
+        "framer-motion": "npm:framer-motion@^11.15.0",
25
+        "react": "npm:react@^18.3.1",
26
+        "react-dom": "npm:react-dom@^18.3.1",
27
+        "vite": "npm:vite@^6.0.1"
28
+    }
29
+}

+ 394
- 0
vite-wrapped/deno.lock View File

@@ -0,0 +1,394 @@
1
+{
2
+  "version": "4",
3
+  "specifiers": {
4
+    "npm:@deno/vite-plugin@1": "1.0.2_vite@6.0.6",
5
+    "npm:@types/node@*": "22.5.4",
6
+    "npm:@types/react-dom@^18.3.1": "18.3.5_@types+react@18.3.18",
7
+    "npm:@types/react@^18.3.12": "18.3.18",
8
+    "npm:@vitejs/plugin-react-swc@^3.7.1": "3.7.2_vite@6.0.6",
9
+    "npm:framer-motion@^11.15.0": "11.15.0_react@18.3.1_react-dom@18.3.1__react@18.3.1",
10
+    "npm:react-dom@^18.3.1": "18.3.1_react@18.3.1",
11
+    "npm:react@^18.3.1": "18.3.1",
12
+    "npm:vite@*": "6.0.6",
13
+    "npm:vite@^6.0.1": "6.0.6"
14
+  },
15
+  "npm": {
16
+    "@deno/vite-plugin@1.0.2_vite@6.0.6": {
17
+      "integrity": "sha512-ZaC5W1yCb1xdRURU5qHFHrxZABr1tC68tS73/YhXUUbe9nuytCV4CG/dpLxMkxS64Fs3YwcqEz2k1eAqE9W5SQ==",
18
+      "dependencies": [
19
+        "vite"
20
+      ]
21
+    },
22
+    "@esbuild/aix-ppc64@0.24.2": {
23
+      "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="
24
+    },
25
+    "@esbuild/android-arm64@0.24.2": {
26
+      "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg=="
27
+    },
28
+    "@esbuild/android-arm@0.24.2": {
29
+      "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="
30
+    },
31
+    "@esbuild/android-x64@0.24.2": {
32
+      "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw=="
33
+    },
34
+    "@esbuild/darwin-arm64@0.24.2": {
35
+      "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA=="
36
+    },
37
+    "@esbuild/darwin-x64@0.24.2": {
38
+      "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA=="
39
+    },
40
+    "@esbuild/freebsd-arm64@0.24.2": {
41
+      "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg=="
42
+    },
43
+    "@esbuild/freebsd-x64@0.24.2": {
44
+      "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q=="
45
+    },
46
+    "@esbuild/linux-arm64@0.24.2": {
47
+      "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg=="
48
+    },
49
+    "@esbuild/linux-arm@0.24.2": {
50
+      "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA=="
51
+    },
52
+    "@esbuild/linux-ia32@0.24.2": {
53
+      "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw=="
54
+    },
55
+    "@esbuild/linux-loong64@0.24.2": {
56
+      "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ=="
57
+    },
58
+    "@esbuild/linux-mips64el@0.24.2": {
59
+      "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw=="
60
+    },
61
+    "@esbuild/linux-ppc64@0.24.2": {
62
+      "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw=="
63
+    },
64
+    "@esbuild/linux-riscv64@0.24.2": {
65
+      "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q=="
66
+    },
67
+    "@esbuild/linux-s390x@0.24.2": {
68
+      "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw=="
69
+    },
70
+    "@esbuild/linux-x64@0.24.2": {
71
+      "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q=="
72
+    },
73
+    "@esbuild/netbsd-arm64@0.24.2": {
74
+      "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw=="
75
+    },
76
+    "@esbuild/netbsd-x64@0.24.2": {
77
+      "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw=="
78
+    },
79
+    "@esbuild/openbsd-arm64@0.24.2": {
80
+      "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A=="
81
+    },
82
+    "@esbuild/openbsd-x64@0.24.2": {
83
+      "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA=="
84
+    },
85
+    "@esbuild/sunos-x64@0.24.2": {
86
+      "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig=="
87
+    },
88
+    "@esbuild/win32-arm64@0.24.2": {
89
+      "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ=="
90
+    },
91
+    "@esbuild/win32-ia32@0.24.2": {
92
+      "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="
93
+    },
94
+    "@esbuild/win32-x64@0.24.2": {
95
+      "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="
96
+    },
97
+    "@rollup/rollup-android-arm-eabi@4.29.1": {
98
+      "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw=="
99
+    },
100
+    "@rollup/rollup-android-arm64@4.29.1": {
101
+      "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew=="
102
+    },
103
+    "@rollup/rollup-darwin-arm64@4.29.1": {
104
+      "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw=="
105
+    },
106
+    "@rollup/rollup-darwin-x64@4.29.1": {
107
+      "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng=="
108
+    },
109
+    "@rollup/rollup-freebsd-arm64@4.29.1": {
110
+      "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw=="
111
+    },
112
+    "@rollup/rollup-freebsd-x64@4.29.1": {
113
+      "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w=="
114
+    },
115
+    "@rollup/rollup-linux-arm-gnueabihf@4.29.1": {
116
+      "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A=="
117
+    },
118
+    "@rollup/rollup-linux-arm-musleabihf@4.29.1": {
119
+      "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ=="
120
+    },
121
+    "@rollup/rollup-linux-arm64-gnu@4.29.1": {
122
+      "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA=="
123
+    },
124
+    "@rollup/rollup-linux-arm64-musl@4.29.1": {
125
+      "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA=="
126
+    },
127
+    "@rollup/rollup-linux-loongarch64-gnu@4.29.1": {
128
+      "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw=="
129
+    },
130
+    "@rollup/rollup-linux-powerpc64le-gnu@4.29.1": {
131
+      "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w=="
132
+    },
133
+    "@rollup/rollup-linux-riscv64-gnu@4.29.1": {
134
+      "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ=="
135
+    },
136
+    "@rollup/rollup-linux-s390x-gnu@4.29.1": {
137
+      "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g=="
138
+    },
139
+    "@rollup/rollup-linux-x64-gnu@4.29.1": {
140
+      "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ=="
141
+    },
142
+    "@rollup/rollup-linux-x64-musl@4.29.1": {
143
+      "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA=="
144
+    },
145
+    "@rollup/rollup-win32-arm64-msvc@4.29.1": {
146
+      "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig=="
147
+    },
148
+    "@rollup/rollup-win32-ia32-msvc@4.29.1": {
149
+      "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng=="
150
+    },
151
+    "@rollup/rollup-win32-x64-msvc@4.29.1": {
152
+      "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg=="
153
+    },
154
+    "@swc/core-darwin-arm64@1.10.2": {
155
+      "integrity": "sha512-xPDbCUfGdVjA/0yhRFVSyog73wO3/W3JNgx1PkOcCc+0OgZtgAnt4YD8QbSsUE+euc5bQJs/7HfJQ3305+HWVA=="
156
+    },
157
+    "@swc/core-darwin-x64@1.10.2": {
158
+      "integrity": "sha512-Dm4R9ffQw4yrGjvdYxxuO5RViwkRkSsn64WF7YGYZIlhkyFoseibPnQlOsx5qnjquc8f3h1C8/806XG+y3rMaQ=="
159
+    },
160
+    "@swc/core-linux-arm-gnueabihf@1.10.2": {
161
+      "integrity": "sha512-aXTqgel7AueM7CcCOFFUq6+gJyD/A3rFBWxPT6wA34IC7oQ0IIFpJjBLl8zN6/0aZ4OQ1ExlQ7zoKaTlk5tBug=="
162
+    },
163
+    "@swc/core-linux-arm64-gnu@1.10.2": {
164
+      "integrity": "sha512-HYFag6ULpnVMnHuKKAFuZH3kco/2eKKZ24I+gI2M4JlIW4soDmP8Oc2eAADIloln4SfQXzADX34m6merCWp65g=="
165
+    },
166
+    "@swc/core-linux-arm64-musl@1.10.2": {
167
+      "integrity": "sha512-N8es+V+M9GijYsxfiIG3NJ+lHgoZosX+yjblc5eOx0xrBDeqH3kNLhJpctOczrJk0rUjN+zX5x+8H8qurcEAaw=="
168
+    },
169
+    "@swc/core-linux-x64-gnu@1.10.2": {
170
+      "integrity": "sha512-fI4rxJkWQaNeG4UcuqKJrc1JM+nAwIzzFba9+A4Aohc6z0EgPokrA1v7WmPUObO+cdZjVXdMpDGkhGQhbok1aQ=="
171
+    },
172
+    "@swc/core-linux-x64-musl@1.10.2": {
173
+      "integrity": "sha512-ycDOxBgII/2xkusMgq2S9n81IQ8SeWk1FU0zuUsZrUkaXEq/78+nHFo/0IStPLrtRxzG2gJ0JZvfaa6jMxr79Q=="
174
+    },
175
+    "@swc/core-win32-arm64-msvc@1.10.2": {
176
+      "integrity": "sha512-s7/UrbdfYGdUar+Nj8jxNeXaFdryWnKuJU5udDONgk9gb1xp7K5TPxBL9j7EtCrAenM2sR9Bd84ZemwzyZ/VLw=="
177
+    },
178
+    "@swc/core-win32-ia32-msvc@1.10.2": {
179
+      "integrity": "sha512-sz8f+dmrzb816Ji25G+vs8HMq6zHq1IMKF4hVUnSJKdNr2k789+qRjF1fnv3YDcz5kkeYSvolXqVS1mCezDebg=="
180
+    },
181
+    "@swc/core-win32-x64-msvc@1.10.2": {
182
+      "integrity": "sha512-XXYHuc5KdhuLx1nP8cEKW+5Kakxy+iq/jcuJ52+27E2uB+xxzLeXvbPvz645je3Cti5nQ4la2HIn6tpST5ufSw=="
183
+    },
184
+    "@swc/core@1.10.2": {
185
+      "integrity": "sha512-d3reIYowBL6gbp4jC6FRZ3hE0eWcWwqh0XcHd6k5rKF/oZA6jLb7gxIRduJhrn+jyLz/HCC8WyfomUkEcs7iZQ==",
186
+      "dependencies": [
187
+        "@swc/core-darwin-arm64",
188
+        "@swc/core-darwin-x64",
189
+        "@swc/core-linux-arm-gnueabihf",
190
+        "@swc/core-linux-arm64-gnu",
191
+        "@swc/core-linux-arm64-musl",
192
+        "@swc/core-linux-x64-gnu",
193
+        "@swc/core-linux-x64-musl",
194
+        "@swc/core-win32-arm64-msvc",
195
+        "@swc/core-win32-ia32-msvc",
196
+        "@swc/core-win32-x64-msvc",
197
+        "@swc/counter",
198
+        "@swc/types"
199
+      ]
200
+    },
201
+    "@swc/counter@0.1.3": {
202
+      "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
203
+    },
204
+    "@swc/types@0.1.17": {
205
+      "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==",
206
+      "dependencies": [
207
+        "@swc/counter"
208
+      ]
209
+    },
210
+    "@types/estree@1.0.6": {
211
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
212
+    },
213
+    "@types/node@22.5.4": {
214
+      "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
215
+      "dependencies": [
216
+        "undici-types"
217
+      ]
218
+    },
219
+    "@types/prop-types@15.7.14": {
220
+      "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="
221
+    },
222
+    "@types/react-dom@18.3.5_@types+react@18.3.18": {
223
+      "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==",
224
+      "dependencies": [
225
+        "@types/react"
226
+      ]
227
+    },
228
+    "@types/react@18.3.18": {
229
+      "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
230
+      "dependencies": [
231
+        "@types/prop-types",
232
+        "csstype"
233
+      ]
234
+    },
235
+    "@vitejs/plugin-react-swc@3.7.2_vite@6.0.6": {
236
+      "integrity": "sha512-y0byko2b2tSVVf5Gpng1eEhX1OvPC7x8yns1Fx8jDzlJp4LS6CMkCPfLw47cjyoMrshQDoQw4qcgjsU9VvlCew==",
237
+      "dependencies": [
238
+        "@swc/core",
239
+        "vite"
240
+      ]
241
+    },
242
+    "csstype@3.1.3": {
243
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
244
+    },
245
+    "esbuild@0.24.2": {
246
+      "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
247
+      "dependencies": [
248
+        "@esbuild/aix-ppc64",
249
+        "@esbuild/android-arm",
250
+        "@esbuild/android-arm64",
251
+        "@esbuild/android-x64",
252
+        "@esbuild/darwin-arm64",
253
+        "@esbuild/darwin-x64",
254
+        "@esbuild/freebsd-arm64",
255
+        "@esbuild/freebsd-x64",
256
+        "@esbuild/linux-arm",
257
+        "@esbuild/linux-arm64",
258
+        "@esbuild/linux-ia32",
259
+        "@esbuild/linux-loong64",
260
+        "@esbuild/linux-mips64el",
261
+        "@esbuild/linux-ppc64",
262
+        "@esbuild/linux-riscv64",
263
+        "@esbuild/linux-s390x",
264
+        "@esbuild/linux-x64",
265
+        "@esbuild/netbsd-arm64",
266
+        "@esbuild/netbsd-x64",
267
+        "@esbuild/openbsd-arm64",
268
+        "@esbuild/openbsd-x64",
269
+        "@esbuild/sunos-x64",
270
+        "@esbuild/win32-arm64",
271
+        "@esbuild/win32-ia32",
272
+        "@esbuild/win32-x64"
273
+      ]
274
+    },
275
+    "framer-motion@11.15.0_react@18.3.1_react-dom@18.3.1__react@18.3.1": {
276
+      "integrity": "sha512-MLk8IvZntxOMg7lDBLw2qgTHHv664bYoYmnFTmE0Gm/FW67aOJk0WM3ctMcG+Xhcv+vh5uyyXwxvxhSeJzSe+w==",
277
+      "dependencies": [
278
+        "motion-dom",
279
+        "motion-utils",
280
+        "react",
281
+        "react-dom",
282
+        "tslib"
283
+      ]
284
+    },
285
+    "fsevents@2.3.3": {
286
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
287
+    },
288
+    "js-tokens@4.0.0": {
289
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
290
+    },
291
+    "loose-envify@1.4.0": {
292
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
293
+      "dependencies": [
294
+        "js-tokens"
295
+      ]
296
+    },
297
+    "motion-dom@11.14.3": {
298
+      "integrity": "sha512-lW+D2wBy5vxLJi6aCP0xyxTxlTfiu+b+zcpVbGVFUxotwThqhdpPRSmX8xztAgtZMPMeU0WGVn/k1w4I+TbPqA=="
299
+    },
300
+    "motion-utils@11.14.3": {
301
+      "integrity": "sha512-Xg+8xnqIJTpr0L/cidfTTBFkvRw26ZtGGuIhA94J9PQ2p4mEa06Xx7QVYZH0BP+EpMSaDlu+q0I0mmvwADPsaQ=="
302
+    },
303
+    "nanoid@3.3.8": {
304
+      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="
305
+    },
306
+    "picocolors@1.1.1": {
307
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
308
+    },
309
+    "postcss@8.4.49": {
310
+      "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
311
+      "dependencies": [
312
+        "nanoid",
313
+        "picocolors",
314
+        "source-map-js"
315
+      ]
316
+    },
317
+    "react-dom@18.3.1_react@18.3.1": {
318
+      "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
319
+      "dependencies": [
320
+        "loose-envify",
321
+        "react",
322
+        "scheduler"
323
+      ]
324
+    },
325
+    "react@18.3.1": {
326
+      "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
327
+      "dependencies": [
328
+        "loose-envify"
329
+      ]
330
+    },
331
+    "rollup@4.29.1": {
332
+      "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==",
333
+      "dependencies": [
334
+        "@rollup/rollup-android-arm-eabi",
335
+        "@rollup/rollup-android-arm64",
336
+        "@rollup/rollup-darwin-arm64",
337
+        "@rollup/rollup-darwin-x64",
338
+        "@rollup/rollup-freebsd-arm64",
339
+        "@rollup/rollup-freebsd-x64",
340
+        "@rollup/rollup-linux-arm-gnueabihf",
341
+        "@rollup/rollup-linux-arm-musleabihf",
342
+        "@rollup/rollup-linux-arm64-gnu",
343
+        "@rollup/rollup-linux-arm64-musl",
344
+        "@rollup/rollup-linux-loongarch64-gnu",
345
+        "@rollup/rollup-linux-powerpc64le-gnu",
346
+        "@rollup/rollup-linux-riscv64-gnu",
347
+        "@rollup/rollup-linux-s390x-gnu",
348
+        "@rollup/rollup-linux-x64-gnu",
349
+        "@rollup/rollup-linux-x64-musl",
350
+        "@rollup/rollup-win32-arm64-msvc",
351
+        "@rollup/rollup-win32-ia32-msvc",
352
+        "@rollup/rollup-win32-x64-msvc",
353
+        "@types/estree",
354
+        "fsevents"
355
+      ]
356
+    },
357
+    "scheduler@0.23.2": {
358
+      "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
359
+      "dependencies": [
360
+        "loose-envify"
361
+      ]
362
+    },
363
+    "source-map-js@1.2.1": {
364
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
365
+    },
366
+    "tslib@2.8.1": {
367
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
368
+    },
369
+    "undici-types@6.19.8": {
370
+      "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
371
+    },
372
+    "vite@6.0.6": {
373
+      "integrity": "sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==",
374
+      "dependencies": [
375
+        "esbuild",
376
+        "fsevents",
377
+        "postcss",
378
+        "rollup"
379
+      ]
380
+    }
381
+  },
382
+  "workspace": {
383
+    "dependencies": [
384
+      "npm:@deno/vite-plugin@1",
385
+      "npm:@types/react-dom@^18.3.1",
386
+      "npm:@types/react@^18.3.12",
387
+      "npm:@vitejs/plugin-react-swc@^3.7.1",
388
+      "npm:framer-motion@^11.15.0",
389
+      "npm:react-dom@^18.3.1",
390
+      "npm:react@^18.3.1",
391
+      "npm:vite@^6.0.1"
392
+    ]
393
+  }
394
+}

+ 13
- 0
vite-wrapped/index.html View File

@@ -0,0 +1,13 @@
1
+<!doctype html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="UTF-8" />
5
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+    <title>Vite + React + TS</title>
8
+  </head>
9
+  <body>
10
+    <div id="root"></div>
11
+    <script type="module" src="/src/main.tsx"></script>
12
+  </body>
13
+</html>

+ 12
- 0
vite-wrapped/public/square.svg View File

@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<svg
3
+    id="Calque_1"
4
+    data-name="Calque 1"
5
+    xmlns="http://www.w3.org/2000/svg"
6
+    viewBox="0 0 400 400"
7
+>
8
+    <polygon stroke="white" fill="#00000000"
9
+    stroke-width="1em"
10
+    points="0 0 400 0 400 400 0 400"
11
+    />
12
+</svg>

+ 15
- 0
vite-wrapped/public/star.svg View File

@@ -0,0 +1,15 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<svg
3
+    id="Calque_1"
4
+    data-name="Calque 1"
5
+    xmlns="http://www.w3.org/2000/svg"
6
+    viewBox="0 0 456.79 436.42"
7
+>
8
+    <defs>
9
+    <style></style>
10
+    </defs>
11
+    <polygon stroke="white" fill="#00000000"
12
+    stroke-width="1em"
13
+    points="372.66 431.54 231.13 359.66 91.67 435.5 116.3 278.68 1.08 169.49 157.83 144.45 226.07 1.13 298.32 142.47 455.72 163.08 343.62 275.48 372.66 431.54"
14
+    />
15
+</svg>

+ 36
- 0
vite-wrapped/public/vite-deno.svg View File

@@ -0,0 +1,36 @@
1
+<svg width="248" height="275" viewBox="0 0 248 275" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+<g clip-path="url(#clip0_553_1862)">
3
+<ellipse cx="133.981" cy="219.676" rx="73.9807" ry="14.6763" fill="#74825F" fill-opacity="0.51"/>
4
+<path d="M136.505 51.7512C153.877 66.3061 166.457 128.128 173.046 171.857C188.321 192.524 218.273 184.138 219.471 183.239C220.669 182.341 233.548 171.857 229.954 190.727C226.36 209.597 197.307 215.287 173.046 210.495C173.046 214.688 158.669 233.258 139.8 235.355C124.704 237.032 113.941 226.869 110.447 221.577L99.3649 216.186C96.7691 217.584 81.6934 221.817 64.9205 221.577C43.9543 221.278 44.8528 197.915 51.1427 186.833C56.1746 177.968 71.41 176.949 78.3987 177.548L84.3891 163.471C82.3923 163.671 69.9524 162.453 67.3166 161.973C64.0219 161.374 55.6307 149.898 59.2296 134.118C63.1234 117.046 95.7707 113.751 99.3649 112.254C102.959 110.756 104.457 96.6787 104.157 83.5C88.5823 81.7029 72.5581 73.9155 76.6016 55.0459C81.9929 29.8864 114.341 33.1812 136.505 51.7512Z" fill="#B1F491" stroke="black" stroke-width="3.5942" stroke-linecap="round"/>
5
+<path d="M102.66 83.5C106.254 83.8994 114.041 84.159 116.437 82.0024" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
6
+<path d="M90.3794 206.601C92.7756 213.191 100.863 216.785 100.863 216.785" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
7
+<path d="M171.249 196.418C172.148 199.114 173.346 210.495 171.249 214.389" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
8
+<circle cx="112.544" cy="54.2488" r="2.99517" fill="black"/>
9
+<path d="M63.1234 128.128C67.9157 130.924 77.62 138.312 78.0992 145.5C78.6983 154.486 73.307 160.476 69.1137 161.973" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
10
+<path d="M54.1379 182.64C58.7305 183.838 66.4181 189.23 67.9157 197.616C69.4132 206.002 71.5099 217.684 62.8239 221.577" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
11
+<path d="M68.5147 137.114L74.505 131.722" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
12
+<path d="M73.0074 149.693L81.6934 151.79" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
13
+<path d="M61.0268 191.626L67.6162 188.031" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
14
+<path d="M65.819 208.099L73.6065 210.196" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
15
+<path d="M30.9264 186.393L26.0084 169.639C25.7495 168.757 26.7174 168.022 27.4972 168.509L105.553 217.204C107.381 218.344 107.446 220.984 105.676 222.213L98.9017 226.917C94.8436 229.735 89.5878 230.226 85.1343 228.087C64.6655 218.258 52.0858 209.624 34.4753 192.341C32.8025 190.699 31.5866 188.642 30.9264 186.393Z" fill="#4DC3FF" stroke="black" stroke-width="4"/>
16
+<path d="M41.8791 160.074L46.7262 148.312C47.4243 146.618 49.8432 146.677 50.459 148.402L55.3497 162.106C55.872 163.57 54.5987 165.044 53.0748 164.74L43.337 162.797C42.1019 162.551 41.3992 161.238 41.8791 160.074Z" fill="#FFA901" stroke="black" stroke-width="4"/>
17
+<path d="M71.8785 149.588L101.01 128.223C105.454 124.964 111.741 127.892 112.05 133.394C113.793 164.454 113.06 185.535 109.769 215.636C109.227 220.59 103.792 223.354 99.4667 220.878C71.8486 205.063 53.3581 192.516 27.512 172.516C22.8968 168.945 24.4798 161.641 30.1507 160.265L69.3892 150.746C70.2872 150.528 71.1333 150.134 71.8785 149.588Z" fill="#BD34FE" stroke="black" stroke-width="4"/>
18
+<path d="M49.0264 145.468C58.7514 138.974 75.386 127.953 81.8842 123.946C83.0547 123.224 84.4689 123.961 84.6314 125.326L86.5911 141.788C86.752 143.139 88.1864 143.939 89.4205 143.365L95.4192 140.575C96.8754 139.898 98.4956 141.126 98.2363 142.711L90.6765 188.924C90.3789 190.743 87.9869 191.225 87.008 189.663L80.4098 179.132C79.7539 178.085 78.3152 177.877 77.3899 178.696L75.0448 180.77C74.0312 181.667 72.4383 181.319 71.8907 180.081L67.5098 170.18C66.9622 168.943 65.3693 168.595 64.3557 169.492L62.2372 171.366C61.2396 172.248 59.6743 171.927 59.1044 170.724L48.3367 147.981C47.9126 147.085 48.202 146.018 49.0264 145.468Z" fill="#FFD32B" stroke="black" stroke-width="4"/>
19
+<circle cx="92.7756" cy="48.2585" r="2.99517" fill="black"/>
20
+<path d="M104.88 161.075C100.088 151.97 109.473 142.505 114.764 138.911L151.904 151.49C152.004 156.582 150.047 168.143 141.421 173.655C130.638 180.543 110.87 172.457 104.88 161.075Z" fill="#B1F491"/>
21
+<path d="M111.346 222.775C101.521 208.878 115.439 197.017 123.626 192.824C129.703 192.1 142.516 193.468 145.152 204.73C148.446 218.807 130.554 237.227 111.346 222.775Z" fill="#B1F491"/>
22
+<path d="M123.626 192.824C112.285 195.592 101.179 211.429 115.799 227.493" stroke="black" stroke-width="3.5942" stroke-linecap="round"/>
23
+<path d="M114.764 138.911C109.473 142.505 100.088 151.97 104.88 161.075C110.87 172.457 130.638 180.543 141.421 173.655C150.047 168.143 152.004 156.582 151.904 151.49" stroke="black" stroke-width="3.5942" stroke-linecap="round"/>
24
+<path d="M104.457 153.887C108.35 151.99 118.354 149.334 127.22 153.887C138.302 159.577 143.993 167.964 144.592 170.66" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
25
+<path d="M115.539 155.684L118.235 147.297" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
26
+<path d="M136.505 165.268L141.896 159.278" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
27
+<path d="M115.539 199.114C130.814 197.317 138.003 205.104 139.5 214.089C140.998 223.075 138.602 235.056 132.312 235.655" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
28
+<path d="M129.317 205.403L134.109 198.814" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
29
+<path d="M135.906 217.384L144.592 213.79" stroke="#3E7B28" stroke-opacity="0.55" stroke-width="3.5942" stroke-linecap="round"/>
30
+</g>
31
+<defs>
32
+<clipPath id="clip0_553_1862">
33
+<rect width="248" height="274.957" fill="white"/>
34
+</clipPath>
35
+</defs>
36
+</svg>

+ 1
- 0
vite-wrapped/public/vite.svg View File

@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

+ 10
- 0
vite-wrapped/src/App.css View File

@@ -0,0 +1,10 @@
1
+html,
2
+body,
3
+#root {
4
+  height: 100%;
5
+  width: 100%;
6
+  margin: 0;
7
+  padding: 0;
8
+  font-family: sans-serif;
9
+  text-align: center;
10
+}

+ 113
- 0
vite-wrapped/src/App.tsx View File

@@ -0,0 +1,113 @@
1
+import "./App.css";
2
+// @deno-types="@types/react"
3
+import React, { useEffect, useState } from "react";
4
+
5
+import {
6
+  AnimatePresence,
7
+  motion,
8
+  useMotionValue,
9
+} from "framer-motion";
10
+import { useAnimationFrame } from "framer-motion";
11
+import Welcome from "./steps/welcome.tsx";
12
+import GlobalTop from "./steps/GlobalTop.tsx";
13
+
14
+const steps = [
15
+  {
16
+    title: "Step 1",
17
+    content: Welcome,
18
+  },
19
+  {
20
+    title: "Step 2",
21
+    content: GlobalTop,
22
+  },
23
+  {
24
+    title: "Step 3",
25
+    content: "Content of step 3",
26
+  },
27
+];
28
+
29
+const enterAnimation = {
30
+  opacity: 0,
31
+  scale: 0,
32
+  transition: {
33
+    duration: 0.25,
34
+  },
35
+};
36
+
37
+const exitAnimation = {
38
+  opacity: 0,
39
+  scale: 0,
40
+  transition: {
41
+    duration: 0.25,
42
+  },
43
+};
44
+
45
+function App() {
46
+  const [currentStep, setCurrentStep] = useState(0);
47
+  const animationDuration = 10_000;
48
+  const animationProgress = useMotionValue(0);
49
+  const [initialAnimationFrame, setInitialAnimationFrame] = useState(0);
50
+
51
+  useAnimationFrame((time, delta) => {
52
+    if (time - initialAnimationFrame >= animationDuration) {
53
+      setInitialAnimationFrame(time);
54
+      //setCurrentStep((prev) => prev + 1);
55
+      animationProgress.set(0);
56
+    } else {
57
+      animationProgress.set(animationProgress.get() + delta);
58
+    }
59
+  });
60
+
61
+  useEffect(() => {
62
+    animationProgress.set(0);
63
+    setInitialAnimationFrame(0);
64
+  }, [currentStep]);
65
+
66
+  const CurrentStepContent = steps[currentStep].content;
67
+  return (
68
+    <>
69
+      <div style={page as React.CSSProperties}>
70
+        <div
71
+          style={{
72
+            position: "absolute",
73
+            top: "0",
74
+            bottom: "0",
75
+            left: "0",
76
+            right: "0",
77
+          }}
78
+        >
79
+          <AnimatePresence>
80
+            <motion.span
81
+              key={`${currentStep}`}
82
+              initial={enterAnimation}
83
+              exit={exitAnimation}
84
+              animate={{ opacity: 1, scale: 1 }}
85
+            >
86
+              <CurrentStepContent progress={animationProgress} />
87
+            </motion.span>
88
+          </AnimatePresence>
89
+          {/*  <Welcome progress={animationProgress} /> */}
90
+        </div>
91
+        <div style={{ zIndex: 100, fontSize: "5px" }}>
92
+          <h1>Step selector (current = {currentStep})</h1>
93
+          <button onClick={() => setCurrentStep(0)}>Step 1</button>
94
+          <button onClick={() => setCurrentStep(1)}>Step 2</button>
95
+        </div>
96
+      </div>
97
+    </>
98
+  );
99
+}
100
+
101
+const page = {
102
+  display: "flex",
103
+  position: "relative",
104
+  justifyContent: "start",
105
+  alignItems: "start",
106
+  margin: 0,
107
+  padding: 0,
108
+  width: "100%",
109
+  height: "100%",
110
+  flexDirection: "column",
111
+  backgroundColor: "#050505",
112
+};
113
+export default App;

+ 1
- 0
vite-wrapped/src/assets/react.svg View File

@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>

+ 44
- 0
vite-wrapped/src/components/Progress.tsx View File

@@ -0,0 +1,44 @@
1
+
2
+type ProgressProps = {
3
+    step: number,
4
+    steps: number,
5
+    progress: number,
6
+    length: number,
7
+}
8
+
9
+const Progress = ({ step, steps, progress, length }: ProgressProps) => {
10
+    const progressWidth = (progress / length) * 100;
11
+    return (
12
+        <div style={progressContainer as React.CSSProperties}>
13
+            <div style={progressBar}>
14
+                <div style={{ ...progressBarInner, width: `${progressWidth}%` }} />
15
+            </div>
16
+            <div style={progressText as React.CSSProperties}>
17
+                {step + 1} / {steps}
18
+            </div>
19
+        </div>
20
+    );
21
+};
22
+
23
+const progressContainer = {
24
+    display: "flex",
25
+    flexDirection: "column",
26
+    alignItems: "center",
27
+    width: "100%",
28
+    marginBottom: "1rem",
29
+};
30
+
31
+const progressBar = {
32
+    width: "100%",
33
+    height: "1rem",
34
+    backgroundColor: "rgba(0, 0, 0, 0.2)",
35
+    borderRadius: "0.5rem",
36
+};
37
+
38
+const progressBarInner = {
39
+    height: "100%",
40
+    backgroundColor: "rgba(0, 0, 0, 0.5)",
41
+    borderRadius: "0.5rem",
42
+};
43
+
44
+export default Progress;

+ 45
- 0
vite-wrapped/src/components/mask/Masked.tsx View File

@@ -0,0 +1,45 @@
1
+import React from "react";
2
+
3
+type Mask = "Star" | "Square";
4
+
5
+function Masked({
6
+  children,
7
+  mask,
8
+  style,
9
+}: {
10
+  children?: React.ReactNode;
11
+  mask: Mask;
12
+  style: React.CSSProperties;
13
+}) {
14
+  const [maskUrl, setMaskUrl] = React.useState<String>({});
15
+
16
+  React.useEffect(() => {
17
+    if (mask === "Star") {
18
+      setMaskUrl("/star.svg");
19
+    }
20
+    if (mask === "Square") {
21
+      setMaskUrl("/square.svg");
22
+    }
23
+  }, [mask]);
24
+
25
+  return (
26
+    <div
27
+      style={{
28
+        ...{
29
+          maskImage: `url(${maskUrl})`,
30
+          maskSize: "cover",
31
+          maskRepeat: "no-repeat",
32
+          maskPosition: "center",
33
+          maskClip: "content-box",
34
+          width: "100%",
35
+          height: "100%",
36
+        },
37
+        ...style,
38
+      }}
39
+    >
40
+        {children}
41
+    </div>
42
+  );
43
+}
44
+
45
+export default Masked;

+ 69
- 0
vite-wrapped/src/index.css View File

@@ -0,0 +1,69 @@
1
+:root {
2
+  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+  line-height: 1.5;
4
+  font-weight: 400;
5
+
6
+  color-scheme: light dark;
7
+  color: rgba(255, 255, 255, 0.87);
8
+  background-color: #242424;
9
+
10
+  font-synthesis: none;
11
+  text-rendering: optimizeLegibility;
12
+  -webkit-font-smoothing: antialiased;
13
+  -moz-osx-font-smoothing: grayscale;
14
+  -webkit-text-size-adjust: 100%;
15
+}
16
+
17
+a {
18
+  font-weight: 500;
19
+  color: #646cff;
20
+  text-decoration: inherit;
21
+}
22
+a:hover {
23
+  color: #535bf2;
24
+}
25
+
26
+body {
27
+  margin: 0;
28
+  display: flex;
29
+  place-items: center;
30
+  min-width: 320px;
31
+  min-height: 100vh;
32
+}
33
+
34
+h1 {
35
+  font-size: 3.2em;
36
+  line-height: 1.1;
37
+}
38
+
39
+button {
40
+  border-radius: 8px;
41
+  border: 1px solid transparent;
42
+  padding: 0.6em 1.2em;
43
+  font-size: 1em;
44
+  font-weight: 500;
45
+  font-family: inherit;
46
+  background-color: #1a1a1a;
47
+  cursor: pointer;
48
+  transition: border-color 0.25s;
49
+}
50
+button:hover {
51
+  border-color: #646cff;
52
+}
53
+button:focus,
54
+button:focus-visible {
55
+  outline: 4px auto -webkit-focus-ring-color;
56
+}
57
+
58
+@media (prefers-color-scheme: light) {
59
+  :root {
60
+    color: #213547;
61
+    background-color: #ffffff;
62
+  }
63
+  a:hover {
64
+    color: #747bff;
65
+  }
66
+  button {
67
+    background-color: #f9f9f9;
68
+  }
69
+}

+ 20
- 0
vite-wrapped/src/layout/canvas.tsx View File

@@ -0,0 +1,20 @@
1
+
2
+function Canvas({children}: {children: React.ReactNode}) {
3
+  return (
4
+    <div style={canvas}>
5
+        {children}
6
+    </div>
7
+  );
8
+}
9
+
10
+const canvas = {
11
+  display: "flex",
12
+  justifyContent: "center",
13
+  alignItems: "center",
14
+  width: "100%",
15
+  height: "100%",
16
+  backgroundColor: "rgba(0, 0, 0, 0.2)",
17
+};
18
+
19
+
20
+export default Canvas;

+ 12
- 0
vite-wrapped/src/main.tsx View File

@@ -0,0 +1,12 @@
1
+import './index.css'
2
+// @deno-types="@types/react"
3
+import { StrictMode } from 'react'
4
+// @deno-types="@types/react-dom/client"
5
+import { createRoot } from 'react-dom/client'
6
+import App from './App.tsx'
7
+
8
+createRoot(document.getElementById('root') as HTMLElement).render(
9
+  <StrictMode>
10
+    <App />
11
+  </StrictMode>,
12
+)

+ 257
- 0
vite-wrapped/src/steps/GlobalTop.tsx View File

@@ -0,0 +1,257 @@
1
+import { motion, Variants } from "framer-motion";
2
+import { StepProps } from "./stepProps.ts";
3
+import Masked from "../components/mask/Masked.tsx";
4
+import React, { useState, useEffect, useRef } from "react";
5
+
6
+type VariantProps = {
7
+  delay: number;
8
+};
9
+
10
+const leftSideVariants: Variants = {
11
+  "1": (props: VariantProps) => ({
12
+    width: 1,
13
+    transition: {
14
+      duration: 1.5,
15
+      repeat: 0,
16
+      type: "spring",
17
+      ease: "easeInOut",
18
+      delay: 1.5 + props.delay,
19
+    },
20
+  }),
21
+  "2": (props: VariantProps) => ({
22
+    width: 10,
23
+    transition: {
24
+      duration: 1,
25
+      repeat: 0,
26
+      delay: 0,
27
+    },
28
+  }),
29
+  "3": (props: VariantProps) => ({
30
+    width: 100 + props.delay * 400,
31
+    transition: {
32
+      duration: 2,
33
+      repeat: 0,
34
+      ease: "easeInOut",
35
+      delay: 0,
36
+    },
37
+  }),
38
+};
39
+
40
+const textAreaVariants: Variants = {
41
+  "1": {
42
+    opacity: 1,
43
+    width: "80%",
44
+    height: "80%",
45
+    transition: {
46
+      duration: 0.5,
47
+    },
48
+  },
49
+  "2": {
50
+    opacity: 1,
51
+    width: "80%",
52
+    height: "80%",
53
+    transition: {
54
+      duration: 1.5,
55
+    },
56
+  },
57
+  "3": {
58
+    opacity: 1,
59
+    width: "80%",
60
+    height: "80%",
61
+    transition: {
62
+      duration: 1,
63
+    },
64
+  },
65
+};
66
+
67
+type RunningAnimations = {
68
+  [key: string]: number;
69
+};
70
+
71
+function usePrevious(value: any) {
72
+  const ref = useRef();
73
+  useEffect(() => {
74
+    ref.current = value;
75
+  }, [value]);
76
+  return ref.current;
77
+}
78
+
79
+function GlobalTop({ progress }: StepProps) {
80
+  const [starVariant, setStarVariant] = useState("1");
81
+  const [runningAnimations, setRunningAnimations] = useState<RunningAnimations>(
82
+    {}
83
+  );
84
+  const previousRunningAnimations = usePrevious(runningAnimations);
85
+
86
+  // implement the logic of "animationComplete" in a useEffect
87
+  // Consider an animation just completed when the associated number of running animations just reached 0
88
+
89
+  useEffect(() => {
90
+    for (const variant in runningAnimations) {
91
+      if (
92
+        runningAnimations[variant] === 0 &&
93
+        previousRunningAnimations[variant] !== 0
94
+      ) {
95
+        if (variant === "1") {
96
+          setStarVariant("2");
97
+        } else if (variant === "2") {
98
+          setStarVariant("3");
99
+        }
100
+      }
101
+    }
102
+  }, [runningAnimations]);
103
+
104
+  const animationComplete = (variant: string) => {
105
+    console.log("animationComplete", variant);
106
+    decrementAnimation(variant);
107
+  };
108
+
109
+  const animationStarted = (variant: string) => {
110
+    console.log("animationStarted", variant);
111
+    incrementAnimation(variant);
112
+  };
113
+
114
+  const incrementAnimation = (animation: string) => {
115
+    setRunningAnimations((prev: RunningAnimations) => {
116
+      const newRunningAnimations = { ...prev };
117
+      if (!newRunningAnimations[animation]) {
118
+        newRunningAnimations[animation] = 1;
119
+      } else {
120
+        newRunningAnimations[animation]++;
121
+      }
122
+      return newRunningAnimations;
123
+    });
124
+  };
125
+
126
+  const decrementAnimation = (animation: string) => {
127
+    setRunningAnimations((prev: RunningAnimations) => {
128
+      const newRunningAnimations = { ...prev };
129
+      if (newRunningAnimations[animation] > 0) {
130
+        newRunningAnimations[animation]--;
131
+      }
132
+      return newRunningAnimations;
133
+    });
134
+  };
135
+
136
+  return (
137
+    <div style={pageStyle as React.CSSProperties}>
138
+      <div
139
+        style={{
140
+          display: "flex",
141
+          position: "absolute",
142
+          top: 0,
143
+          bottom: 0,
144
+          left: 0,
145
+          right: 0,
146
+          flexDirection: "column",
147
+          alignItems: "start",
148
+          justifyContent: "start",
149
+        }}
150
+      >
151
+        {Array.from({ length: 7 }).map((_, i) => (
152
+          <motion.div
153
+            key={i}
154
+            variants={leftSideVariants}
155
+            style={baseStyle as React.CSSProperties}
156
+            animate={starVariant}
157
+            custom={{ delay: i * 0.05 }}
158
+            onAnimationStart={() => animationStarted(starVariant)}
159
+            onAnimationComplete={() => animationComplete(starVariant)}
160
+          >
161
+            <div style={starStyle as React.CSSProperties}></div>
162
+          </motion.div>
163
+        ))}
164
+      </div>
165
+      <div
166
+        style={{
167
+          display: "flex",
168
+          position: "absolute",
169
+          top: 0,
170
+          bottom: 0,
171
+          left: 0,
172
+          right: 0,
173
+          flexDirection: "column",
174
+          alignItems: "end",
175
+          justifyContent: "end",
176
+        }}
177
+      >
178
+        {Array.from({ length: 7 }).map((_, i) => (
179
+          <motion.div
180
+            key={i}
181
+            variants={leftSideVariants}
182
+            style={baseStyle as React.CSSProperties}
183
+            animate={starVariant}
184
+            custom={{ delay: i * 0.05 }}
185
+            onAnimationStart={() => animationStarted(starVariant)}
186
+            onAnimationComplete={() => animationComplete(starVariant)}
187
+          >
188
+            <div style={starStyle as React.CSSProperties}></div>
189
+          </motion.div>
190
+        ))}
191
+      </div>
192
+
193
+      <div style={bgSquareStyle as React.CSSProperties}></div>
194
+
195
+      <motion.div
196
+        variants={textAreaVariants}
197
+        animate={starVariant}
198
+        style={textAreaStyle as React.CSSProperties}
199
+      >
200
+        Coucou ! {starVariant}
201
+      </motion.div>
202
+    </div>
203
+  );
204
+}
205
+
206
+const pageStyle = {
207
+  position: "relative",
208
+  width: "100%",
209
+  height: "100%",
210
+  display: "flex",
211
+  justifyContent: "center",
212
+  alignItems: "center",
213
+  overflow: "hidden",
214
+};
215
+
216
+const baseStyle = {
217
+  zIndex: 1,
218
+  width: "50%",
219
+  flex: 1,
220
+};
221
+
222
+const starStyle = {
223
+  position: "relative",
224
+  width: "100%",
225
+  height: "100%",
226
+  backgroundBlendMode: "multiply",
227
+  background:
228
+    "linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(195,203,56,1) 86%)",
229
+};
230
+
231
+const textAreaStyle = {
232
+  background:
233
+    "linear-gradient(194deg, rgba(255,255,255,0.3650366748166259) 0%, rgba(195,203,56,0.2885085574572127) 86%)",
234
+  backdropFilter: "blur(10px)",
235
+  color: "#000000FF",
236
+  opacity: 0,
237
+  padding: "2",
238
+  width: "200px",
239
+  height: "100px",
240
+  zIndex: 0,
241
+  borderRadius: "10px",
242
+};
243
+
244
+const bgSquareStyle = {
245
+  width: "80%",
246
+  height: "70%",
247
+  position: "absolute",
248
+  top: 300,
249
+  left: 5,
250
+  backgroundImage:
251
+    "linear-gradient(#07b503 1px, transparent 1px, transparent calc(100% - 1px), rgba(195,203,56,1) calc(100% - 1px)), linear-gradient(90deg, rgba(195,203,56,1) 1px, transparent 1px, transparent calc(100% - 1px), rgba(195,203,56,1) calc(100% - 1px))",
252
+  backgroundSize: "3.3333333333333335% 3.3333333333333335%",
253
+  border: "1px solid rgba(195,203,56,1)",
254
+  zIndex: 0,
255
+};
256
+
257
+export default GlobalTop;

+ 5
- 0
vite-wrapped/src/steps/stepProps.ts View File

@@ -0,0 +1,5 @@
1
+import { MotionValue } from "framer-motion";
2
+
3
+export type StepProps = {
4
+    progress: MotionValue<number>;
5
+}

+ 208
- 0
vite-wrapped/src/steps/welcome.tsx View File

@@ -0,0 +1,208 @@
1
+import { motion, Variants } from "framer-motion";
2
+import { StepProps } from "./stepProps.ts";
3
+import Masked from "../components/mask/Masked.tsx";
4
+import React, { useState } from "react";
5
+
6
+type VariantProps = {
7
+  delay: number;
8
+};
9
+
10
+type NudgedVariantProps = VariantProps & {
11
+  nudge: number;
12
+  id: number;
13
+};
14
+
15
+const starVariants: Variants = {
16
+  "1": (props: VariantProps) => ({
17
+    rotate: 360,
18
+    scale: 3,
19
+    transition: {
20
+      duration: 2,
21
+      repeat: 0,
22
+      delay: 1.5 + props.delay,
23
+    },
24
+  }),
25
+  "2": (props: VariantProps) => ({
26
+    scale: 10,
27
+    x: props.delay,
28
+    rotate: props.delay + 365 * 2,
29
+    transition: {
30
+      duration: 0.1,
31
+      repeat: 0,
32
+    },
33
+  }),
34
+  "3": (_: VariantProps) => ({
35
+    scale: 10,
36
+  }),
37
+};
38
+
39
+
40
+const letterVariants: Variants = {
41
+  "1": (props: NudgedVariantProps) => ({
42
+    rotate: 360 * props.nudge + props.id * 5,
43
+    x: -props.nudge * props.id * 15,
44
+    scale: 9 - props.id / 2,
45
+    transition: {
46
+      duration: 2,
47
+      ease: "easeInOut",
48
+      //delay: 1.5 + props.delay + props.id / 10,
49
+      delay: 1.5 + props.id/50,
50
+    },
51
+  }),
52
+  "2": (props: NudgedVariantProps) => ({
53
+    rotate: 360 * props.nudge + props.id * 5,
54
+    x: -props.nudge * props.id * 15,
55
+    z: 0.1,
56
+    scale: 9 - props.id / 2,
57
+    transition: {
58
+      duration: 2,
59
+      ease: "linear",
60
+      //delay: props.delay + props.id / 10,
61
+    },
62
+  }),
63
+  "3": (props: NudgedVariantProps) => ({
64
+    rotate: 361 * props.nudge,
65
+    x: -props.nudge * 30,
66
+    scale: 4,
67
+    z: 0.2,
68
+    transition: {
69
+      duration: 1,
70
+      ease: "easeInOut",
71
+      delay: props.delay,
72
+    },
73
+  }),
74
+};
75
+
76
+type RunningAnimations = {
77
+  [key: string]: number;
78
+};
79
+
80
+function Welcome({ progress }: StepProps) {
81
+  const [starVariant, setStarVariant] = useState("1");
82
+  const [runningAnimations, setRunningAnimations] = useState<RunningAnimations>(
83
+    {}
84
+  );
85
+
86
+  const animationComplete = (variant: string) => {
87
+    const newRunningAnimations = { ...runningAnimations };
88
+    if (newRunningAnimations[variant] > 0) {
89
+      newRunningAnimations[variant]--;
90
+      setRunningAnimations(newRunningAnimations);
91
+    } else {
92
+      if (variant === "1") {
93
+        setStarVariant("2");
94
+      } else if (variant === "2") {
95
+        setStarVariant("3");
96
+      }
97
+    }
98
+  };
99
+
100
+  const animationStarted = (variant: string) => {
101
+    const newRunningAnimations = { ...runningAnimations };
102
+    if (!newRunningAnimations[variant]) {
103
+      newRunningAnimations[variant] = 1;
104
+    } else {
105
+      newRunningAnimations[variant]++;
106
+    }
107
+    setRunningAnimations(newRunningAnimations);
108
+  };
109
+
110
+  return (
111
+    <div style={pageStyle as React.CSSProperties}>
112
+      {Array.from({ length: 10 }).map((_, i) => (
113
+        <motion.div
114
+          key={i}
115
+          variants={starVariants}
116
+          style={baseStyle as React.CSSProperties}
117
+          animate={starVariant}
118
+          custom={{ delay: i * 0.05 }}
119
+          onAnimationStart={() => animationStarted(starVariant)}
120
+          onAnimationComplete={() => animationComplete(starVariant)}
121
+        >
122
+          <div style={{ width: "800px", height: "800px" }}>
123
+            <Masked mask="Square" style={starStyle} />
124
+          </div>
125
+        </motion.div>
126
+      ))}
127
+
128
+      <motion.div
129
+        style={{
130
+          display: "flex",
131
+          position: "relative",
132
+        }}
133
+      >
134
+        {Array.from({ length: 10 }).map((_, i) => (
135
+          <motion.div
136
+            variants={letterVariants}
137
+            animate={starVariant}
138
+            custom={{ delay: 0, nudge: 1, id: 9-i }}
139
+            style={
140
+              letterStyle({
141
+                delay: 0.1,
142
+                nudge: -1,
143
+                id: 9-i,
144
+              }) as React.CSSProperties
145
+            }
146
+          >
147
+            2
148
+          </motion.div>
149
+        ))}
150
+        {Array.from({ length: 10 }).map((_, i) => (
151
+          <motion.div
152
+            variants={letterVariants}
153
+            animate={starVariant}
154
+            custom={{ delay: 0.1, nudge: -1, id: 9-i }}
155
+            style={
156
+              letterStyle({
157
+                delay: 0.1,
158
+                nudge: -1,
159
+                id: 9-i,
160
+              }) as React.CSSProperties
161
+            }
162
+          >
163
+            4
164
+          </motion.div>
165
+        ))}
166
+      </motion.div>
167
+    </div>
168
+  );
169
+}
170
+
171
+const pageStyle = {
172
+  position: "relative",
173
+  width: "100%",
174
+  height: "100%",
175
+  display: "flex",
176
+  justifyContent: "center",
177
+  alignItems: "center",
178
+  overflow: "hidden",
179
+};
180
+
181
+const baseStyle = {
182
+  position: "absolute",
183
+  top: "calc(50% - 400px)",
184
+  left: "calc(50% - 400px)",
185
+  zIndex: 1,
186
+};
187
+
188
+const starStyle = {
189
+  position: "relative",
190
+  width: "100%",
191
+  height: "100%",
192
+  backgroundBlendMode: "multiply",
193
+  backgroundImage:
194
+    "radial-gradient(circle at center center,rgb(95, 247, 68),rgb(247, 255, 19)), repeating-radial-gradient(circle at center center,rgb(0, 255, 140),rgb(77, 247, 68), 10px, transparent 90px, transparent 10px)",
195
+};
196
+
197
+const letterStyle = (props: NudgedVariantProps) => ({
198
+  transform: `translateX(${Math.floor(props.nudge * props.id * 100)}px)`,
199
+  scale: 0,
200
+  fontSize: "3em",
201
+  position: "absolute",
202
+  color: "transparent",
203
+  backgroundClip: "text",
204
+  backgroundImage: "linear-gradient(90deg, rgb(95, 247, 68),rgb(247, 255, 19))",
205
+  //webkitTextStroke: "1px white",
206
+});
207
+
208
+export default Welcome;

+ 1
- 0
vite-wrapped/src/vite-env.d.ts View File

@@ -0,0 +1 @@
1
+/// <reference types="vite/client" />

+ 5
- 0
vite-wrapped/tsconfig.json View File

@@ -0,0 +1,5 @@
1
+{
2
+  "compilerOptions": {
3
+    "lib": ["esnext", "dom"]
4
+  }
5
+}

+ 8
- 0
vite-wrapped/vite.config.ts View File

@@ -0,0 +1,8 @@
1
+import { defineConfig } from 'vite'
2
+import deno from '@deno/vite-plugin'
3
+import react from '@vitejs/plugin-react-swc'
4
+
5
+// https://vite.dev/config/
6
+export default defineConfig({
7
+  plugins: [deno(), react()],
8
+})

Loading…
Cancel
Save