@walrosproject/ui 0.3.4 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,359 +1,273 @@
1
1
  // src/components/WalrosPortalTransition.tsx
2
- import {
3
- useEffect,
4
- useId,
5
- useLayoutEffect,
6
- useRef,
7
- useState
8
- } from "react";
9
- import { gsap } from "gsap";
2
+ import { useState as useState3 } from "react";
3
+ import { createPortal } from "react-dom";
4
+
5
+ // src/assets.ts
6
+ var walrosPortalSilhouetteUrl = new URL(
7
+ "./assets/walros-portal-silhouette.svg",
8
+ import.meta.url
9
+ ).href;
10
+ var walrosLogoChromeUrl = new URL(
11
+ "./assets/walros-logo-chrome.png",
12
+ import.meta.url
13
+ ).href;
14
+ var walrosWordmarkLightUrl = new URL(
15
+ "./assets/walros-wordmark-light.png",
16
+ import.meta.url
17
+ ).href;
18
+ var walrosWordmarkDarkUrl = new URL(
19
+ "./assets/walros-wordmark-dark.png",
20
+ import.meta.url
21
+ ).href;
22
+ var walrosThemeGlitchSoundUrl = new URL(
23
+ "./assets/walros-theme-glitch.mp3",
24
+ import.meta.url
25
+ ).href;
26
+
27
+ // src/components/Walros3DPortalTransition.tsx
28
+ import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
29
+ import gsap from "gsap";
30
+
31
+ // src/portalSilhouette.ts
32
+ var walrosPortalPaths = [
33
+ "M 244.00 354.00 C 240.50 377.67, 240.17 413.67, 242.00 434.00 C 243.83 454.33, 249.50 464.50, 255.00 476.00 C 260.50 487.50, 268.50 496.00, 275.00 503.00 C 281.50 510.00, 287.17 513.67, 294.00 518.00 C 300.83 522.33, 306.00 525.67, 316.00 529.00 C 326.00 532.33, 336.33 536.67, 354.00 538.00 C 371.67 539.33, 397.83 540.83, 422.00 537.00 C 446.17 533.17, 474.17 525.33, 499.00 515.00 C 523.83 504.67, 550.00 489.33, 571.00 475.00 C 592.00 460.67, 615.00 436.50, 625.00 429.00 C 635.00 421.50, 623.67 424.00, 631.00 430.00 C 638.33 436.00, 656.67 454.83, 669.00 465.00 C 681.33 475.17, 690.00 482.17, 705.00 491.00 C 720.00 499.83, 743.67 511.50, 759.00 518.00 C 774.33 524.50, 784.00 526.83, 797.00 530.00 C 810.00 533.17, 820.00 535.83, 837.00 537.00 C 854.00 538.17, 881.17 539.00, 899.00 537.00 C 916.83 535.00, 932.00 530.00, 944.00 525.00 C 956.00 520.00, 962.00 516.17, 971.00 507.00 C 980.00 497.83, 991.67 482.83, 998.00 470.00 C 1004.33 457.17, 1007.17 446.33, 1009.00 430.00 C 1010.83 413.67, 1010.83 390.50, 1009.00 372.00 C 1007.17 353.50, 1003.83 337.17, 998.00 319.00 C 992.17 300.83, 983.67 280.17, 974.00 263.00 C 964.33 245.83, 952.00 229.67, 940.00 216.00 C 928.00 202.33, 917.17 192.00, 902.00 181.00 C 886.83 170.00, 866.50 158.17, 849.00 150.00 C 831.50 141.83, 816.67 137.00, 797.00 132.00 C 777.33 127.00, 756.67 122.83, 731.00 120.00 C 705.33 117.17, 673.67 115.33, 643.00 115.00 C 612.33 114.67, 575.67 115.67, 547.00 118.00 C 518.33 120.33, 496.17 123.00, 471.00 129.00 C 445.83 135.00, 418.33 143.50, 396.00 154.00 C 373.67 164.50, 353.50 178.50, 337.00 192.00 C 320.50 205.50, 309.33 218.33, 297.00 235.00 C 284.67 251.67, 271.83 272.17, 263.00 292.00 C 254.17 311.83, 247.50 330.33, 244.00 354.00 Z",
34
+ "M 289.00 536.00 C 283.17 535.50, 284.83 533.00, 284.00 544.00 C 283.17 555.00, 282.67 578.67, 284.00 602.00 C 285.33 625.33, 287.00 651.83, 292.00 684.00 C 297.00 716.17, 306.33 763.50, 314.00 795.00 C 321.67 826.50, 328.67 847.17, 338.00 873.00 C 347.33 898.83, 357.00 923.67, 370.00 950.00 C 383.00 976.33, 399.67 1006.00, 416.00 1031.00 C 432.33 1056.00, 455.33 1084.50, 468.00 1100.00 C 480.67 1115.50, 486.17 1119.33, 492.00 1124.00 C 497.83 1128.67, 500.67 1127.67, 503.00 1128.00 C 505.33 1128.33, 505.50 1127.50, 506.00 1126.00 C 506.50 1124.50, 510.33 1132.00, 506.00 1119.00 C 501.67 1106.00, 488.17 1073.83, 480.00 1048.00 C 471.83 1022.17, 462.67 987.67, 457.00 964.00 C 451.33 940.33, 449.17 927.33, 446.00 906.00 C 442.83 884.67, 439.50 865.00, 438.00 836.00 C 436.50 807.00, 436.50 758.33, 437.00 732.00 C 437.50 705.67, 439.00 696.33, 441.00 678.00 C 443.00 659.67, 444.83 644.00, 449.00 622.00 C 453.17 600.00, 464.50 559.00, 466.00 546.00 C 467.50 533.00, 468.67 542.50, 458.00 544.00 C 447.33 545.50, 418.33 553.17, 402.00 555.00 C 385.67 556.83, 373.83 556.33, 360.00 555.00 C 346.17 553.67, 330.83 550.17, 319.00 547.00 C 307.17 543.83, 294.83 536.50, 289.00 536.00 Z",
35
+ "M 961.00 537.00 C 948.50 539.17, 907.83 552.00, 889.00 555.00 C 870.17 558.00, 864.17 557.00, 848.00 555.00 C 831.83 553.00, 802.83 545.00, 792.00 543.00 C 781.17 541.00, 784.83 542.50, 783.00 543.00 C 781.17 543.50, 778.67 533.83, 781.00 546.00 C 783.33 558.17, 792.67 592.33, 797.00 616.00 C 801.33 639.67, 804.83 668.83, 807.00 688.00 C 809.17 707.17, 809.67 709.00, 810.00 731.00 C 810.33 753.00, 810.50 792.83, 809.00 820.00 C 807.50 847.17, 804.50 870.50, 801.00 894.00 C 797.50 917.50, 794.00 936.17, 788.00 961.00 C 782.00 985.83, 773.50 1016.67, 765.00 1043.00 C 756.50 1069.33, 741.33 1104.83, 737.00 1119.00 C 732.67 1133.17, 736.67 1127.17, 739.00 1128.00 C 741.33 1128.83, 745.83 1127.83, 751.00 1124.00 C 756.17 1120.17, 757.33 1120.50, 770.00 1105.00 C 782.67 1089.50, 811.33 1053.50, 827.00 1031.00 C 842.67 1008.50, 851.17 995.00, 864.00 970.00 C 876.83 945.00, 892.83 910.00, 904.00 881.00 C 915.17 852.00, 923.00 826.67, 931.00 796.00 C 939.00 765.33, 946.67 728.67, 952.00 697.00 C 957.33 665.33, 961.00 631.83, 963.00 606.00 C 965.00 580.17, 964.33 553.50, 964.00 542.00 C 963.67 530.50, 973.50 534.83, 961.00 537.00 Z"
36
+ ];
37
+
38
+ // src/components/Walros3DPortalTransition.tsx
10
39
  import { jsx, jsxs } from "react/jsx-runtime";
11
- var SILHOUETTE_VIEWBOX = 1254;
12
40
  var FOCUS_X = 626;
13
41
  var FOCUS_Y = 333;
42
+ var VISIBLE_LEFT = 244;
43
+ var VISIBLE_RIGHT = 1010;
44
+ var VISIBLE_TOP = 115;
45
+ var VISIBLE_BOTTOM = 1128;
14
46
  var PORTAL_RADIUS = 190;
15
- var WALROS_PORTAL_SILHOUETTE_URL = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgICAgdmlld0JveD0iMCAwIDEyNTQgMTI1NCIKICAgICB3aWR0aD0iMTI1NCIgaGVpZ2h0PSIxMjU0IgogICAgIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iU2lsdWV0YSBXYWxyT1MiPgogIDxnIGZpbGw9IiMwMDAwMDAiPgogICAgPHBhdGggZD0iTSAyNDQuMDAgMzU0LjAwIEMgMjQwLjUwIDM3Ny42NywgMjQwLjE3IDQxMy42NywgMjQyLjAwIDQzNC4wMCBDIDI0My44MyA0NTQuMzMsIDI0OS41MCA0NjQuNTAsIDI1NS4wMCA0NzYuMDAgQyAyNjAuNTAgNDg3LjUwLCAyNjguNTAgNDk2LjAwLCAyNzUuMDAgNTAzLjAwIEMgMjgxLjUwIDUxMC4wMCwgMjg3LjE3IDUxMy42NywgMjk0LjAwIDUxOC4wMCBDIDMwMC44MyA1MjIuMzMsIDMwNi4wMCA1MjUuNjcsIDMxNi4wMCA1MjkuMDAgQyAzMjYuMDAgNTMyLjMzLCAzMzYuMzMgNTM2LjY3LCAzNTQuMDAgNTM4LjAwIEMgMzcxLjY3IDUzOS4zMywgMzk3LjgzIDU0MC44MywgNDIyLjAwIDUzNy4wMCBDIDQ0Ni4xNyA1MzMuMTcsIDQ3NC4xNyA1MjUuMzMsIDQ5OS4wMCA1MTUuMDAgQyA1MjMuODMgNTA0LjY3LCA1NTAuMDAgNDg5LjMzLCA1NzEuMDAgNDc1LjAwIEMgNTkyLjAwIDQ2MC42NywgNjE1LjAwIDQzNi41MCwgNjI1LjAwIDQyOS4wMCBDIDYzNS4wMCA0MjEuNTAsIDYyMy42NyA0MjQuMDAsIDYzMS4wMCA0MzAuMDAgQyA2MzguMzMgNDM2LjAwLCA2NTYuNjcgNDU0LjgzLCA2NjkuMDAgNDY1LjAwIEMgNjgxLjMzIDQ3NS4xNywgNjkwLjAwIDQ4Mi4xNywgNzA1LjAwIDQ5MS4wMCBDIDcyMC4wMCA0OTkuODMsIDc0My42NyA1MTEuNTAsIDc1OS4wMCA1MTguMDAgQyA3NzQuMzMgNTI0LjUwLCA3ODQuMDAgNTI2LjgzLCA3OTcuMDAgNTMwLjAwIEMgODEwLjAwIDUzMy4xNywgODIwLjAwIDUzNS44MywgODM3LjAwIDUzNy4wMCBDIDg1NC4wMCA1MzguMTcsIDg4MS4xNyA1MzkuMDAsIDg5OS4wMCA1MzcuMDAgQyA5MTYuODMgNTM1LjAwLCA5MzIuMDAgNTMwLjAwLCA5NDQuMDAgNTI1LjAwIEMgOTU2LjAwIDUyMC4wMCwgOTYyLjAwIDUxNi4xNywgOTcxLjAwIDUwNy4wMCBDIDk4MC4wMCA0OTcuODMsIDk5MS42NyA0ODIuODMsIDk5OC4wMCA0NzAuMDAgQyAxMDA0LjMzIDQ1Ny4xNywgMTAwNy4xNyA0NDYuMzMsIDEwMDkuMDAgNDMwLjAwIEMgMTAxMC44MyA0MTMuNjcsIDEwMTAuODMgMzkwLjUwLCAxMDA5LjAwIDM3Mi4wMCBDIDEwMDcuMTcgMzUzLjUwLCAxMDAzLjgzIDMzNy4xNywgOTk4LjAwIDMxOS4wMCBDIDk5Mi4xNyAzMDAuODMsIDk4My42NyAyODAuMTcsIDk3NC4wMCAyNjMuMDAgQyA5NjQuMzMgMjQ1LjgzLCA5NTIuMDAgMjI5LjY3LCA5NDAuMDAgMjE2LjAwIEMgOTI4LjAwIDIwMi4zMywgOTE3LjE3IDE5Mi4wMCwgOTAyLjAwIDE4MS4wMCBDIDg4Ni44MyAxNzAuMDAsIDg2Ni41MCAxNTguMTcsIDg0OS4wMCAxNTAuMDAgQyA4MzEuNTAgMTQxLjgzLCA4MTYuNjcgMTM3LjAwLCA3OTcuMDAgMTMyLjAwIEMgNzc3LjMzIDEyNy4wMCwgNzU2LjY3IDEyMi44MywgNzMxLjAwIDEyMC4wMCBDIDcwNS4zMyAxMTcuMTcsIDY3My42NyAxMTUuMzMsIDY0My4wMCAxMTUuMDAgQyA2MTIuMzMgMTE0LjY3LCA1NzUuNjcgMTE1LjY3LCA1NDcuMDAgMTE4LjAwIEMgNTE4LjMzIDEyMC4zMywgNDk2LjE3IDEyMy4wMCwgNDcxLjAwIDEyOS4wMCBDIDQ0NS44MyAxMzUuMDAsIDQxOC4zMyAxNDMuNTAsIDM5Ni4wMCAxNTQuMDAgQyAzNzMuNjcgMTY0LjUwLCAzNTMuNTAgMTc4LjUwLCAzMzcuMDAgMTkyLjAwIEMgMzIwLjUwIDIwNS41MCwgMzA5LjMzIDIxOC4zMywgMjk3LjAwIDIzNS4wMCBDIDI4NC42NyAyNTEuNjcsIDI3MS44MyAyNzIuMTcsIDI2My4wMCAyOTIuMDAgQyAyNTQuMTcgMzExLjgzLCAyNDcuNTAgMzMwLjMzLCAyNDQuMDAgMzU0LjAwIFoiLz48cGF0aCBkPSJNIDI4OS4wMCA1MzYuMDAgQyAyODMuMTcgNTM1LjUwLCAyODQuODMgNTMzLjAwLCAyODQuMDAgNTQ0LjAwIEMgMjgzLjE3IDU1NS4wMCwgMjgyLjY3IDU3OC42NywgMjg0LjAwIDYwMi4wMCBDIDI4NS4zMyA2MjUuMzMsIDI4Ny4wMCA2NTEuODMsIDI5Mi4wMCA2ODQuMDAgQyAyOTcuMDAgNzE2LjE3LCAzMDYuMzMgNzYzLjUwLCAzMTQuMDAgNzk1LjAwIEMgMzIxLjY3IDgyNi41MCwgMzI4LjY3IDg0Ny4xNywgMzM4LjAwIDg3My4wMCBDIDM0Ny4zMyA4OTguODMsIDM1Ny4wMCA5MjMuNjcsIDM3MC4wMCA5NTAuMDAgQyAzODMuMDAgOTc2LjMzLCAzOTkuNjcgMTAwNi4wMCwgNDE2LjAwIDEwMzEuMDAgQyA0MzIuMzMgMTA1Ni4wMCwgNDU1LjMzIDEwODQuNTAsIDQ2OC4wMCAxMTAwLjAwIEMgNDgwLjY3IDExMTUuNTAsIDQ4Ni4xNyAxMTE5LjMzLCA0OTIuMDAgMTEyNC4wMCBDIDQ5Ny44MyAxMTI4LjY3LCA1MDAuNjcgMTEyNy42NywgNTAzLjAwIDExMjguMDAgQyA1MDUuMzMgMTEyOC4zMywgNTA1LjUwIDExMjcuNTAsIDUwNi4wMCAxMTI2LjAwIEMgNTA2LjUwIDExMjQuNTAsIDUxMC4zMyAxMTMyLjAwLCA1MDYuMDAgMTExOS4wMCBDIDUwMS42NyAxMTA2LjAwLCA0ODguMTcgMTA3My44MywgNDgwLjAwIDEwNDguMDAgQyA0NzEuODMgMTAyMi4xNywgNDYyLjY3IDk4Ny42NywgNDU3LjAwIDk2NC4wMCBDIDQ1MS4zMyA5NDAuMzMsIDQ0OS4xNyA5MjcuMzMsIDQ0Ni4wMCA5MDYuMDAgQyA0NDIuODMgODg0LjY3LCA0MzkuNTAgODY1LjAwLCA0MzguMDAgODM2LjAwIEMgNDM2LjUwIDgwNy4wMCwgNDM2LjUwIDc1OC4zMywgNDM3LjAwIDczMi4wMCBDIDQzNy41MCA3MDUuNjcsIDQzOS4wMCA2OTYuMzMsIDQ0MS4wMCA2NzguMDAgQyA0NDMuMDAgNjU5LjY3LCA0NDQuODMgNjQ0LjAwLCA0NDkuMDAgNjIyLjAwIEMgNDUzLjE3IDYwMC4wMCwgNDY0LjUwIDU1OS4wMCwgNDY2LjAwIDU0Ni4wMCBDIDQ2Ny41MCA1MzMuMDAsIDQ2OC42NyA1NDIuNTAsIDQ1OC4wMCA1NDQuMDAgQyA0NDcuMzMgNTQ1LjUwLCA0MTguMzMgNTUzLjE3LCA0MDIuMDAgNTU1LjAwIEMgMzg1LjY3IDU1Ni44MywgMzczLjgzIDU1Ni4zMywgMzYwLjAwIDU1NS4wMCBDIDM0Ni4xNyA1NTMuNjcsIDMzMC44MyA1NTAuMTcsIDMxOS4wMCA1NDcuMDAgQyAzMDcuMTcgNTQzLjgzLCAyOTQuODMgNTM2LjUwLCAyODkuMDAgNTM2LjAwIFoiLz48cGF0aCBkPSJNIDk2MS4wMCA1MzcuMDAgQyA5NDguNTAgNTM5LjE3LCA5MDcuODMgNTUyLjAwLCA4ODkuMDAgNTU1LjAwIEMgODcwLjE3IDU1OC4wMCwgODY0LjE3IDU1Ny4wMCwgODQ4LjAwIDU1NS4wMCBDIDgzMS44MyA1NTMuMDAsIDgwMi44MyA1NDUuMDAsIDc5Mi4wMCA1NDMuMDAgQyA3ODEuMTcgNTQxLjAwLCA3ODQuODMgNTQyLjUwLCA3ODMuMDAgNTQzLjAwIEMgNzgxLjE3IDU0My41MCwgNzc4LjY3IDUzMy44MywgNzgxLjAwIDU0Ni4wMCBDIDc4My4zMyA1NTguMTcsIDc5Mi42NyA1OTIuMzMsIDc5Ny4wMCA2MTYuMDAgQyA4MDEuMzMgNjM5LjY3LCA4MDQuODMgNjY4LjgzLCA4MDcuMDAgNjg4LjAwIEMgODA5LjE3IDcwNy4xNywgODA5LjY3IDcwOS4wMCwgODEwLjAwIDczMS4wMCBDIDgxMC4zMyA3NTMuMDAsIDgxMC41MCA3OTIuODMsIDgwOS4wMCA4MjAuMDAgQyA4MDcuNTAgODQ3LjE3LCA4MDQuNTAgODcwLjUwLCA4MDEuMDAgODk0LjAwIEMgNzk3LjUwIDkxNy41MCwgNzk0LjAwIDkzNi4xNywgNzg4LjAwIDk2MS4wMCBDIDc4Mi4wMCA5ODUuODMsIDc3My41MCAxMDE2LjY3LCA3NjUuMDAgMTA0My4wMCBDIDc1Ni41MCAxMDY5LjMzLCA3NDEuMzMgMTEwNC44MywgNzM3LjAwIDExMTkuMDAgQyA3MzIuNjcgMTEzMy4xNywgNzM2LjY3IDExMjcuMTcsIDczOS4wMCAxMTI4LjAwIEMgNzQxLjMzIDExMjguODMsIDc0NS44MyAxMTI3LjgzLCA3NTEuMDAgMTEyNC4wMCBDIDc1Ni4xNyAxMTIwLjE3LCA3NTcuMzMgMTEyMC41MCwgNzcwLjAwIDExMDUuMDAgQyA3ODIuNjcgMTA4OS41MCwgODExLjMzIDEwNTMuNTAsIDgyNy4wMCAxMDMxLjAwIEMgODQyLjY3IDEwMDguNTAsIDg1MS4xNyA5OTUuMDAsIDg2NC4wMCA5NzAuMDAgQyA4NzYuODMgOTQ1LjAwLCA4OTIuODMgOTEwLjAwLCA5MDQuMDAgODgxLjAwIEMgOTE1LjE3IDg1Mi4wMCwgOTIzLjAwIDgyNi42NywgOTMxLjAwIDc5Ni4wMCBDIDkzOS4wMCA3NjUuMzMsIDk0Ni42NyA3MjguNjcsIDk1Mi4wMCA2OTcuMDAgQyA5NTcuMzMgNjY1LjMzLCA5NjEuMDAgNjMxLjgzLCA5NjMuMDAgNjA2LjAwIEMgOTY1LjAwIDU4MC4xNywgOTY0LjMzIDU1My41MCwgOTY0LjAwIDU0Mi4wMCBDIDk2My42NyA1MzAuNTAsIDk3My41MCA1MzQuODMsIDk2MS4wMCA1MzcuMDAgWiIvPgogIDwvZz4KPC9zdmc+Cg==";
16
- function getStorage(mode) {
17
- if (typeof window === "undefined") return null;
18
- try {
19
- const storage = mode === "session" ? window.sessionStorage : window.localStorage;
20
- const testKey = "__walros_portal_test__";
21
- storage.setItem(testKey, "1");
22
- storage.removeItem(testKey);
23
- return storage;
24
- } catch {
25
- return null;
26
- }
27
- }
28
- function shouldRunTransition(enabled, storageMode, storageKey) {
29
- if (!enabled) return false;
30
- if (storageMode === "always") return true;
31
- if (typeof window === "undefined") return false;
32
- const storage = getStorage(storageMode);
33
- if (!storage) return true;
34
- try {
35
- return storage.getItem(storageKey) == null;
36
- } catch {
37
- return true;
38
- }
39
- }
40
- function WalrosPortalTransition({
41
- duration = 900,
47
+ function Walros3DPortalTransition({
48
+ origin,
42
49
  onComplete,
43
- backgroundColor = "#000000",
44
- enabled = true,
45
- storageMode = "always",
46
- storageKey = "walros-portal-seen",
47
- zIndex = 9999
50
+ duration = 900,
51
+ backgroundColor = "#000",
52
+ zIndex = 10001,
53
+ lockScroll = true,
54
+ logoUrl = false
48
55
  }) {
49
- const [shouldRun] = useState(
50
- () => shouldRunTransition(enabled, storageMode, storageKey)
51
- );
52
- const [visible, setVisible] = useState(shouldRun);
53
- const [size, setSize] = useState({ w: 0, h: 0 });
54
- const overlayRef = useRef(null);
55
- const maskScaleRef = useRef(null);
56
- const completedRef = useRef(false);
57
- const startedRef = useRef(false);
58
- const rafRef = useRef(null);
59
- const timelineRef = useRef(null);
60
- const previousOverflowRef = useRef("");
61
- const uid = useId().replace(/[:]/g, "_");
62
- const maskId = `walros-portal-mask-${uid}`;
56
+ const [visible, setVisible] = useState(true);
57
+ const [viewport, setViewport] = useState(() => ({
58
+ width: typeof window === "undefined" ? 0 : window.innerWidth,
59
+ height: typeof window === "undefined" ? 0 : window.innerHeight
60
+ }));
61
+ const overlay = useRef(null);
62
+ const portal = useRef(null);
63
+ const logo = useRef(null);
64
+ const completeRef = useRef(onComplete);
65
+ completeRef.current = onComplete;
66
+ const maskId = `walros-3d-portal-${useId().replace(/:/g, "_")}`;
63
67
  useEffect(() => {
64
- if (!visible) return;
65
- const element = overlayRef.current;
66
- if (!element) return;
67
- const measure = () => {
68
- const rect = element.getBoundingClientRect();
69
- setSize(
70
- (previous) => previous.w === rect.width && previous.h === rect.height ? previous : { w: rect.width, h: rect.height }
71
- );
72
- };
73
- measure();
74
- const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
75
- observer?.observe(element);
76
- window.addEventListener("resize", measure);
77
- return () => {
78
- observer?.disconnect();
79
- window.removeEventListener("resize", measure);
80
- };
81
- }, [visible]);
68
+ const updateViewport = () => setViewport({ width: window.innerWidth, height: window.innerHeight });
69
+ window.addEventListener("resize", updateViewport);
70
+ return () => window.removeEventListener("resize", updateViewport);
71
+ }, []);
82
72
  useLayoutEffect(() => {
83
- if (!enabled) {
84
- setVisible(false);
85
- return;
73
+ if (!visible || viewport.width <= 0 || viewport.height <= 0 || !overlay.current || !portal.current) return;
74
+ const previousOverflow = document.body.style.overflow;
75
+ if (lockScroll) document.body.style.overflow = "hidden";
76
+ const visibleWidth = VISIBLE_RIGHT - VISIBLE_LEFT;
77
+ const visibleHeight = VISIBLE_BOTTOM - VISIBLE_TOP;
78
+ const visualCenterX = (VISIBLE_LEFT + VISIBLE_RIGHT) / 2;
79
+ const visualCenterY = (VISIBLE_TOP + VISIBLE_BOTTOM) / 2;
80
+ const initialScale = Math.min(origin.width / visibleWidth, origin.height / visibleHeight) * 0.985;
81
+ const initialX = origin.centerX - (visualCenterX - FOCUS_X) * initialScale;
82
+ const initialY = origin.centerY - (visualCenterY - FOCUS_Y) * initialScale;
83
+ const diagonal = Math.hypot(viewport.width, viewport.height) / 2;
84
+ const finalScale = (diagonal + 80) / PORTAL_RADIUS * 1.15;
85
+ const targetX = viewport.width / 2 - (visualCenterX - FOCUS_X) * finalScale;
86
+ const targetY = viewport.height / 2 - (visualCenterY - FOCUS_Y) * finalScale;
87
+ const total = duration / 1e3;
88
+ gsap.set(portal.current, {
89
+ x: initialX,
90
+ y: initialY,
91
+ scale: initialScale,
92
+ svgOrigin: "0 0"
93
+ });
94
+ if (logo.current) {
95
+ gsap.set(logo.current, {
96
+ x: initialX,
97
+ y: initialY,
98
+ scale: initialScale,
99
+ opacity: 1,
100
+ svgOrigin: "0 0"
101
+ });
86
102
  }
87
- if (!visible) return;
88
- if (completedRef.current || startedRef.current) return;
89
- if (size.w <= 0 || size.h <= 0) return;
90
- const overlay = overlayRef.current;
91
- const maskScale = maskScaleRef.current;
92
- if (!overlay || !maskScale) return;
93
- startedRef.current = true;
94
- previousOverflowRef.current = document.body.style.overflow;
95
- document.body.style.overflow = "hidden";
103
+ gsap.set(overlay.current, { opacity: 1 });
96
104
  const finish = () => {
97
- if (completedRef.current) return;
98
- completedRef.current = true;
99
- document.body.style.overflow = previousOverflowRef.current;
100
- if (storageMode !== "always") {
101
- const storage = getStorage(storageMode);
102
- try {
103
- storage?.setItem(storageKey, "1");
104
- } catch {
105
- }
106
- }
105
+ if (lockScroll) document.body.style.overflow = previousOverflow;
107
106
  setVisible(false);
108
- onComplete?.();
107
+ completeRef.current?.();
109
108
  };
110
- const reducedMotion = window.matchMedia(
111
- "(prefers-reduced-motion: reduce)"
112
- ).matches;
109
+ const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
110
+ const timeline = gsap.timeline({ onComplete: finish });
113
111
  if (reducedMotion) {
114
- const timeline = gsap.timeline({ onComplete: finish });
115
- timelineRef.current = timeline;
116
- timeline.to(overlay, {
117
- opacity: 0,
118
- duration: 0.15,
119
- ease: "power1.out"
120
- });
121
- return;
122
- }
123
- rafRef.current = requestAnimationFrame(() => {
124
- rafRef.current = null;
125
- const halfDiagonal = Math.sqrt(size.w * size.w + size.h * size.h) / 2;
126
- const initialPixels2 = Math.min(
127
- 180,
128
- Math.max(120, size.w * 0.12)
129
- );
130
- const initialScale2 = initialPixels2 / SILHOUETTE_VIEWBOX;
131
- const finalScale = (halfDiagonal + 80) / PORTAL_RADIUS * 1.15;
132
- gsap.set(maskScale, {
133
- svgOrigin: "0 0",
134
- scale: initialScale2
112
+ timeline.to(overlay.current, { opacity: 0, duration: 0.15, ease: "power1.out" });
113
+ } else {
114
+ timeline.to([portal.current, logo.current].filter(Boolean), {
115
+ x: targetX,
116
+ y: targetY,
117
+ scale: finalScale,
118
+ duration: total,
119
+ ease: "power2.in"
135
120
  });
136
- gsap.set(overlay, { opacity: 1 });
137
- const totalSeconds = duration / 1e3;
138
- const timeline = gsap.timeline({ onComplete: finish });
139
- timelineRef.current = timeline;
140
- timeline.to(
141
- maskScale,
142
- {
143
- scale: initialScale2 * 1.4,
144
- duration: totalSeconds * 0.15,
145
- ease: "power2.out"
146
- },
147
- 0
148
- );
149
- timeline.to(
150
- maskScale,
151
- {
152
- scale: finalScale,
153
- duration: totalSeconds * 0.85,
154
- ease: "power2.in"
155
- },
156
- totalSeconds * 0.15
157
- );
158
- timeline.to(
159
- overlay,
160
- {
121
+ if (logo.current) {
122
+ timeline.to(logo.current, {
161
123
  opacity: 0,
162
- duration: totalSeconds * 0.1,
163
- ease: "power2.out"
164
- },
165
- totalSeconds * 0.9
166
- );
167
- });
168
- }, [
169
- duration,
170
- enabled,
171
- onComplete,
172
- size,
173
- storageKey,
174
- storageMode,
175
- visible
176
- ]);
177
- useEffect(() => {
178
- return () => {
179
- if (rafRef.current !== null) {
180
- cancelAnimationFrame(rafRef.current);
181
- }
182
- timelineRef.current?.kill();
183
- if (startedRef.current && !completedRef.current) {
184
- document.body.style.overflow = previousOverflowRef.current;
124
+ duration: total * 0.18,
125
+ ease: "power1.out"
126
+ }, total * 0.7);
185
127
  }
128
+ timeline.to(overlay.current, {
129
+ opacity: 0,
130
+ duration: total * 0.1,
131
+ ease: "power2.out"
132
+ }, total * 0.9);
133
+ }
134
+ return () => {
135
+ timeline.kill();
136
+ if (lockScroll) document.body.style.overflow = previousOverflow;
186
137
  };
187
- }, []);
188
- if (!shouldRun || !visible) return null;
189
- const { w, h } = size;
190
- const ready = w > 0 && h > 0;
191
- const viewportCenterX = w / 2;
192
- const viewportCenterY = h / 2;
193
- const initialPixels = Math.min(
194
- 180,
195
- Math.max(120, w * 0.12)
196
- );
197
- const initialScale = initialPixels / SILHOUETTE_VIEWBOX;
198
- const visualCenter = SILHOUETTE_VIEWBOX / 2;
199
- const initialOffsetX = (visualCenter - FOCUS_X) * initialScale;
200
- const initialOffsetY = (visualCenter - FOCUS_Y) * initialScale;
201
- const portalCenterX = viewportCenterX - initialOffsetX;
202
- const portalCenterY = viewportCenterY - initialOffsetY;
138
+ }, [duration, lockScroll, origin, viewport, visible]);
139
+ if (!visible) return null;
203
140
  return /* @__PURE__ */ jsx(
204
141
  "div",
205
142
  {
206
- ref: overlayRef,
207
- "aria-hidden": "true",
143
+ ref: overlay,
144
+ className: "walros-3d-portal",
208
145
  style: {
209
146
  position: "fixed",
210
147
  inset: 0,
211
148
  width: "100vw",
212
149
  height: "100dvh",
213
- zIndex,
214
- pointerEvents: "none",
215
150
  overflow: "hidden",
216
- backgroundColor: ready ? "transparent" : backgroundColor
151
+ pointerEvents: "none",
152
+ zIndex,
153
+ backgroundColor: viewport.width > 0 && viewport.height > 0 ? "transparent" : backgroundColor
217
154
  },
218
- children: ready && /* @__PURE__ */ jsxs(
219
- "svg",
220
- {
221
- width: "100%",
222
- height: "100%",
223
- viewBox: `0 0 ${w} ${h}`,
224
- preserveAspectRatio: "none",
225
- style: {
226
- position: "absolute",
227
- inset: 0,
228
- display: "block"
229
- },
230
- children: [
231
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
232
- "mask",
233
- {
234
- id: maskId,
235
- maskUnits: "userSpaceOnUse",
236
- x: "0",
237
- y: "0",
238
- width: w,
239
- height: h,
240
- children: [
241
- /* @__PURE__ */ jsx(
242
- "rect",
243
- {
244
- x: "0",
245
- y: "0",
246
- width: w,
247
- height: h,
248
- fill: "white"
249
- }
250
- ),
251
- /* @__PURE__ */ jsx(
252
- "g",
253
- {
254
- transform: `translate(${portalCenterX} ${portalCenterY})`,
255
- children: /* @__PURE__ */ jsx(
256
- "g",
257
- {
258
- ref: maskScaleRef,
259
- transform: `scale(${initialScale})`,
260
- children: /* @__PURE__ */ jsx(
261
- "image",
262
- {
263
- href: WALROS_PORTAL_SILHOUETTE_URL,
264
- x: -FOCUS_X,
265
- y: -FOCUS_Y,
266
- width: SILHOUETTE_VIEWBOX,
267
- height: SILHOUETTE_VIEWBOX
268
- }
269
- )
270
- }
271
- )
272
- }
273
- )
274
- ]
275
- }
276
- ) }),
277
- /* @__PURE__ */ jsx(
278
- "rect",
279
- {
280
- x: "0",
281
- y: "0",
282
- width: w,
283
- height: h,
284
- fill: backgroundColor,
285
- mask: `url(#${maskId})`
286
- }
287
- )
288
- ]
289
- }
290
- )
155
+ "aria-hidden": "true",
156
+ children: /* @__PURE__ */ jsxs("svg", { width: "100%", height: "100%", viewBox: `0 0 ${viewport.width} ${viewport.height}`, preserveAspectRatio: "none", style: { display: "block" }, children: [
157
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("mask", { id: maskId, maskUnits: "userSpaceOnUse", x: "0", y: "0", width: viewport.width, height: viewport.height, children: [
158
+ /* @__PURE__ */ jsx("rect", { width: viewport.width, height: viewport.height, fill: "white" }),
159
+ /* @__PURE__ */ jsx("g", { ref: portal, children: /* @__PURE__ */ jsx("g", { transform: `translate(${-FOCUS_X} ${-FOCUS_Y})`, fill: "black", children: walrosPortalPaths.map((path, index) => /* @__PURE__ */ jsx("path", { d: path }, index)) }) })
160
+ ] }) }),
161
+ logoUrl && /* @__PURE__ */ jsx("g", { ref: logo, children: /* @__PURE__ */ jsx("image", { href: logoUrl, x: -FOCUS_X, y: -FOCUS_Y, width: "1254", height: "1254" }) }),
162
+ /* @__PURE__ */ jsx("rect", { width: viewport.width, height: viewport.height, fill: backgroundColor, mask: `url(#${maskId})` })
163
+ ] })
291
164
  }
292
165
  );
293
166
  }
294
167
 
295
- // src/assets.ts
296
- var walrosPortalSilhouetteUrl = new URL(
297
- "./assets/walros-portal-silhouette.svg",
298
- import.meta.url
299
- ).href;
300
- var walrosLogoChromeUrl = new URL(
301
- "./assets/walros-logo-chrome.png",
302
- import.meta.url
303
- ).href;
304
- var walrosWordmarkLightUrl = new URL(
305
- "./assets/walros-wordmark-light.png",
306
- import.meta.url
307
- ).href;
308
- var walrosWordmarkDarkUrl = new URL(
309
- "./assets/walros-wordmark-dark.png",
310
- import.meta.url
311
- ).href;
312
- var walrosThemeGlitchSoundUrl = new URL(
313
- "./assets/walros-theme-glitch.mp3",
314
- import.meta.url
315
- ).href;
316
-
317
- // src/components/WalrosLogo.tsx
318
- import { jsx as jsx2 } from "react/jsx-runtime";
319
- function WalrosLogo({
320
- alt = "WalrOS",
321
- draggable = false,
322
- ...props
323
- }) {
324
- return /* @__PURE__ */ jsx2(
325
- "img",
326
- {
327
- src: walrosLogoChromeUrl,
328
- alt,
329
- draggable,
330
- ...props
331
- }
168
+ // src/components/useOverlayHost.ts
169
+ import { useLayoutEffect as useLayoutEffect2, useState as useState2 } from "react";
170
+ function useOverlayHost(zIndex) {
171
+ const [host] = useState2(
172
+ () => typeof document === "undefined" ? null : document.createElement("div")
332
173
  );
174
+ useLayoutEffect2(() => {
175
+ if (!host) return;
176
+ host.dataset.walrosOverlayRoot = "";
177
+ host.style.position = "fixed";
178
+ host.style.inset = "0";
179
+ host.style.pointerEvents = "none";
180
+ document.documentElement.appendChild(host);
181
+ return () => host.remove();
182
+ }, [host]);
183
+ useLayoutEffect2(() => {
184
+ if (host) host.style.zIndex = String(zIndex);
185
+ }, [host, zIndex]);
186
+ return host;
333
187
  }
334
188
 
335
- // src/components/WalrosWordmark.tsx
336
- import { jsx as jsx3 } from "react/jsx-runtime";
337
- function WalrosWordmark({
338
- variant = "light",
339
- alt = "WalrOS",
340
- draggable = false,
341
- ...props
189
+ // src/components/WalrosPortalTransition.tsx
190
+ import { jsx as jsx2 } from "react/jsx-runtime";
191
+ var IMAGE_SIZE = (width) => Math.min(150, Math.max(90, width * 0.11));
192
+ var IMAGE_WIDTH = 1254;
193
+ var VISIBLE_LEFT2 = 244;
194
+ var VISIBLE_RIGHT2 = 1010;
195
+ var VISIBLE_TOP2 = 115;
196
+ var VISIBLE_BOTTOM2 = 1128;
197
+ function defaultOrigin() {
198
+ if (typeof window === "undefined") return { centerX: 0, centerY: 0, width: 70, height: 90 };
199
+ const size = IMAGE_SIZE(window.innerWidth);
200
+ return {
201
+ centerX: window.innerWidth / 2,
202
+ centerY: window.innerHeight * 0.45 + ((VISIBLE_TOP2 + VISIBLE_BOTTOM2) / 2 - IMAGE_WIDTH / 2) / IMAGE_WIDTH * size,
203
+ width: (VISIBLE_RIGHT2 - VISIBLE_LEFT2) / IMAGE_WIDTH * size,
204
+ height: (VISIBLE_BOTTOM2 - VISIBLE_TOP2) / IMAGE_WIDTH * size
205
+ };
206
+ }
207
+ function getStorage(mode) {
208
+ if (typeof window === "undefined") return null;
209
+ try {
210
+ return mode === "session" ? window.sessionStorage : window.localStorage;
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+ function shouldRun(enabled, mode, key) {
216
+ if (!enabled) return false;
217
+ if (mode === "always") return true;
218
+ try {
219
+ return getStorage(mode)?.getItem(key) == null;
220
+ } catch {
221
+ return true;
222
+ }
223
+ }
224
+ function WalrosPortalTransition({
225
+ duration = 900,
226
+ onComplete,
227
+ backgroundColor = "#000",
228
+ enabled = true,
229
+ storageMode = "always",
230
+ storageKey = "walros-portal-seen",
231
+ zIndex = 1e4,
232
+ origin,
233
+ logoUrl = walrosLogoChromeUrl
342
234
  }) {
343
- const src = variant === "dark" ? walrosWordmarkDarkUrl : walrosWordmarkLightUrl;
344
- return /* @__PURE__ */ jsx3(
345
- "img",
346
- {
347
- src,
348
- alt,
349
- draggable,
350
- ...props
351
- }
235
+ const [run] = useState3(() => shouldRun(enabled, storageMode, storageKey));
236
+ const [complete, setComplete] = useState3(false);
237
+ const [initialOrigin] = useState3(() => origin ?? defaultOrigin());
238
+ const host = useOverlayHost(zIndex);
239
+ if (!host || !enabled || !run || complete) return null;
240
+ return createPortal(
241
+ /* @__PURE__ */ jsx2(
242
+ Walros3DPortalTransition,
243
+ {
244
+ origin: initialOrigin,
245
+ logoUrl,
246
+ duration,
247
+ backgroundColor,
248
+ zIndex: 1,
249
+ lockScroll: true,
250
+ onComplete: () => {
251
+ if (storageMode !== "always") {
252
+ try {
253
+ getStorage(storageMode)?.setItem(storageKey, "1");
254
+ } catch {
255
+ }
256
+ }
257
+ setComplete(true);
258
+ onComplete?.();
259
+ }
260
+ }
261
+ ),
262
+ host
352
263
  );
353
264
  }
354
265
 
266
+ // src/components/WalrosThemeTransition.tsx
267
+ import { createPortal as createPortal2 } from "react-dom";
268
+
355
269
  // src/components/WalrosThemeGlitch.tsx
356
- import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
270
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
357
271
  var GLITCH_STYLES = `
358
272
  .walros-theme-glitch-root {
359
273
  position: fixed;
@@ -371,7 +285,6 @@ var GLITCH_STYLES = `
371
285
  content: "";
372
286
  position: absolute;
373
287
  inset: 0;
374
- mix-blend-mode: screen;
375
288
  }
376
289
  .walros-theme-glitch-root::before {
377
290
  background: rgba(0, 0, 0, .82);
@@ -501,7 +414,7 @@ function WalrosThemeGlitch({
501
414
  style
502
415
  }) {
503
416
  return /* @__PURE__ */ jsxs2(Fragment, { children: [
504
- /* @__PURE__ */ jsx4("style", { children: GLITCH_STYLES }),
417
+ /* @__PURE__ */ jsx3("style", { children: GLITCH_STYLES }),
505
418
  active ? /* @__PURE__ */ jsxs2(
506
419
  "div",
507
420
  {
@@ -509,22 +422,213 @@ function WalrosThemeGlitch({
509
422
  style: { zIndex, ...style },
510
423
  "aria-hidden": "true",
511
424
  children: [
512
- /* @__PURE__ */ jsx4("span", { className: "walros-theme-glitch-band" }),
513
- /* @__PURE__ */ jsx4("span", { className: "walros-theme-glitch-band" }),
514
- /* @__PURE__ */ jsx4("span", { className: "walros-theme-glitch-band" }),
515
- /* @__PURE__ */ jsx4("span", { className: "walros-theme-glitch-band" })
425
+ /* @__PURE__ */ jsx3("span", { className: "walros-theme-glitch-band" }),
426
+ /* @__PURE__ */ jsx3("span", { className: "walros-theme-glitch-band" }),
427
+ /* @__PURE__ */ jsx3("span", { className: "walros-theme-glitch-band" }),
428
+ /* @__PURE__ */ jsx3("span", { className: "walros-theme-glitch-band" })
516
429
  ]
517
430
  }
518
431
  ) : null
519
432
  ] });
520
433
  }
521
434
 
435
+ // src/components/WalrosThemeTransition.tsx
436
+ import { jsx as jsx4 } from "react/jsx-runtime";
437
+ function WalrosThemeTransition({
438
+ active,
439
+ zIndex = 1e4
440
+ }) {
441
+ const host = useOverlayHost(zIndex);
442
+ if (!host) return null;
443
+ return createPortal2(
444
+ /* @__PURE__ */ jsx4(WalrosThemeGlitch, { active, zIndex: 1 }),
445
+ host
446
+ );
447
+ }
448
+
449
+ // src/components/DirectionalCursor.tsx
450
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
451
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
452
+ var DESKTOP_CURSOR_QUERY = "(min-width: 1024px) and (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference)";
453
+ var CURSOR_STYLES = `
454
+ .directional-cursor { position: fixed; top: 0; left: 0; z-index: 9999; width: 20px; height: 22px; opacity: 0; pointer-events: none; contain: layout style; will-change: transform; transition: opacity 100ms linear; }
455
+ .directional-cursor.is-visible { opacity: 1; }
456
+ .directional-cursor-shape { width: 20px; height: 22px; transform-origin: center center; will-change: transform; }
457
+ .directional-cursor svg { display: block; width: 20px; height: 22px; }
458
+ @media (min-width: 1024px) and (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
459
+ html.has-directional-cursor, html.has-directional-cursor body, html.has-directional-cursor body * { cursor: none !important; }
460
+ html.has-directional-cursor :is(a, button, [role="button"], [onclick], .clickable, [data-clickable], input[type="button"], input[type="submit"], input[type="reset"], label[for]),
461
+ html.has-directional-cursor :is(a, button, [role="button"], [onclick], .clickable, [data-clickable], input[type="button"], input[type="submit"], input[type="reset"], label[for]) * { cursor: pointer !important; }
462
+ html.has-directional-cursor :is(input, textarea, select, [contenteditable="true"]) { cursor: text !important; }
463
+ }
464
+ `;
465
+ function DirectionalCursor() {
466
+ const positionLayer = useRef2(null);
467
+ const directionLayer = useRef2(null);
468
+ useEffect2(() => {
469
+ const position = positionLayer.current;
470
+ const direction = directionLayer.current;
471
+ if (!position || !direction) return;
472
+ const media = window.matchMedia(DESKTOP_CURSOR_QUERY);
473
+ let active = false;
474
+ let frame = 0;
475
+ let moveFrame = 0;
476
+ let scaleTimer = 0;
477
+ let latestEvent = null;
478
+ let currentX = window.innerWidth / 2;
479
+ let currentY = -60;
480
+ let targetX = currentX;
481
+ let targetY = currentY;
482
+ let currentAngle = 0;
483
+ let targetAngle = 0;
484
+ let hasAngle = false;
485
+ let currentScale = 1;
486
+ let targetScale = 1;
487
+ let lastX = currentX;
488
+ let lastY = 0;
489
+ const render = () => {
490
+ currentX += (targetX - currentX) * 0.4;
491
+ currentY += (targetY - currentY) * 0.4;
492
+ const angleDelta = (targetAngle - currentAngle + 540) % 360 - 180;
493
+ currentAngle += angleDelta * 0.24;
494
+ currentScale += (targetScale - currentScale) * 0.24;
495
+ position.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`;
496
+ direction.style.transform = `translate(-50%, -50%) rotate(${currentAngle}deg) scale(${currentScale})`;
497
+ const unsettled = Math.abs(targetX - currentX) > 0.08 || Math.abs(targetY - currentY) > 0.08 || Math.abs(angleDelta) > 0.08 || Math.abs(targetScale - currentScale) > 2e-3;
498
+ frame = unsettled ? window.requestAnimationFrame(render) : 0;
499
+ };
500
+ const requestRender = () => {
501
+ if (!frame) frame = window.requestAnimationFrame(render);
502
+ };
503
+ const processMove = () => {
504
+ moveFrame = 0;
505
+ const event = latestEvent;
506
+ if (!event) return;
507
+ const deltaX = event.clientX - lastX;
508
+ const deltaY = event.clientY - lastY;
509
+ const distance = Math.hypot(deltaX, deltaY);
510
+ targetX = event.clientX;
511
+ targetY = event.clientY;
512
+ position.classList.add("is-visible");
513
+ if (distance > 1.25) {
514
+ const rawAngle = Math.atan2(deltaY, deltaX) * (180 / Math.PI) + 90;
515
+ const normalizedAngle = (rawAngle % 360 + 360) % 360;
516
+ if (!hasAngle) {
517
+ targetAngle = normalizedAngle;
518
+ currentAngle = normalizedAngle;
519
+ hasAngle = true;
520
+ } else {
521
+ targetAngle = normalizedAngle;
522
+ }
523
+ targetScale = 0.92;
524
+ window.clearTimeout(scaleTimer);
525
+ scaleTimer = window.setTimeout(() => {
526
+ targetScale = 1;
527
+ requestRender();
528
+ }, 130);
529
+ }
530
+ lastX = event.clientX;
531
+ lastY = event.clientY;
532
+ requestRender();
533
+ };
534
+ const handleMouseMove = (event) => {
535
+ latestEvent = event;
536
+ if (!moveFrame) moveFrame = window.requestAnimationFrame(processMove);
537
+ };
538
+ const activate = () => {
539
+ if (active) return;
540
+ active = true;
541
+ document.documentElement.classList.add("has-directional-cursor");
542
+ window.addEventListener("mousemove", handleMouseMove, { passive: true });
543
+ };
544
+ const deactivate = () => {
545
+ if (!active) return;
546
+ active = false;
547
+ document.documentElement.classList.remove("has-directional-cursor");
548
+ position.classList.remove("is-visible");
549
+ window.removeEventListener("mousemove", handleMouseMove);
550
+ window.cancelAnimationFrame(frame);
551
+ window.cancelAnimationFrame(moveFrame);
552
+ window.clearTimeout(scaleTimer);
553
+ frame = 0;
554
+ moveFrame = 0;
555
+ };
556
+ const sync = () => {
557
+ if (media.matches) activate();
558
+ else deactivate();
559
+ };
560
+ media.addEventListener("change", sync);
561
+ sync();
562
+ return () => {
563
+ media.removeEventListener("change", sync);
564
+ deactivate();
565
+ };
566
+ }, []);
567
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
568
+ /* @__PURE__ */ jsx5("style", { children: CURSOR_STYLES }),
569
+ /* @__PURE__ */ jsx5("div", { ref: positionLayer, className: "directional-cursor", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("div", { ref: directionLayer, className: "directional-cursor-shape", children: /* @__PURE__ */ jsxs3("svg", { width: "50", height: "54", viewBox: "0 0 50 54", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
570
+ /* @__PURE__ */ jsxs3("g", { filter: "url(#walros-cursor-shadow)", children: [
571
+ /* @__PURE__ */ jsx5("path", { d: "M42.6817 41.1495L27.5103 6.79925C26.7269 5.02557 24.2082 5.02558 23.3927 6.79925L7.59814 41.1495C6.75833 42.9759 8.52712 44.8902 10.4125 44.1954L24.3757 39.0496C24.8829 38.8627 25.4385 38.8627 25.9422 39.0496L39.8121 44.1954C41.6849 44.8902 43.4884 42.9759 42.6817 41.1495Z", fill: "black" }),
572
+ /* @__PURE__ */ jsx5("path", { d: "M43.7146 40.6933L28.5431 6.34306C27.3556 3.65428 23.5772 3.69516 22.3668 6.32755L6.57226 40.6778C5.3134 43.4156 7.97238 46.298 10.803 45.2549L24.7662 40.109C25.0221 40.0147 25.2999 40.0156 25.5494 40.1082L39.4193 45.254C42.2261 46.2953 44.9254 43.4347 43.7146 40.6933Z", stroke: "white", strokeWidth: "2.25825" })
573
+ ] }),
574
+ /* @__PURE__ */ jsx5("defs", { children: /* @__PURE__ */ jsxs3("filter", { id: "walros-cursor-shadow", x: "0.602397", y: "0.952444", width: "49.0584", height: "52.428", filterUnits: "userSpaceOnUse", colorInterpolationFilters: "sRGB", children: [
575
+ /* @__PURE__ */ jsx5("feFlood", { floodOpacity: "0", result: "BackgroundImageFix" }),
576
+ /* @__PURE__ */ jsx5("feColorMatrix", { in: "SourceAlpha", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0", result: "hardAlpha" }),
577
+ /* @__PURE__ */ jsx5("feOffset", { dy: "2.25825" }),
578
+ /* @__PURE__ */ jsx5("feGaussianBlur", { stdDeviation: "2.25825" }),
579
+ /* @__PURE__ */ jsx5("feComposite", { in2: "hardAlpha", operator: "out" }),
580
+ /* @__PURE__ */ jsx5("feColorMatrix", { type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.08 0" }),
581
+ /* @__PURE__ */ jsx5("feBlend", { mode: "normal", in2: "BackgroundImageFix", result: "effect1_dropShadow" }),
582
+ /* @__PURE__ */ jsx5("feBlend", { mode: "normal", in: "SourceGraphic", in2: "effect1_dropShadow", result: "shape" })
583
+ ] }) })
584
+ ] }) }) })
585
+ ] });
586
+ }
587
+
588
+ // src/components/WalrosLogo.tsx
589
+ import { jsx as jsx6 } from "react/jsx-runtime";
590
+ function WalrosLogo({
591
+ alt = "WalrOS",
592
+ draggable = false,
593
+ ...props
594
+ }) {
595
+ return /* @__PURE__ */ jsx6(
596
+ "img",
597
+ {
598
+ src: walrosLogoChromeUrl,
599
+ alt,
600
+ draggable,
601
+ ...props
602
+ }
603
+ );
604
+ }
605
+
606
+ // src/components/WalrosWordmark.tsx
607
+ import { jsx as jsx7 } from "react/jsx-runtime";
608
+ function WalrosWordmark({
609
+ variant = "light",
610
+ alt = "WalrOS",
611
+ draggable = false,
612
+ ...props
613
+ }) {
614
+ const src = variant === "dark" ? walrosWordmarkDarkUrl : walrosWordmarkLightUrl;
615
+ return /* @__PURE__ */ jsx7(
616
+ "img",
617
+ {
618
+ src,
619
+ alt,
620
+ draggable,
621
+ ...props
622
+ }
623
+ );
624
+ }
625
+
522
626
  // src/hooks/useWalrosTheme.ts
523
627
  import {
524
628
  useCallback,
525
- useEffect as useEffect2,
526
- useRef as useRef2,
527
- useState as useState2
629
+ useEffect as useEffect3,
630
+ useRef as useRef3,
631
+ useState as useState4
528
632
  } from "react";
529
633
  var GLITCH_BASE_CLASS = "walros-theme-glitch-page";
530
634
  var GLITCH_BODY_CLASS = "walros-theme-glitch-page--body";
@@ -571,25 +675,25 @@ function useWalrosTheme({
571
675
  lockScroll,
572
676
  onAfterUnlock
573
677
  } = {}) {
574
- const [theme, setThemeState] = useState2(
678
+ const [theme, setThemeState] = useState4(
575
679
  () => readInitialTheme(storageKey, defaultTheme, useSystemPreference)
576
680
  );
577
- const [mounted, setMounted] = useState2(false);
578
- const [glitching, setGlitching] = useState2(false);
579
- const themeRef = useRef2(theme);
580
- const glitchingRef = useRef2(false);
581
- const swapTimerRef = useRef2(null);
582
- const cleanupTimerRef = useRef2(null);
583
- const applyThemeRef = useRef2(applyTheme);
584
- const modeRef = useRef2(mode);
585
- const shakeTargetsRef = useRef2(shakeTargets);
586
- const lockScrollRef = useRef2(lockScroll);
587
- const onAfterUnlockRef = useRef2(onAfterUnlock);
588
- const activeTargetsRef = useRef2([]);
589
- const viewportLockRef = useRef2(null);
590
- const soundRef = useRef2(null);
591
- const soundUrlRef = useRef2(soundUrl);
592
- const soundVolumeRef = useRef2(soundVolume);
681
+ const [mounted, setMounted] = useState4(false);
682
+ const [glitching, setGlitching] = useState4(false);
683
+ const themeRef = useRef3(theme);
684
+ const glitchingRef = useRef3(false);
685
+ const swapTimerRef = useRef3(null);
686
+ const cleanupTimerRef = useRef3(null);
687
+ const applyThemeRef = useRef3(applyTheme);
688
+ const modeRef = useRef3(mode);
689
+ const shakeTargetsRef = useRef3(shakeTargets);
690
+ const lockScrollRef = useRef3(lockScroll);
691
+ const onAfterUnlockRef = useRef3(onAfterUnlock);
692
+ const activeTargetsRef = useRef3([]);
693
+ const viewportLockRef = useRef3(null);
694
+ const soundRef = useRef3(null);
695
+ const soundUrlRef = useRef3(soundUrl);
696
+ const soundVolumeRef = useRef3(soundVolume);
593
697
  themeRef.current = theme;
594
698
  applyThemeRef.current = applyTheme;
595
699
  modeRef.current = mode;
@@ -626,7 +730,7 @@ function useWalrosTheme({
626
730
  },
627
731
  [storageKey]
628
732
  );
629
- useEffect2(() => {
733
+ useEffect3(() => {
630
734
  const initialTheme = readInitialTheme(
631
735
  storageKey,
632
736
  defaultTheme,
@@ -708,6 +812,10 @@ function useWalrosTheme({
708
812
  activeTargetsRef.current = [];
709
813
  }, []);
710
814
  const finishGlitch = useCallback(() => {
815
+ if (soundRef.current) {
816
+ soundRef.current.pause();
817
+ soundRef.current.currentTime = 0;
818
+ }
711
819
  removeShakeClasses();
712
820
  unlockViewport(true);
713
821
  glitchingRef.current = false;
@@ -757,12 +865,13 @@ function useWalrosTheme({
757
865
  finishGlitch();
758
866
  }, effectiveDuration);
759
867
  }, [finishGlitch, lockViewport, persistTheme, playThemeSound, swapDelay, totalDuration]);
760
- useEffect2(() => {
868
+ useEffect3(() => {
761
869
  return () => {
762
870
  clearTimers();
763
871
  removeShakeClasses();
764
872
  unlockViewport(false);
765
873
  soundRef.current?.pause();
874
+ if (soundRef.current) soundRef.current.currentTime = 0;
766
875
  soundRef.current = null;
767
876
  };
768
877
  }, [clearTimers, removeShakeClasses, unlockViewport]);
@@ -776,9 +885,12 @@ function useWalrosTheme({
776
885
  };
777
886
  }
778
887
  export {
888
+ DirectionalCursor,
889
+ Walros3DPortalTransition,
779
890
  WalrosLogo,
780
891
  WalrosPortalTransition,
781
892
  WalrosThemeGlitch,
893
+ WalrosThemeTransition,
782
894
  WalrosWordmark,
783
895
  useWalrosTheme,
784
896
  walrosLogoChromeUrl,