Compare commits

..

27 commits
v2.1.0 ... main

Author SHA1 Message Date
MAZE
aa2b47ace4 chore(release): 2.4.0 2025-11-25 20:09:52 +03:30
MAZE
3a96d38a77 feat: add audio session type 2025-11-25 19:45:54 +03:30
MAZE
7e8f23f5fa chore(release): 2.3.0 2025-11-25 00:27:03 +03:30
MAZE
d0160763ee feat: turn links into buttons 2025-11-25 00:26:47 +03:30
MAZE
b921629ee3 chore: change silence 2025-11-25 00:17:52 +03:30
MAZE
ee139150f5 chore(release): 2.2.0 2025-11-25 00:10:46 +03:30
MAZE
04c52962c3 style: minor changes 2025-11-25 00:10:07 +03:30
maze
97ca030534
Merge pull request #84 from amir-rahmanii/feat/add-category-icons
feat: add category icons
2025-11-25 00:06:52 +03:30
MAZE
e160d26677 feat: replace the silence file 2025-11-24 23:56:07 +03:30
Amirreza
642a551226 feat: add category icons 2025-10-30 13:03:58 +03:30
MAZE
6ac65c1948 style: change cursor 2025-10-26 12:48:55 +03:30
MAZE
50687c97ca style: add animation on active 2025-10-26 12:43:14 +03:30
MAZE
95b641a88f feat: extract the provider for the tooltip 2025-10-25 13:11:04 +03:30
MAZE
d11a6ab062 style: increase text color 2025-08-13 12:12:27 +03:30
MAZE
a071ba04c7 style: decrease background opacity 2025-08-13 12:01:38 +03:30
MAZE
a179c09d0c style: increase line height 2025-08-13 11:59:45 +03:30
MAZE
066af9e2f3 feat: change lofi icon 2025-08-13 11:57:08 +03:30
MAZE
1e5bda707c style: change snackbar styles 2025-08-06 13:23:41 +03:30
MAZE
e2bb4dd55f style: increase border radius 2025-08-06 13:21:44 +03:30
MAZE
d9df0d4b2c feat: add shine effect 2025-08-06 13:13:14 +03:30
MAZE
3feb9c1a09 style: remove cipher animation 2025-08-06 13:08:57 +03:30
MAZE
b191e6067d feat: migrate to motion and fix some animations 2025-08-06 13:07:03 +03:30
MAZE
81d9d7ca03 feat: make sound file addresses relative 2025-07-19 22:07:06 +03:30
MAZE ✧
1e24cbc6eb
Merge pull request #69 from ncguk/patch-2
Update animals.tsx
2025-07-19 21:48:24 +03:30
MAZE ✧
78fb8cd76f
Merge pull request #68 from ncguk/patch-1
Rename horse-galopp.mp3 to horse-gallop.mp3
2025-07-19 21:47:50 +03:30
ncguk
d6484103a7
Update animals.tsx
Change instances of "galopp" to "gallop"
2025-06-18 10:51:10 +01:00
ncguk
374de8b0d2
Rename horse-galopp.mp3 to horse-gallop.mp3
Fixes typo in filename.
2025-06-18 10:43:55 +01:00
44 changed files with 968 additions and 277 deletions

View file

@ -2,6 +2,51 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [2.4.0](https://github.com/remvze/moodist/compare/v2.3.0...v2.4.0) (2025-11-25)
### ✨ Features
* add audio session type ([3a96d38](https://github.com/remvze/moodist/commit/3a96d38a774c7675811d5a3ea323a49d9d129bbc))
## [2.3.0](https://github.com/remvze/moodist/compare/v2.2.0...v2.3.0) (2025-11-24)
### 🚚 Chores
* change silence ([b921629](https://github.com/remvze/moodist/commit/b921629ee33c4a18a86258ba204921f732f404ff))
### ✨ Features
* turn links into buttons ([d016076](https://github.com/remvze/moodist/commit/d0160763eeb66ba47dd06098b1f2a84e234fca36))
## [2.2.0](https://github.com/remvze/moodist/compare/v2.1.0...v2.2.0) (2025-11-24)
### ✨ Features
* add category icons ([642a551](https://github.com/remvze/moodist/commit/642a5512267ce66492cf86f222fa01714960162a))
* add shine effect ([d9df0d4](https://github.com/remvze/moodist/commit/d9df0d4b2c5071c12cecc6452acc0f160c57deb5))
* change lofi icon ([066af9e](https://github.com/remvze/moodist/commit/066af9e2f31bc9201d349d888c6dc19cd5ad7750))
* extract the provider for the tooltip ([95b641a](https://github.com/remvze/moodist/commit/95b641a88f2eee264b59b5bd62206bb84119da57))
* make sound file addresses relative ([81d9d7c](https://github.com/remvze/moodist/commit/81d9d7ca03f6c7410ca750e069c9c8b935114950))
* migrate to motion and fix some animations ([b191e60](https://github.com/remvze/moodist/commit/b191e6067ddc3233689a34946c602db36d6133ba))
* replace the silence file ([e160d26](https://github.com/remvze/moodist/commit/e160d2667737b47c18b08887735be26f21bf52ae))
### 💄 Styling
* add animation on active ([50687c9](https://github.com/remvze/moodist/commit/50687c97ca483f4de3ee7633d333dfcb4def0c4d))
* change cursor ([6ac65c1](https://github.com/remvze/moodist/commit/6ac65c1948ad93fed012a8203fc8c6c2b2898b5b))
* change snackbar styles ([1e5bda7](https://github.com/remvze/moodist/commit/1e5bda707cc202407b179e2d1b95dec34bfe9420))
* decrease background opacity ([a071ba0](https://github.com/remvze/moodist/commit/a071ba04c7e86b3056049492386516b58c4210c0))
* increase border radius ([e2bb4dd](https://github.com/remvze/moodist/commit/e2bb4dd55fbf17e777ddbb6825e400bd023da328))
* increase line height ([a179c09](https://github.com/remvze/moodist/commit/a179c09d0c637d33d310960dbf3e92af4b5c526b))
* increase text color ([d11a6ab](https://github.com/remvze/moodist/commit/d11a6ab062061da5809ebddd6eb39b17c2cd3862))
* minor changes ([04c5296](https://github.com/remvze/moodist/commit/04c52962c3b65ebb7875ebadf20132846a5c020b))
* remove cipher animation ([3feb9c1](https://github.com/remvze/moodist/commit/3feb9c1a09b52a35d79cebb7ece54989e9faf481))
## [2.1.0](https://github.com/remvze/moodist/compare/v2.0.1...v2.1.0) (2025-07-19) ## [2.1.0](https://github.com/remvze/moodist/compare/v2.0.1...v2.1.0) (2025-07-19)

548
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "moodist", "name": "moodist",
"version": "2.1.0", "version": "2.4.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "moodist", "name": "moodist",
"version": "2.1.0", "version": "2.4.0",
"dependencies": { "dependencies": {
"@astrojs/react": "3.6.0", "@astrojs/react": "3.6.0",
"@floating-ui/react": "0.26.0", "@floating-ui/react": "0.26.0",
@ -14,7 +14,7 @@
"@radix-ui/react-checkbox": "1.1.4", "@radix-ui/react-checkbox": "1.1.4",
"@radix-ui/react-dropdown-menu": "2.0.6", "@radix-ui/react-dropdown-menu": "2.0.6",
"@radix-ui/react-slider": "1.2.3", "@radix-ui/react-slider": "1.2.3",
"@radix-ui/react-tooltip": "1.0.7", "@radix-ui/react-tooltip": "1.2.8",
"@types/howler": "2.2.10", "@types/howler": "2.2.10",
"@types/react": "^18.2.25", "@types/react": "^18.2.25",
"@types/react-dom": "^18.2.10", "@types/react-dom": "^18.2.10",
@ -25,6 +25,7 @@
"framer-motion": "10.16.4", "framer-motion": "10.16.4",
"howler": "2.2.4", "howler": "2.2.4",
"js-confetti": "0.12.0", "js-confetti": "0.12.0",
"motion": "12.23.24",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hotkeys-hook": "3.2.1", "react-hotkeys-hook": "3.2.1",
@ -5123,29 +5124,29 @@
} }
}, },
"node_modules/@radix-ui/react-tooltip": { "node_modules/@radix-ui/react-tooltip": {
"version": "1.0.7", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
"integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.1.3",
"@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.1.2",
"@radix-ui/react-context": "1.0.1", "@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-dismissable-layer": "1.0.5", "@radix-ui/react-id": "1.1.1",
"@radix-ui/react-id": "1.0.1", "@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-popper": "1.1.3", "@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-portal": "1.0.4", "@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-slot": "1.0.2", "@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-controllable-state": "1.0.1", "@radix-ui/react-visually-hidden": "1.2.3"
"@radix-ui/react-visually-hidden": "1.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "*", "@types/react": "*",
"@types/react-dom": "*", "@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0" "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@types/react": { "@types/react": {
@ -5156,6 +5157,340 @@
} }
} }
}, },
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"license": "MIT"
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-escape-keydown": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-id": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
"integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.0",
"@radix-ui/react-arrow": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"@radix-ui/react-use-layout-effect": "1.1.1",
"@radix-ui/react-use-rect": "1.1.1",
"@radix-ui/react-use-size": "1.1.1",
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-controllable-state": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-effect-event": "0.0.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-escape-keydown": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
"license": "MIT",
"dependencies": {
"@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-size": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/rect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
"node_modules/@radix-ui/react-use-callback-ref": { "node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
@ -5191,6 +5526,39 @@
} }
} }
}, },
"node_modules/@radix-ui/react-use-effect-event": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-escape-keydown": { "node_modules/@radix-ui/react-use-escape-keydown": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
@ -5278,18 +5646,18 @@
} }
}, },
"node_modules/@radix-ui/react-visually-hidden": { "node_modules/@radix-ui/react-visually-hidden": {
"version": "1.0.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
"integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "2.1.3"
"@radix-ui/react-primitive": "1.0.3"
}, },
"peerDependencies": { "peerDependencies": {
"@types/react": "*", "@types/react": "*",
"@types/react-dom": "*", "@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0" "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@types/react": { "@types/react": {
@ -5300,6 +5668,62 @@
} }
} }
}, },
"node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-slot": "1.2.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2"
},
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@radix-ui/rect": { "node_modules/@radix-ui/rect": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
@ -20732,6 +21156,74 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/motion": {
"version": "12.23.24",
"resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz",
"integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==",
"license": "MIT",
"dependencies": {
"framer-motion": "^12.23.24",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/motion-dom": {
"version": "12.23.23",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.23.6"
}
},
"node_modules/motion-utils": {
"version": "12.23.6",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
"license": "MIT"
},
"node_modules/motion/node_modules/framer-motion": {
"version": "12.23.24",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
"integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.23.23",
"motion-utils": "^12.23.6",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",

View file

@ -1,7 +1,7 @@
{ {
"name": "moodist", "name": "moodist",
"type": "module", "type": "module",
"version": "2.1.0", "version": "2.4.0",
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"start": "astro dev", "start": "astro dev",
@ -30,7 +30,7 @@
"@radix-ui/react-checkbox": "1.1.4", "@radix-ui/react-checkbox": "1.1.4",
"@radix-ui/react-dropdown-menu": "2.0.6", "@radix-ui/react-dropdown-menu": "2.0.6",
"@radix-ui/react-slider": "1.2.3", "@radix-ui/react-slider": "1.2.3",
"@radix-ui/react-tooltip": "1.0.7", "@radix-ui/react-tooltip": "1.2.8",
"@types/howler": "2.2.10", "@types/howler": "2.2.10",
"@types/react": "^18.2.25", "@types/react": "^18.2.25",
"@types/react-dom": "^18.2.10", "@types/react-dom": "^18.2.10",
@ -41,6 +41,7 @@
"framer-motion": "10.16.4", "framer-motion": "10.16.4",
"howler": "2.2.4", "howler": "2.2.4",
"js-confetti": "0.12.0", "js-confetti": "0.12.0",
"motion": "12.23.24",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hotkeys-hook": "3.2.1", "react-hotkeys-hook": "3.2.1",

Binary file not shown.

BIN
public/sounds/silence.wav Normal file

Binary file not shown.

View file

@ -18,6 +18,10 @@
background-color: var(--color-neutral-800); background-color: var(--color-neutral-800);
} }
&:not(.disabled):active {
transform: scale(0.97);
}
&:disabled, &:disabled,
&.disabled { &.disabled {
cursor: not-allowed; cursor: not-allowed;

View file

@ -19,6 +19,10 @@
cursor: not-allowed; cursor: not-allowed;
} }
&:active {
transform: scale(0.97);
}
&:hover, &:hover,
&:focus-visible { &:focus-visible {
background-color: var(--color-neutral-200); background-color: var(--color-neutral-200);

View file

@ -1,6 +1,6 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { BiUndo, BiTrash } from 'react-icons/bi/index'; import { BiUndo, BiTrash } from 'react-icons/bi/index';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'motion/react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { Tooltip } from '@/components/tooltip'; import { Tooltip } from '@/components/tooltip';
@ -41,30 +41,31 @@ export function UnselectButton() {
initial="hidden" initial="hidden"
variants={variants} variants={variants}
> >
<Tooltip <Tooltip.Provider delayDuration={0}>
showDelay={0} <Tooltip
content={ content={
hasHistory
? 'Restore unselected sounds.'
: 'Unselect all sounds.'
}
>
<button
disabled={noSelected && !hasHistory}
aria-label={
hasHistory hasHistory
? 'Restore Unselected Sounds' ? 'Restore unselected sounds.'
: 'Unselect All Sounds' : 'Unselect all sounds.'
} }
className={cn(
styles.unselectButton,
noSelected && !hasHistory && styles.disabled,
)}
onClick={handleToggle}
> >
{hasHistory ? <BiUndo /> : <BiTrash />} <button
</button> disabled={noSelected && !hasHistory}
</Tooltip> aria-label={
hasHistory
? 'Restore Unselected Sounds'
: 'Unselect All Sounds'
}
className={cn(
styles.unselectButton,
noSelected && !hasHistory && styles.disabled,
)}
onClick={handleToggle}
>
{hasHistory ? <BiUndo /> : <BiTrash />}
</button>
</Tooltip>
</Tooltip.Provider>
</motion.div> </motion.div>
)} )}
</AnimatePresence> </AnimatePresence>

View file

@ -1,4 +1,4 @@
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence } from 'motion/react';
import { Category } from './category'; import { Category } from './category';
import { Donate } from './donate'; import { Donate } from './donate';

View file

@ -0,0 +1,47 @@
.wrapper {
display: flex;
flex-direction: column;
gap: 20px;
padding-bottom: 80px;
& .title {
font-family: var(--font-display);
font-size: var(--font-lg);
font-weight: 600;
text-align: center;
}
& .categoryIconsWrapper {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: center;
justify-content: center;
& .icon {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
font-size: var(--font-md);
color: var(--color-foreground);
cursor: pointer;
background: linear-gradient(
var(--color-neutral-50),
var(--color-neutral-100)
);
border: 1px solid var(--color-neutral-300);
border-radius: 50%;
transition: 0.2s;
&:hover {
background: linear-gradient(
var(--color-neutral-100),
var(--color-neutral-200)
);
transform: scale(1.15);
}
}
}
}

View file

@ -0,0 +1,42 @@
import { sounds } from '@/data/sounds';
import { useMemo } from 'react';
import styles from './category-icons.module.css';
import { Container } from '@/components/container';
import { Tooltip } from '@/components/tooltip';
export default function CategoryIcons() {
const categories = useMemo(() => sounds.categories, []);
const goto = (id: string) => {
const category = document.getElementById(`category-${id}`);
category?.scrollIntoView();
};
return (
<Container>
<div className={styles.wrapper}>
<h3 className={styles.title}>Categories</h3>
<div className={styles.categoryIconsWrapper}>
<Tooltip.Provider delayDuration={0}>
{categories.map(category => {
return (
<Tooltip
content={category.title}
key={category.id}
placement="bottom"
>
<button
className={styles.icon}
onClick={() => goto(category.id)}
>
{category.icon}
</button>
</Tooltip>
);
})}
</Tooltip.Provider>
</div>
</div>
</Container>
);
}

View file

@ -38,7 +38,7 @@ import { Container } from './container';
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
transparent, transparent,
var(--color-neutral-200), var(--color-neutral-400),
transparent transparent
); );
transform: translateX(-50%); transform: translateX(-50%);

View file

@ -2,7 +2,6 @@
import { BsSoundwave } from 'react-icons/bs/index'; import { BsSoundwave } from 'react-icons/bs/index';
import { Container } from './container'; import { Container } from './container';
import { CipherText } from './cipher';
import { count as soundCount } from '@/lib/sounds'; import { count as soundCount } from '@/lib/sounds';
@ -27,9 +26,7 @@ const count = soundCount();
<h1 class="title"> <h1 class="title">
Ambient Sounds<span class="line">For Focus and Calm</span> Ambient Sounds<span class="line">For Focus and Calm</span>
</h1> </h1>
<h2 class="desc"> <h2 class="desc">Free and Open-Source.</h2>
Free and <CipherText client:load text="Open-Source" />.
</h2>
<p class="sounds"> <p class="sounds">
<span aria-hidden="true" class="icon"> <span aria-hidden="true" class="icon">
@ -64,6 +61,20 @@ const count = soundCount();
background-size: 21px 21px; background-size: 21px 21px;
opacity: 0.8; opacity: 0.8;
mask-image: linear-gradient(#fff, transparent, transparent); mask-image: linear-gradient(#fff, transparent, transparent);
&::before {
position: absolute;
top: 0;
left: 50%;
width: 300px;
height: 100px;
content: '';
background: var(--color-neutral-200);
filter: blur(50px);
border-radius: 100%;
opacity: 0.8;
transform: translate(-50%, -50%);
}
} }
} }
@ -86,17 +97,7 @@ const count = soundCount();
font-family: var(--font-display); font-family: var(--font-display);
font-size: var(--font-xlg); font-size: var(--font-xlg);
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 1.1;
/* & .gradient {
background: linear-gradient(
135deg,
var(--color-foreground),
var(--color-foreground-subtle)
);
background-clip: text;
-webkit-text-fill-color: transparent;
} */
& .line { & .line {
display: block; display: block;

View file

@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef } from 'react';
import { BrowserDetect } from '@/helpers/browser-detect'; import { BrowserDetect } from '@/helpers/browser-detect';
@ -15,23 +15,14 @@ const metadata: MediaMetadataInit = {
export function MediaSessionTrack() { export function MediaSessionTrack() {
const { isBrowser } = useSSR(); const { isBrowser } = useSSR();
const isDarkTheme = useDarkTheme(); const isDarkTheme = useDarkTheme();
const [isGenerated, setIsGenerated] = useState(false);
const isPlaying = useSoundStore(state => state.isPlaying); const isPlaying = useSoundStore(state => state.isPlaying);
const play = useSoundStore(state => state.play); const play = useSoundStore(state => state.play);
const pause = useSoundStore(state => state.pause); const pause = useSoundStore(state => state.pause);
const masterAudioSoundRef = useRef<HTMLAudioElement>(null); const masterAudioSoundRef = useRef<HTMLAudioElement>(null);
const artworkURL = isDarkTheme ? '/logo-dark.png' : '/logo-light.png'; const artworkURL = isDarkTheme ? '/logo-dark.png' : '/logo-light.png';
const generateSilence = useCallback(async () => {
if (!masterAudioSoundRef.current) return;
masterAudioSoundRef.current.src = '/sounds/silence.mp3';
setIsGenerated(true);
}, []);
useEffect(() => { useEffect(() => {
if (!isBrowser || !isPlaying || !isGenerated) return; if (!isBrowser || !isPlaying) return;
navigator.mediaSession.metadata = new MediaMetadata({ navigator.mediaSession.metadata = new MediaMetadata({
...metadata, ...metadata,
@ -43,11 +34,7 @@ export function MediaSessionTrack() {
}, },
], ],
}); });
}, [artworkURL, isBrowser, isDarkTheme, isGenerated, isPlaying]); }, [artworkURL, isBrowser, isDarkTheme, isPlaying]);
useEffect(() => {
generateSilence();
}, [generateSilence]);
const startMasterAudio = useCallback(async () => { const startMasterAudio = useCallback(async () => {
if (!masterAudioSoundRef.current) return; if (!masterAudioSoundRef.current) return;
@ -79,7 +66,6 @@ export function MediaSessionTrack() {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!isGenerated) return;
if (!masterAudioSoundRef.current) return; if (!masterAudioSoundRef.current) return;
if (isPlaying) { if (isPlaying) {
@ -87,7 +73,7 @@ export function MediaSessionTrack() {
} else { } else {
stopMasterAudio(); stopMasterAudio();
} }
}, [isGenerated, isPlaying, startMasterAudio, stopMasterAudio]); }, [isPlaying, startMasterAudio, stopMasterAudio]);
useEffect(() => { useEffect(() => {
const masterAudioSound = masterAudioSoundRef.current; const masterAudioSound = masterAudioSoundRef.current;
@ -101,5 +87,12 @@ export function MediaSessionTrack() {
}; };
}, []); }, []);
return <audio id="media-session-track" loop ref={masterAudioSoundRef} />; return (
<audio
id="media-session-track"
loop
ref={masterAudioSoundRef}
src="/sounds/silence.wav"
/>
);
} }

View file

@ -1,5 +1,5 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'motion/react';
import { IoClose } from 'react-icons/io5/index'; import { IoClose } from 'react-icons/io5/index';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';

View file

@ -1,5 +1,5 @@
import { useState, useEffect, useMemo, useCallback } from 'react'; import { useState, useEffect, useMemo, useCallback } from 'react';
import { motion } from 'framer-motion'; import { motion } from 'motion/react';
import { padNumber } from '@/helpers/number'; import { padNumber } from '@/helpers/number';

View file

@ -9,14 +9,16 @@ export function Shuffle() {
const shuffle = useSoundStore(state => state.shuffle); const shuffle = useSoundStore(state => state.shuffle);
return ( return (
<Tooltip content="Shuffle sounds" showDelay={0}> <Tooltip.Provider delayDuration={0}>
<button <Tooltip content="Shuffle sounds">
aria-label="Shuffle sounds" <button
className={styles.button} aria-label="Shuffle sounds"
onClick={shuffle} className={styles.button}
> onClick={shuffle}
<BiShuffle /> >
</button> <BiShuffle />
</Tooltip> </button>
</Tooltip>
</Tooltip.Provider>
); );
} }

View file

@ -13,8 +13,8 @@
margin: 0 auto; margin: 0 auto;
font-size: var(--font-sm); font-size: var(--font-sm);
pointer-events: fill; pointer-events: fill;
background-color: var(--color-neutral-200); background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-300); border: 1px solid var(--color-neutral-300);
border-radius: 4px; border-radius: 8px;
} }
} }

View file

@ -1,4 +1,4 @@
import { motion } from 'framer-motion'; import { motion } from 'motion/react';
import { mix, fade, slideY } from '@/lib/motion'; import { mix, fade, slideY } from '@/lib/motion';

View file

@ -1,5 +1,5 @@
import { BiHeart, BiSolidHeart } from 'react-icons/bi/index'; import { BiHeart, BiSolidHeart } from 'react-icons/bi/index';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'motion/react';
import { useSoundStore } from '@/stores/sound'; import { useSoundStore } from '@/stores/sound';
import { cn } from '@/helpers/styles'; import { cn } from '@/helpers/styles';

View file

@ -6,9 +6,10 @@
justify-content: center; justify-content: center;
padding: 25px 20px; padding: 25px 20px;
text-align: center; text-align: center;
background: linear-gradient(var(--color-neutral-100), transparent); cursor: pointer;
background: linear-gradient(rgb(24 24 27 / 50%), transparent);
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);
border-radius: 8px; border-radius: 12px;
transition: 0.2s; transition: 0.2s;
&:focus-visible { &:focus-visible {
@ -107,7 +108,6 @@
font-size: var(--font-sm); font-size: var(--font-sm);
font-weight: 600; font-weight: 600;
line-height: 1.6; line-height: 1.6;
cursor: default;
} }
} }

View file

@ -1,5 +1,5 @@
import { useState, useMemo, useCallback, useRef, useEffect } from 'react'; import { useState, useMemo, useCallback, useRef, useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'motion/react';
import { Sound } from './sound'; import { Sound } from './sound';
import { useLocalStorage } from '@/hooks/use-local-storage'; import { useLocalStorage } from '@/hooks/use-local-storage';

View file

@ -1,4 +1,4 @@
import { FaHeadphonesAlt } from 'react-icons/fa/index'; import { IoIosMusicalNote } from 'react-icons/io/index';
import { Item } from '../item'; import { Item } from '../item';
@ -7,5 +7,11 @@ interface LofiProps {
} }
export function Lofi({ open }: LofiProps) { export function Lofi({ open }: LofiProps) {
return <Item icon={<FaHeadphonesAlt />} label="Lofi Music" onClick={open} />; return (
<Item
icon={<IoIosMusicalNote />}
label="Lofi Music Player"
onClick={open}
/>
);
} }

View file

@ -2,7 +2,7 @@ import { useState, useMemo, useCallback } from 'react';
import { IoMenu, IoClose } from 'react-icons/io5/index'; import { IoMenu, IoClose } from 'react-icons/io5/index';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'motion/react';
import { import {
ShuffleItem, ShuffleItem,

View file

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { BiUpArrowAlt } from 'react-icons/bi/index'; import { BiUpArrowAlt } from 'react-icons/bi/index';
import { motion, AnimatePresence } from 'framer-motion'; import { motion } from 'motion/react';
import { mix, fade, slideY } from '@/lib/motion'; import { mix, fade, slideY } from '@/lib/motion';
@ -30,22 +30,20 @@ export function ScrollToTop() {
const variants = mix(fade(), slideY(10, 0)); const variants = mix(fade(), slideY(10, 0));
return ( return (
<AnimatePresence> <motion.button
{isVisible ? ( animate={isVisible ? 'show' : 'hidden'}
<motion.button aria-label="Scroll to top"
animate="show" className={styles.button}
aria-label="Scroll to top" exit="hidden"
className={styles.button} initial="hidden"
exit="hidden" variants={variants}
initial="hidden" style={{
variants={variants} pointerEvents: isVisible ? 'auto' : 'none',
onClick={scrollToTop} visibility: isVisible ? 'visible' : 'hidden',
> }}
<BiUpArrowAlt /> onClick={scrollToTop}
</motion.button> >
) : ( <BiUpArrowAlt />
<div /> </motion.button>
)}
</AnimatePresence>
); );
} }

View file

@ -20,14 +20,16 @@ export function Button({
tooltip, tooltip,
}: ButtonProps) { }: ButtonProps) {
return ( return (
<Tooltip content={tooltip} placement="bottom" showDelay={0}> <Tooltip.Provider delayDuration={0}>
<button <Tooltip content={tooltip} placement="bottom">
className={cn(styles.button, smallIcon && styles.smallIcon)} <button
disabled={disabled} className={cn(styles.button, smallIcon && styles.smallIcon)}
onClick={onClick} disabled={disabled}
> onClick={onClick}
{icon} >
</button> {icon}
</Tooltip> </button>
</Tooltip>
</Tooltip.Provider>
); );
} }

View file

@ -20,7 +20,7 @@ export function Button({
tooltip, tooltip,
}: ButtonProps) { }: ButtonProps) {
return ( return (
<Tooltip content={tooltip} placement="bottom" showDelay={0}> <Tooltip content={tooltip} placement="bottom">
<button <button
className={cn( className={cn(
styles.button, styles.button,

View file

@ -22,7 +22,7 @@
height: 350px; height: 350px;
padding: 12px; padding: 12px;
line-height: 1.6; line-height: 1.6;
color: var(--color-foreground-subtle); color: var(--color-foreground);
resize: none; resize: none;
background-color: var(--color-neutral-50); background-color: var(--color-neutral-50);
border: 1px solid var(--color-neutral-200); border: 1px solid var(--color-neutral-200);

View file

@ -12,6 +12,7 @@ import { useCopy } from '@/hooks/use-copy';
import { download } from '@/helpers/download'; import { download } from '@/helpers/download';
import styles from './notepad.module.css'; import styles from './notepad.module.css';
import { Tooltip } from '@/components/tooltip';
interface NotepadProps { interface NotepadProps {
onClose: () => void; onClose: () => void;
@ -50,23 +51,25 @@ export function Notepad({ onClose, show }: NotepadProps) {
<header className={styles.header}> <header className={styles.header}>
<h2 className={styles.label}>Your Note</h2> <h2 className={styles.label}>Your Note</h2>
<div className={styles.buttons}> <div className={styles.buttons}>
<Button <Tooltip.Provider delayDuration={0}>
icon={copying ? <FaCheck /> : <LuCopy />} <Button
tooltip="Copy Note" icon={copying ? <FaCheck /> : <LuCopy />}
onClick={() => copy(note)} tooltip="Copy Note"
/> onClick={() => copy(note)}
<Button />
icon={<LuDownload />} <Button
tooltip="Download Note" icon={<LuDownload />}
onClick={() => download('Moodit Note.txt', note)} tooltip="Download Note"
/> onClick={() => download('Moodit Note.txt', note)}
<Button />
critical={!history} <Button
icon={history ? <FaUndo /> : <BiTrash />} critical={!history}
recommended={!!history} icon={history ? <FaUndo /> : <BiTrash />}
tooltip={history ? 'Restore Note' : 'Clear Note'} recommended={!!history}
onClick={() => (history ? restore() : clear())} tooltip={history ? 'Restore Note' : 'Clear Note'}
/> onClick={() => (history ? restore() : clear())}
/>
</Tooltip.Provider>
</div> </div>
</header> </header>

View file

@ -1,5 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'motion/react';
import * as TooltipPrimitive from '@radix-ui/react-tooltip'; import * as TooltipPrimitive from '@radix-ui/react-tooltip';
import { slideX, slideY, mix, fade } from '@/lib/motion'; import { slideX, slideY, mix, fade } from '@/lib/motion';
@ -24,14 +24,12 @@ interface TooltipProps {
children: JSX.Element; children: JSX.Element;
content: string; content: string;
placement?: Placement; placement?: Placement;
showDelay?: number;
} }
export function Tooltip({ export function Tooltip({
children, children,
content, content,
placement = 'top', placement = 'top',
showDelay = 500,
}: TooltipProps) { }: TooltipProps) {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -52,35 +50,56 @@ export function Tooltip({
const variants = mix(fade(), slide!); const variants = mix(fade(), slide!);
return ( return (
<TooltipPrimitive.Provider delayDuration={showDelay}> <TooltipPrimitive.Root open={isOpen} onOpenChange={o => setIsOpen(o)}>
<TooltipPrimitive.Root open={isOpen} onOpenChange={o => setIsOpen(o)}> <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
<AnimatePresence> <AnimatePresence>
{isOpen && ( {isOpen && (
<TooltipPrimitive.Portal forceMount> <TooltipPrimitive.Portal forceMount>
<TooltipPrimitive.Content <TooltipPrimitive.Content
align={align} align={align}
asChild asChild
className={styles.tooltip}
collisionPadding={8}
side={side}
sideOffset={12}
>
<motion.div
animate="show"
className={styles.tooltip} className={styles.tooltip}
collisionPadding={8} exit="hidden"
side={side} initial="hidden"
sideOffset={12} variants={variants}
> >
<motion.div {content}
animate="show" </motion.div>
className={styles.tooltip} </TooltipPrimitive.Content>
exit="hidden" </TooltipPrimitive.Portal>
initial="hidden" )}
variants={variants} </AnimatePresence>
> </TooltipPrimitive.Root>
{content} );
</motion.div> }
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal> interface TooltipProviderProps {
)} children: React.ReactNode;
</AnimatePresence> delayDuration?: number;
</TooltipPrimitive.Root> skipDelayDuration?: number;
}
function Provider({
children,
delayDuration = 500,
skipDelayDuration = 0,
}: TooltipProviderProps) {
return (
<TooltipPrimitive.Provider
delayDuration={delayDuration}
skipDelayDuration={skipDelayDuration}
>
{children}
</TooltipPrimitive.Provider> </TooltipPrimitive.Provider>
); );
} }
Tooltip.Provider = Provider;

View file

@ -5,7 +5,7 @@ import {
useRef, useRef,
useContext, useContext,
} from 'react'; } from 'react';
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence } from 'motion/react';
import { Snackbar } from '@/components/snackbar'; import { Snackbar } from '@/components/snackbar';

View file

@ -21,6 +21,8 @@ import { PiBirdFill, PiDogBold } from 'react-icons/pi/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const animals: Category = { export const animals: Category = {
icon: <FaDog />, icon: <FaDog />,
id: 'animals', id: 'animals',
@ -29,97 +31,97 @@ export const animals: Category = {
icon: <PiBirdFill />, icon: <PiBirdFill />,
id: 'birds', id: 'birds',
label: 'Birds', label: 'Birds',
src: '/sounds/animals/birds.mp3', src: getAssetPath('/sounds/animals/birds.mp3'),
}, },
{ {
icon: <GiSeagull />, icon: <GiSeagull />,
id: 'seagulls', id: 'seagulls',
label: 'Seagulls', label: 'Seagulls',
src: '/sounds/animals/seagulls.mp3', src: getAssetPath('/sounds/animals/seagulls.mp3'),
}, },
{ {
icon: <GiCricket />, icon: <GiCricket />,
id: 'crickets', id: 'crickets',
label: 'Crickets', label: 'Crickets',
src: '/sounds/animals/crickets.mp3', src: getAssetPath('/sounds/animals/crickets.mp3'),
}, },
{ {
icon: <GiWolfHead />, icon: <GiWolfHead />,
id: 'wolf', id: 'wolf',
label: 'Wolf', label: 'Wolf',
src: '/sounds/animals/wolf.mp3', src: getAssetPath('/sounds/animals/wolf.mp3'),
}, },
{ {
icon: <GiOwl />, icon: <GiOwl />,
id: 'owl', id: 'owl',
label: 'Owl', label: 'Owl',
src: '/sounds/animals/owl.mp3', src: getAssetPath('/sounds/animals/owl.mp3'),
}, },
{ {
icon: <FaFrog />, icon: <FaFrog />,
id: 'frog', id: 'frog',
label: 'Frog', label: 'Frog',
src: '/sounds/animals/frog.mp3', src: getAssetPath('/sounds/animals/frog.mp3'),
}, },
{ {
icon: <PiDogBold />, icon: <PiDogBold />,
id: 'dog-barking', id: 'dog-barking',
label: 'Dog Barking', label: 'Dog Barking',
src: '/sounds/animals/dog-barking.mp3', src: getAssetPath('/sounds/animals/dog-barking.mp3'),
}, },
{ {
icon: <FaHorseHead />, icon: <FaHorseHead />,
id: 'horse-galopp', id: 'horse-gallop',
label: 'Horse Galopp', label: 'Horse Gallop',
src: '/sounds/animals/horse-galopp.mp3', src: getAssetPath('/sounds/animals/horse-gallop.mp3'),
}, },
{ {
icon: <FaCat />, icon: <FaCat />,
id: 'cat-purring', id: 'cat-purring',
label: 'Cat Purring', label: 'Cat Purring',
src: '/sounds/animals/cat-purring.mp3', src: getAssetPath('/sounds/animals/cat-purring.mp3'),
}, },
{ {
icon: <FaCrow />, icon: <FaCrow />,
id: 'crows', id: 'crows',
label: 'Crows', label: 'Crows',
src: '/sounds/animals/crows.mp3', src: getAssetPath('/sounds/animals/crows.mp3'),
}, },
{ {
icon: <GiWhaleTail />, icon: <GiWhaleTail />,
id: 'whale', id: 'whale',
label: 'Whale', label: 'Whale',
src: '/sounds/animals/whale.mp3', src: getAssetPath('/sounds/animals/whale.mp3'),
}, },
{ {
icon: <GiTreeBeehive />, icon: <GiTreeBeehive />,
id: 'beehive', id: 'beehive',
label: 'Beehive', label: 'Beehive',
src: '/sounds/animals/beehive.mp3', src: getAssetPath('/sounds/animals/beehive.mp3'),
}, },
{ {
icon: <GiEgyptianBird />, icon: <GiEgyptianBird />,
id: 'woodpecker', id: 'woodpecker',
label: 'Woodpecker', label: 'Woodpecker',
src: '/sounds/animals/woodpecker.mp3', src: getAssetPath('/sounds/animals/woodpecker.mp3'),
}, },
{ {
icon: <GiChicken />, icon: <GiChicken />,
id: 'chickens', id: 'chickens',
label: 'Chickens', label: 'Chickens',
src: '/sounds/animals/chickens.mp3', src: getAssetPath('/sounds/animals/chickens.mp3'),
}, },
{ {
icon: <GiCow />, icon: <GiCow />,
id: 'cows', id: 'cows',
label: 'Cows', label: 'Cows',
src: '/sounds/animals/cows.mp3', src: getAssetPath('/sounds/animals/cows.mp3'),
}, },
{ {
icon: <GiSheep />, icon: <GiSheep />,
id: 'sheep', id: 'sheep',
label: 'Sheep', label: 'Sheep',
src: '/sounds/animals/sheep.mp3', src: getAssetPath('/sounds/animals/sheep.mp3'),
}, },
], ],
title: 'Animals', title: 'Animals',

View file

@ -3,6 +3,8 @@ import { BsSoundwave } from 'react-icons/bs/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const binaural: Category = { export const binaural: Category = {
icon: <TbWaveSine />, icon: <TbWaveSine />,
id: 'binaural', id: 'binaural',
@ -11,31 +13,31 @@ export const binaural: Category = {
icon: <BsSoundwave />, icon: <BsSoundwave />,
id: 'binaural-delta', id: 'binaural-delta',
label: 'Delta', label: 'Delta',
src: '/sounds/binaural/binaural-delta.wav', src: getAssetPath('/sounds/binaural/binaural-delta.wav'),
}, },
{ {
icon: <BsSoundwave />, icon: <BsSoundwave />,
id: 'binaural-theta', id: 'binaural-theta',
label: 'Theta', label: 'Theta',
src: '/sounds/binaural/binaural-theta.wav', src: getAssetPath('/sounds/binaural/binaural-theta.wav'),
}, },
{ {
icon: <BsSoundwave />, icon: <BsSoundwave />,
id: 'binaural-alpha', id: 'binaural-alpha',
label: 'Alpha', label: 'Alpha',
src: '/sounds/binaural/binaural-alpha.wav', src: getAssetPath('/sounds/binaural/binaural-alpha.wav'),
}, },
{ {
icon: <BsSoundwave />, icon: <BsSoundwave />,
id: 'binaural-beta', id: 'binaural-beta',
label: 'Beta', label: 'Beta',
src: '/sounds/binaural/binaural-beta.wav', src: getAssetPath('/sounds/binaural/binaural-beta.wav'),
}, },
{ {
icon: <BsSoundwave />, icon: <BsSoundwave />,
id: 'binaural-gamma', id: 'binaural-gamma',
label: 'Gamma', label: 'Gamma',
src: '/sounds/binaural/binaural-gamma.wav', src: getAssetPath('/sounds/binaural/binaural-gamma.wav'),
}, },
], ],
title: 'Binaural Beats', title: 'Binaural Beats',

View file

@ -11,6 +11,8 @@ import {
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const nature: Category = { export const nature: Category = {
icon: <BiSolidTree />, icon: <BiSolidTree />,
id: 'nature', id: 'nature',
@ -19,73 +21,73 @@ export const nature: Category = {
icon: <BiWater />, icon: <BiWater />,
id: 'river', id: 'river',
label: 'River', label: 'River',
src: '/sounds/nature/river.mp3', src: getAssetPath('/sounds/nature/river.mp3'),
}, },
{ {
icon: <FaWater />, icon: <FaWater />,
id: 'waves', id: 'waves',
label: 'Waves', label: 'Waves',
src: '/sounds/nature/waves.mp3', src: getAssetPath('/sounds/nature/waves.mp3'),
}, },
{ {
icon: <BsFire />, icon: <BsFire />,
id: 'campfire', id: 'campfire',
label: 'Campfire', label: 'Campfire',
src: '/sounds/nature/campfire.mp3', src: getAssetPath('/sounds/nature/campfire.mp3'),
}, },
{ {
icon: <FaWind />, icon: <FaWind />,
id: 'wind', id: 'wind',
label: 'Wind', label: 'Wind',
src: '/sounds/nature/wind.mp3', src: getAssetPath('/sounds/nature/wind.mp3'),
}, },
{ {
icon: <FaWind />, icon: <FaWind />,
id: 'howling-wind', id: 'howling-wind',
label: 'Howling Wind', label: 'Howling Wind',
src: '/sounds/nature/howling-wind.mp3', src: getAssetPath('/sounds/nature/howling-wind.mp3'),
}, },
{ {
icon: <BiSolidTree />, icon: <BiSolidTree />,
id: 'wind-in-trees', id: 'wind-in-trees',
label: 'Wind in Trees', label: 'Wind in Trees',
src: '/sounds/nature/wind-in-trees.mp3', src: getAssetPath('/sounds/nature/wind-in-trees.mp3'),
}, },
{ {
icon: <GiWaterfall />, icon: <GiWaterfall />,
id: 'waterfall', id: 'waterfall',
label: 'Waterfall', label: 'Waterfall',
src: '/sounds/nature/waterfall.mp3', src: getAssetPath('/sounds/nature/waterfall.mp3'),
}, },
{ {
icon: <FaRegSnowflake />, icon: <FaRegSnowflake />,
id: 'walk-in-snow', id: 'walk-in-snow',
label: 'Walk in Snow', label: 'Walk in Snow',
src: '/sounds/nature/walk-in-snow.mp3', src: getAssetPath('/sounds/nature/walk-in-snow.mp3'),
}, },
{ {
icon: <FaLeaf />, icon: <FaLeaf />,
id: 'walk-on-leaves', id: 'walk-on-leaves',
label: 'Walk on Leaves', label: 'Walk on Leaves',
src: '/sounds/nature/walk-on-leaves.mp3', src: getAssetPath('/sounds/nature/walk-on-leaves.mp3'),
}, },
{ {
icon: <GiStonePile />, icon: <GiStonePile />,
id: 'walk-on-gravel', id: 'walk-on-gravel',
label: 'Walk on Gravel', label: 'Walk on Gravel',
src: '/sounds/nature/walk-on-gravel.mp3', src: getAssetPath('/sounds/nature/walk-on-gravel.mp3'),
}, },
{ {
icon: <BsFillDropletFill />, icon: <BsFillDropletFill />,
id: 'droplets', id: 'droplets',
label: 'Droplets', label: 'Droplets',
src: '/sounds/nature/droplets.mp3', src: getAssetPath('/sounds/nature/droplets.mp3'),
}, },
{ {
icon: <FaTree />, icon: <FaTree />,
id: 'jungle', id: 'jungle',
label: 'Jungle', label: 'Jungle',
src: '/sounds/nature/jungle.mp3', src: getAssetPath('/sounds/nature/jungle.mp3'),
}, },
], ],
title: 'Nature', title: 'Nature',

View file

@ -3,6 +3,8 @@ import { BsSoundwave } from 'react-icons/bs/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const noise: Category = { export const noise: Category = {
icon: <BsSoundwave />, icon: <BsSoundwave />,
id: 'noise', id: 'noise',
@ -11,19 +13,19 @@ export const noise: Category = {
icon: <GiSoundWaves />, icon: <GiSoundWaves />,
id: 'white-noise', id: 'white-noise',
label: 'White Noise', label: 'White Noise',
src: '/sounds/noise/white-noise.wav', src: getAssetPath('/sounds/noise/white-noise.wav'),
}, },
{ {
icon: <GiSoundWaves />, icon: <GiSoundWaves />,
id: 'pink-noise', id: 'pink-noise',
label: 'Pink Noise', label: 'Pink Noise',
src: '/sounds/noise/pink-noise.wav', src: getAssetPath('/sounds/noise/pink-noise.wav'),
}, },
{ {
icon: <GiSoundWaves />, icon: <GiSoundWaves />,
id: 'brown-noise', id: 'brown-noise',
label: 'Brown Noise', label: 'Brown Noise',
src: '/sounds/noise/brown-noise.wav', src: getAssetPath('/sounds/noise/brown-noise.wav'),
}, },
], ],
title: 'Noise', title: 'Noise',

View file

@ -18,6 +18,8 @@ import { FaBookOpen } from 'react-icons/fa6/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const places: Category = { export const places: Category = {
icon: <MdLocationPin />, icon: <MdLocationPin />,
id: 'places', id: 'places',
@ -26,97 +28,97 @@ export const places: Category = {
icon: <BiSolidCoffeeAlt />, icon: <BiSolidCoffeeAlt />,
id: 'cafe', id: 'cafe',
label: 'Cafe', label: 'Cafe',
src: '/sounds/places/cafe.mp3', src: getAssetPath('/sounds/places/cafe.mp3'),
}, },
{ {
icon: <BiSolidPlaneAlt />, icon: <BiSolidPlaneAlt />,
id: 'airport', id: 'airport',
label: 'Airport', label: 'Airport',
src: '/sounds/places/airport.mp3', src: getAssetPath('/sounds/places/airport.mp3'),
}, },
{ {
icon: <FaChurch />, icon: <FaChurch />,
id: 'church', id: 'church',
label: 'Church', label: 'Church',
src: '/sounds/places/church.mp3', src: getAssetPath('/sounds/places/church.mp3'),
}, },
{ {
icon: <MdTempleBuddhist />, icon: <MdTempleBuddhist />,
id: 'temple', id: 'temple',
label: 'Temple', label: 'Temple',
src: '/sounds/places/temple.mp3', src: getAssetPath('/sounds/places/temple.mp3'),
}, },
{ {
icon: <MdConstruction />, icon: <MdConstruction />,
id: 'construction-site', id: 'construction-site',
label: 'Construction Site', label: 'Construction Site',
src: '/sounds/places/construction-site.mp3', src: getAssetPath('/sounds/places/construction-site.mp3'),
}, },
{ {
icon: <TbScubaMask />, icon: <TbScubaMask />,
id: 'underwater', id: 'underwater',
label: 'Underwater', label: 'Underwater',
src: '/sounds/places/underwater.mp3', src: getAssetPath('/sounds/places/underwater.mp3'),
}, },
{ {
icon: <TbBeerFilled />, icon: <TbBeerFilled />,
id: 'crowded-bar', id: 'crowded-bar',
label: 'Crowded Bar', label: 'Crowded Bar',
src: '/sounds/places/crowded-bar.mp3', src: getAssetPath('/sounds/places/crowded-bar.mp3'),
}, },
{ {
icon: <GiVillage />, icon: <GiVillage />,
id: 'night-village', id: 'night-village',
label: 'Night Village', label: 'Night Village',
src: '/sounds/places/night-village.mp3', src: getAssetPath('/sounds/places/night-village.mp3'),
}, },
{ {
icon: <FaSubway />, icon: <FaSubway />,
id: 'subway-station', id: 'subway-station',
label: 'Subway Station', label: 'Subway Station',
src: '/sounds/places/subway-station.mp3', src: getAssetPath('/sounds/places/subway-station.mp3'),
}, },
{ {
icon: <HiOfficeBuilding />, icon: <HiOfficeBuilding />,
id: 'office', id: 'office',
label: 'Office', label: 'Office',
src: '/sounds/places/office.mp3', src: getAssetPath('/sounds/places/office.mp3'),
}, },
{ {
icon: <FaShoppingBasket />, icon: <FaShoppingBasket />,
id: 'supermarket', id: 'supermarket',
label: 'Supermarket', label: 'Supermarket',
src: '/sounds/places/supermarket.mp3', src: getAssetPath('/sounds/places/supermarket.mp3'),
}, },
{ {
icon: <GiCarousel />, icon: <GiCarousel />,
id: 'carousel', id: 'carousel',
label: 'Carousel', label: 'Carousel',
src: '/sounds/places/carousel.mp3', src: getAssetPath('/sounds/places/carousel.mp3'),
}, },
{ {
icon: <AiFillExperiment />, icon: <AiFillExperiment />,
id: 'laboratory', id: 'laboratory',
label: 'Laboratory', label: 'Laboratory',
src: '/sounds/places/laboratory.mp3', src: getAssetPath('/sounds/places/laboratory.mp3'),
}, },
{ {
icon: <BiSolidDryer />, icon: <BiSolidDryer />,
id: 'laundry-room', id: 'laundry-room',
label: 'Laundry Room', label: 'Laundry Room',
src: '/sounds/places/laundry-room.mp3', src: getAssetPath('/sounds/places/laundry-room.mp3'),
}, },
{ {
icon: <IoRestaurant />, icon: <IoRestaurant />,
id: 'restaurant', id: 'restaurant',
label: 'Restaurant', label: 'Restaurant',
src: '/sounds/places/restaurant.mp3', src: getAssetPath('/sounds/places/restaurant.mp3'),
}, },
{ {
icon: <FaBookOpen />, icon: <FaBookOpen />,
id: 'library', id: 'library',
label: 'Library', label: 'Library',
src: '/sounds/places/library.mp3', src: getAssetPath('/sounds/places/library.mp3'),
}, },
], ],
title: 'Places', title: 'Places',

View file

@ -10,6 +10,8 @@ import { MdOutlineThunderstorm } from 'react-icons/md/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const rain: Category = { export const rain: Category = {
icon: <BsFillCloudRainFill />, icon: <BsFillCloudRainFill />,
id: 'rain', id: 'rain',
@ -18,49 +20,49 @@ export const rain: Category = {
icon: <BsFillCloudRainFill />, icon: <BsFillCloudRainFill />,
id: 'light-rain', id: 'light-rain',
label: 'Light Rain', label: 'Light Rain',
src: '/sounds/rain/light-rain.mp3', src: getAssetPath('/sounds/rain/light-rain.mp3'),
}, },
{ {
icon: <BsFillCloudRainHeavyFill />, icon: <BsFillCloudRainHeavyFill />,
id: 'heavy-rain', id: 'heavy-rain',
label: 'Heavy Rain', label: 'Heavy Rain',
src: '/sounds/rain/heavy-rain.mp3', src: getAssetPath('/sounds/rain/heavy-rain.mp3'),
}, },
{ {
icon: <MdOutlineThunderstorm />, icon: <MdOutlineThunderstorm />,
id: 'thunder', id: 'thunder',
label: 'Thunder', label: 'Thunder',
src: '/sounds/rain/thunder.mp3', src: getAssetPath('/sounds/rain/thunder.mp3'),
}, },
{ {
icon: <GiWindow />, icon: <GiWindow />,
id: 'rain-on-window', id: 'rain-on-window',
label: 'Rain on Window', label: 'Rain on Window',
src: '/sounds/rain/rain-on-window.mp3', src: getAssetPath('/sounds/rain/rain-on-window.mp3'),
}, },
{ {
icon: <FaCarSide />, icon: <FaCarSide />,
id: 'rain-on-car-roof', id: 'rain-on-car-roof',
label: 'Rain on Car Roof', label: 'Rain on Car Roof',
src: '/sounds/rain/rain-on-car-roof.mp3', src: getAssetPath('/sounds/rain/rain-on-car-roof.mp3'),
}, },
{ {
icon: <BsUmbrellaFill />, icon: <BsUmbrellaFill />,
id: 'rain-on-umbrella', id: 'rain-on-umbrella',
label: 'Rain on Umbrella', label: 'Rain on Umbrella',
src: '/sounds/rain/rain-on-umbrella.mp3', src: getAssetPath('/sounds/rain/rain-on-umbrella.mp3'),
}, },
{ {
icon: <PiTentFill />, icon: <PiTentFill />,
id: 'rain-on-tent', id: 'rain-on-tent',
label: 'Rain on Tent', label: 'Rain on Tent',
src: '/sounds/rain/rain-on-tent.mp3', src: getAssetPath('/sounds/rain/rain-on-tent.mp3'),
}, },
{ {
icon: <FaLeaf />, icon: <FaLeaf />,
id: 'rain-on-leaves', id: 'rain-on-leaves',
label: 'Rain on Leaves', label: 'Rain on Leaves',
src: '/sounds/rain/rain-on-leaves.mp3', src: getAssetPath('/sounds/rain/rain-on-leaves.mp3'),
}, },
], ],
title: 'Rain', title: 'Rain',

View file

@ -14,6 +14,8 @@ import { PiVinylRecord } from 'react-icons/pi/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const things: Category = { export const things: Category = {
icon: <MdSmartToy />, icon: <MdSmartToy />,
id: 'things', id: 'things',
@ -22,97 +24,97 @@ export const things: Category = {
icon: <BsFillKeyboardFill />, icon: <BsFillKeyboardFill />,
id: 'keyboard', id: 'keyboard',
label: 'Keyboard', label: 'Keyboard',
src: '/sounds/things/keyboard.mp3', src: getAssetPath('/sounds/things/keyboard.mp3'),
}, },
{ {
icon: <FaKeyboard />, icon: <FaKeyboard />,
id: 'typewriter', id: 'typewriter',
label: 'Typewriter', label: 'Typewriter',
src: '/sounds/things/typewriter.mp3', src: getAssetPath('/sounds/things/typewriter.mp3'),
}, },
{ {
icon: <RiFilePaper2Fill />, icon: <RiFilePaper2Fill />,
id: 'paper', id: 'paper',
label: 'Paper', label: 'Paper',
src: '/sounds/things/paper.mp3', src: getAssetPath('/sounds/things/paper.mp3'),
}, },
{ {
icon: <FaClock />, icon: <FaClock />,
id: 'clock', id: 'clock',
label: 'Clock', label: 'Clock',
src: '/sounds/things/clock.mp3', src: getAssetPath('/sounds/things/clock.mp3'),
}, },
{ {
icon: <GiWindchimes />, icon: <GiWindchimes />,
id: 'wind-chimes', id: 'wind-chimes',
label: 'Wind Chimes', label: 'Wind Chimes',
src: '/sounds/things/wind-chimes.mp3', src: getAssetPath('/sounds/things/wind-chimes.mp3'),
}, },
{ {
icon: <TbBowlFilled />, icon: <TbBowlFilled />,
id: 'singing-bowl', id: 'singing-bowl',
label: 'Singing Bowl', label: 'Singing Bowl',
src: '/sounds/things/singing-bowl.mp3', src: getAssetPath('/sounds/things/singing-bowl.mp3'),
}, },
{ {
icon: <FaFan />, icon: <FaFan />,
id: 'ceiling-fan', id: 'ceiling-fan',
label: 'Ceiling Fan', label: 'Ceiling Fan',
src: '/sounds/things/ceiling-fan.mp3', src: getAssetPath('/sounds/things/ceiling-fan.mp3'),
}, },
{ {
icon: <BiSolidDryer />, icon: <BiSolidDryer />,
id: 'dryer', id: 'dryer',
label: 'Dryer', label: 'Dryer',
src: '/sounds/things/dryer.mp3', src: getAssetPath('/sounds/things/dryer.mp3'),
}, },
{ {
icon: <GiFilmProjector />, icon: <GiFilmProjector />,
id: 'slide-projector', id: 'slide-projector',
label: 'Slide Projector', label: 'Slide Projector',
src: '/sounds/things/slide-projector.mp3', src: getAssetPath('/sounds/things/slide-projector.mp3'),
}, },
{ {
icon: <MdWaterDrop />, icon: <MdWaterDrop />,
id: 'boiling-water', id: 'boiling-water',
label: 'Boiling Water', label: 'Boiling Water',
src: '/sounds/things/boiling-water.mp3', src: getAssetPath('/sounds/things/boiling-water.mp3'),
}, },
{ {
icon: <RiBubbleChartFill />, icon: <RiBubbleChartFill />,
id: 'bubbles', id: 'bubbles',
label: 'Bubbles', label: 'Bubbles',
src: '/sounds/things/bubbles.mp3', src: getAssetPath('/sounds/things/bubbles.mp3'),
}, },
{ {
icon: <MdRadio />, icon: <MdRadio />,
id: 'tuning-radio', id: 'tuning-radio',
label: 'Tuning Radio', label: 'Tuning Radio',
src: '/sounds/things/tuning-radio.mp3', src: getAssetPath('/sounds/things/tuning-radio.mp3'),
}, },
{ {
icon: <IoIosRadio />, icon: <IoIosRadio />,
id: 'morse-code', id: 'morse-code',
label: 'Morse Code', label: 'Morse Code',
src: '/sounds/things/morse-code.mp3', src: getAssetPath('/sounds/things/morse-code.mp3'),
}, },
{ {
icon: <GiWashingMachine />, icon: <GiWashingMachine />,
id: 'washing-machine', id: 'washing-machine',
label: 'Washing Machine', label: 'Washing Machine',
src: '/sounds/things/washing-machine.mp3', src: getAssetPath('/sounds/things/washing-machine.mp3'),
}, },
{ {
icon: <PiVinylRecord />, icon: <PiVinylRecord />,
id: 'vinyl-effect', id: 'vinyl-effect',
label: 'Vinyl Effect', label: 'Vinyl Effect',
src: '/sounds/things/vinyl-effect.mp3', src: getAssetPath('/sounds/things/vinyl-effect.mp3'),
}, },
{ {
icon: <TbWiper />, icon: <TbWiper />,
id: 'windshield-wipers', id: 'windshield-wipers',
label: 'Windshield Wipers', label: 'Windshield Wipers',
src: '/sounds/things/windshield-wipers.mp3', src: getAssetPath('/sounds/things/windshield-wipers.mp3'),
}, },
], ],
title: 'Things', title: 'Things',

View file

@ -5,6 +5,8 @@ import { TbSailboat } from 'react-icons/tb/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const transport: Category = { export const transport: Category = {
icon: <FaCarSide />, icon: <FaCarSide />,
id: 'transport', id: 'transport',
@ -13,37 +15,37 @@ export const transport: Category = {
icon: <BiSolidTrain />, icon: <BiSolidTrain />,
id: 'train', id: 'train',
label: 'Train', label: 'Train',
src: '/sounds/transport/train.mp3', src: getAssetPath('/sounds/transport/train.mp3'),
}, },
{ {
icon: <BiSolidTrain />, icon: <BiSolidTrain />,
id: 'inside-a-train', id: 'inside-a-train',
label: 'Inside a Train', label: 'Inside a Train',
src: '/sounds/transport/inside-a-train.mp3', src: getAssetPath('/sounds/transport/inside-a-train.mp3'),
}, },
{ {
icon: <BiSolidPlaneAlt />, icon: <BiSolidPlaneAlt />,
id: 'airplane', id: 'airplane',
label: 'Airplane', label: 'Airplane',
src: '/sounds/transport/airplane.mp3', src: getAssetPath('/sounds/transport/airplane.mp3'),
}, },
{ {
icon: <GiSubmarine />, icon: <GiSubmarine />,
id: 'submarine', id: 'submarine',
label: 'Submarine', label: 'Submarine',
src: '/sounds/transport/submarine.mp3', src: getAssetPath('/sounds/transport/submarine.mp3'),
}, },
{ {
icon: <GiSailboat />, icon: <GiSailboat />,
id: 'sailboat', id: 'sailboat',
label: 'Sailboat', label: 'Sailboat',
src: '/sounds/transport/sailboat.mp3', src: getAssetPath('/sounds/transport/sailboat.mp3'),
}, },
{ {
icon: <TbSailboat />, icon: <TbSailboat />,
id: 'rowing-boat', id: 'rowing-boat',
label: 'Rowing Boat', label: 'Rowing Boat',
src: '/sounds/transport/rowing-boat.mp3', src: getAssetPath('/sounds/transport/rowing-boat.mp3'),
}, },
], ],
title: 'Transport', title: 'Transport',

View file

@ -6,6 +6,8 @@ import { RiSparkling2Fill } from 'react-icons/ri/index';
import type { Category } from '../types'; import type { Category } from '../types';
import { getAssetPath } from '@/helpers/path';
export const urban: Category = { export const urban: Category = {
icon: <FaCity />, icon: <FaCity />,
id: 'urban', id: 'urban',
@ -14,43 +16,43 @@ export const urban: Category = {
icon: <PiRoadHorizonFill />, icon: <PiRoadHorizonFill />,
id: 'highway', id: 'highway',
label: 'Highway', label: 'Highway',
src: '/sounds/urban/highway.mp3', src: getAssetPath('/sounds/urban/highway.mp3'),
}, },
{ {
icon: <FaRoad />, icon: <FaRoad />,
id: 'road', id: 'road',
label: 'Road', label: 'Road',
src: '/sounds/urban/road.mp3', src: getAssetPath('/sounds/urban/road.mp3'),
}, },
{ {
icon: <PiSirenBold />, icon: <PiSirenBold />,
id: 'ambulance-siren', id: 'ambulance-siren',
label: 'Ambulance Siren', label: 'Ambulance Siren',
src: '/sounds/urban/ambulance-siren.mp3', src: getAssetPath('/sounds/urban/ambulance-siren.mp3'),
}, },
{ {
icon: <BsSoundwave />, icon: <BsSoundwave />,
id: 'busy-street', id: 'busy-street',
label: 'Busy Street', label: 'Busy Street',
src: '/sounds/urban/busy-street.mp3', src: getAssetPath('/sounds/urban/busy-street.mp3'),
}, },
{ {
icon: <BsPeopleFill />, icon: <BsPeopleFill />,
id: 'crowd', id: 'crowd',
label: 'Crowd', label: 'Crowd',
src: '/sounds/urban/crowd.mp3', src: getAssetPath('/sounds/urban/crowd.mp3'),
}, },
{ {
icon: <BiSolidTraffic />, icon: <BiSolidTraffic />,
id: 'traffic', id: 'traffic',
label: 'Traffic', label: 'Traffic',
src: '/sounds/urban/traffic.mp3', src: getAssetPath('/sounds/urban/traffic.mp3'),
}, },
{ {
icon: <RiSparkling2Fill />, icon: <RiSparkling2Fill />,
id: 'fireworks', id: 'fireworks',
label: 'Fireworks', label: 'Fireworks',
src: '/sounds/urban/fireworks.mp3', src: getAssetPath('/sounds/urban/fireworks.mp3'),
}, },
], ],
title: 'Urban', title: 'Urban',

7
src/helpers/path.ts Normal file
View file

@ -0,0 +1,7 @@
export function getAssetPath(relativePath: string): string {
const baseURL = import.meta.env.BASE_URL;
const withoutTrailingSlash = baseURL.replace(/\/+$/, '');
const withoutLeadingSlash = relativePath.replace(/^\/+/, '');
return `${withoutTrailingSlash}/${withoutLeadingSlash}`;
}

View file

@ -47,6 +47,10 @@ export function useSound(
preload: options.preload ?? false, preload: options.preload ?? false,
src: src, src: src,
}); });
if (window.navigator.audioSession) {
window.navigator.audioSession.type = 'playback';
}
} }
return sound; return sound;

View file

@ -8,11 +8,13 @@ import Source from '@/components/source.astro';
import Footer from '@/components/footer.astro'; import Footer from '@/components/footer.astro';
import { App } from '@/components/app'; import { App } from '@/components/app';
import CategoryIcons from '@/components/categories/category-icons/category-icons';
--- ---
<Layout title="Moodist: Ambient Sounds for Focus and Calm"> <Layout title="Moodist: Ambient Sounds for Focus and Calm">
<Donate /> <Donate />
<Hero /> <Hero />
<CategoryIcons client:load />
<App client:load /> <App client:load />
<About /> <About />
<Source /> <Source />