├── .dockerignore
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── README_CN.md
├── app
├── [lang]
│ ├── (default)
│ │ ├── _components
│ │ │ ├── footer
│ │ │ │ └── index.tsx
│ │ │ ├── header
│ │ │ │ └── index.tsx
│ │ │ ├── hero
│ │ │ │ └── index.tsx
│ │ │ ├── input
│ │ │ │ └── index.tsx
│ │ │ ├── langswitch
│ │ │ │ └── index.tsx
│ │ │ ├── producthunt
│ │ │ │ └── index.tsx
│ │ │ ├── social
│ │ │ │ └── index.tsx
│ │ │ ├── tab
│ │ │ │ └── index.tsx
│ │ │ └── videos
│ │ │ │ └── index.tsx
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ ├── video
│ │ │ └── [uuid]
│ │ │ │ └── page.tsx
│ │ └── videos
│ │ │ └── [cate]
│ │ │ └── page.tsx
│ ├── globals.css
│ └── layout.tsx
└── api
│ ├── gen-video
│ └── route.ts
│ ├── process
│ └── update-videos
│ │ └── route.ts
│ └── save-user
│ └── route.ts
├── components.json
├── components
└── ui
│ ├── menubar.tsx
│ └── select.tsx
├── data
└── install.sql
├── debug
└── apitest.http
├── deploy
├── Dockerfile
└── nginx.conf
├── dictionaries
├── de.json
├── en.json
├── fr.json
├── ja.json
├── ko.json
└── zh.json
├── lib
├── index.ts
├── resp.ts
└── utils.ts
├── middleware.ts
├── models
├── db.ts
├── user.ts
└── video.ts
├── next.config.mjs
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── preview.png
├── preview_cn.png
├── public
├── email.svg
├── favicon.ico
├── next.svg
└── vercel.svg
├── services
├── i18n.ts
├── user.ts
└── video.ts
├── tailwind.config.ts
├── tsconfig.json
└── types
├── nav.d.ts
├── user.d.ts
└── video.d.ts
/.dockerignore:
--------------------------------------------------------------------------------
1 | .next
2 | .vercel
3 | .vscode
4 | data
5 | debug
6 | node_modules
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*
30 | Makefile
31 | data/data.json
32 |
33 | # vercel
34 | .vercel
35 |
36 | # typescript
37 | *.tsbuildinfo
38 | next-env.d.ts
39 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.defaultFormatter": "esbenp.prettier-vscode",
3 | "[javascript]": {
4 | "editor.defaultFormatter": "esbenp.prettier-vscode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sora.FM
2 |
3 | Sora AI Video Generator
4 |
5 | > Sora text-to-video API is not available. All the videos display on Sora.FM are generated by OpenAI's red team.
6 |
7 | [中文说明](./README_CN.md)
8 |
9 | ## Live Demo
10 |
11 | [https://sorafm.trys.ai](https://sorafm.trys.ai)
12 |
13 | 
14 |
15 | ## Deploy with Vercel
16 |
17 | [](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fall-in-aigc%2Fsorafm&env=POSTGRES_URL,WEB_BASE_URI&envDescription=POSTGRES_URL%20needed%20for%20the%20application&project-name=my-sora-project&repository-name=my-sora-project&redirect-url=https%3A%2F%2Fsorafm.trys.ai&demo-title=Sora.FM&demo-description=Sora%20AI%20Video%20generator&demo-url=https%3A%2F%2Fsorafm.trys.ai&demo-image=https%3A%2F%2Fgithub.com%2Fall-in-aigc%2Fsorafm%2Fraw%2Fmain%2Fpreview.png)
18 |
19 | ## Deploy with docker
20 |
21 | - build image
22 |
23 | ```shell
24 | sudo docker build -f deploy/Dockerfile -t sorafm:latest .
25 | ```
26 |
27 | - run server
28 |
29 | ```shell
30 | sudo docker run -itd -p 127.0.0.1:8014:8080 --restart=always sorafm:latest
31 | ```
32 |
33 | - nginx conf
34 |
35 | ```txt
36 | server {
37 | listen 80;
38 |
39 | location / {
40 | proxy_pass http://127.0.0.1:8014/;
41 | proxy_set_header Host $http_host;
42 | proxy_set_header X-Real-IP $remote_addr;
43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
44 | proxy_set_header X-Forwarded-Proto $scheme;
45 | }
46 |
47 | error_log /var/log/nginx/sorafm.error;
48 | }
49 | ```
50 |
51 | ## Local development
52 |
53 | 1. clone project
54 |
55 | ```shell
56 | git clone https://github.com/all-in-aigc/sorafm
57 | ```
58 |
59 | 2. install dependencies
60 |
61 | ```shell
62 | cd sorafm
63 | pnpm install
64 | ```
65 |
66 | 3. init database
67 |
68 | create your database use [local postgres](https://wiki.postgresql.org/wiki/Homebrew) or [vercel-postgres](https://vercel.com/docs/storage/vercel-postgres) or [supabase](https://supabase.com/)
69 |
70 | create tables from sql at `data/install.sql`
71 |
72 | 4. set environmental values
73 |
74 | put `.env.local` under root dir with values list below
75 |
76 | ```
77 | POSTGRES_URL="postgres://USER:PASSWORD@HOST/DB"
78 |
79 | WEB_BASE_URI="http://localhost:3000"
80 | ```
81 |
82 | 5. local development
83 |
84 | ```shell
85 | pnpm dev --port 3000
86 | ```
87 |
88 | open `http://localhost:3000` for preview
89 |
90 | ## Credit to
91 |
92 | - [aiwallpaper](https://aiwallpaper.shop) for code reference
93 | - [nextjs](https://nextjs.org/docs) for full-stack development
94 | - [node-postgres](https://node-postgres.com/) for data processing
95 | - [tailwindcss](https://tailwindcss.com/) for page building
96 |
97 | ## Other Things
98 |
99 | you can contact me at Twitter: https://twitter.com/idoubicc
100 |
101 | if this project is helpful to you, buy be a coffee.
102 |
103 |
104 |
105 | ## Star History
106 |
107 | [](https://star-history.com/#all-in-aigc/sorafm&Date)
108 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # Sora.FM
2 |
3 | Sora AI 视频生成器
4 |
5 | > Sora 文本生成视频 API 还未发布,网站上展示的所有视频都是由 OpenAI 官方生成的。
6 |
7 | ## 线上演示
8 |
9 | [https://sora.trys.ai](https://sora.trys.ai)
10 |
11 | 
12 |
13 | ## 使用 Vercel 一键部署
14 |
15 | [](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fall-in-aigc%2Fsorafm&env=POSTGRES_URL,WEB_BASE_URI&envDescription=POSTGRES_URL%20needed%20for%20the%20application&project-name=my-sora-project&repository-name=my-sora-project&redirect-url=https%3A%2F%2Fsora.trys.ai&demo-title=Sora.FM&demo-description=Sora%20AI%20Video%20generator&demo-url=https%3A%2F%2Fsora.trys.ai&demo-image=https%3A%2F%2Fgithub.com%2Fall-in-aigc%2Fsorafm%2Fraw%2Fmain%2Fpreview.png)
16 |
17 | ## 使用 Docker 部署
18 |
19 | - 构建镜像
20 |
21 | ```shell
22 | sudo docker build -f deploy/Dockerfile -t sorafm:latest .
23 | ```
24 |
25 | - 运行服务
26 |
27 | ```shell
28 | sudo docker run -itd -p 127.0.0.1:8014:8080 --restart=always sorafm:latest
29 | ```
30 |
31 | - 配置 nginx
32 |
33 | ```txt
34 | server {
35 | listen 80;
36 |
37 | location / {
38 | proxy_pass http://127.0.0.1:8014/;
39 | proxy_set_header Host $http_host;
40 | proxy_set_header X-Real-IP $remote_addr;
41 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
42 | proxy_set_header X-Forwarded-Proto $scheme;
43 | }
44 |
45 | error_log /var/log/nginx/sorafm.error;
46 | }
47 | ```
48 |
49 | ## 本地开发
50 |
51 | 1. 克隆项目
52 |
53 | ```shell
54 | git clone https://github.com/all-in-aigc/sorafm
55 | ```
56 |
57 | 2. 安装依赖
58 |
59 | ```shell
60 | cd sorafm
61 | pnpm install
62 | ```
63 |
64 | 3. 初始化数据库
65 |
66 | 使用本地的 [local postgres](https://wiki.postgresql.org/wiki/Homebrew) 或者托管的 [vercel-postgres](https://vercel.com/docs/storage/vercel-postgres) 或者 [supabase](https://supabase.com/)
67 |
68 | 使用 `data/install.sql` 文件里的建表语句创建数据表。
69 |
70 | 4. 设置环境变量
71 |
72 | 在项目根目录创建 `.env.local` 文件,写入如下配置:
73 |
74 | ```
75 | POSTGRES_URL="postgres://USER:PASSWORD@HOST/DB"
76 |
77 | WEB_BASE_URI="http://localhost:3000"
78 | ```
79 |
80 | 5. 本地开发
81 |
82 | ```shell
83 | pnpm dev --port 3000
84 | ```
85 |
86 | 打开 `http://localhost:3000` 预览
87 |
88 | ## 致谢以下项目
89 |
90 | - [aiwallpaper](https://aiwallpaper.shop) 提供代码参考
91 | - [nextjs](https://nextjs.org/docs) 全栈开发框架
92 | - [node-postgres](https://node-postgres.com/) 数据处理库
93 | - [tailwindcss](https://tailwindcss.com/) 页面构建
94 |
95 | ## 其他
96 |
97 | > 如果你想学习全栈开发,实现类似的产品,你可以参加我的 [全栈开发课程](https://mp.weixin.qq.com/s/4duIpeZkmqlKPa4jrcUdIA)
98 |
99 | 你可以在 Twitter 上联系我: https://twitter.com/idoubicc
100 |
101 | 或者关注我的微信公众号 👇
102 |
103 |
104 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/footer/index.tsx:
--------------------------------------------------------------------------------
1 | import Social from "../social";
2 |
3 | export default function ({ lang, dict }: { lang: string; dict: any }) {
4 | return (
5 |
6 |
7 |
8 |
19 |
20 |
58 |
96 |
127 | {lang.startsWith("zh") && (
128 |
129 |
130 |
131 |
132 |
141 |
142 |
143 |
144 | )}
145 |
146 |
147 |
148 |
149 |
150 | © Copyright 2024.{" "}
151 |
156 | Sora.FM
157 | {" "}
158 | All rights reserved.
159 |
160 |
161 |
162 |
163 | );
164 | }
165 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/header/index.tsx:
--------------------------------------------------------------------------------
1 | import Langswitch from "../langswitch";
2 | import { Nav } from "@/types/nav";
3 | import Social from "../social";
4 |
5 | export default function ({ lang, dict }: { lang: string; dict: any }) {
6 | const navigations: Nav[] = [];
7 |
8 | return (
9 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/hero/index.tsx:
--------------------------------------------------------------------------------
1 | import Input from "../input";
2 | import Producthunt from "../producthunt";
3 |
4 | export default function ({ dict }: { dict: any }) {
5 | return (
6 |
7 |
8 |
9 | {dict.brand.title}
10 |
11 |
12 | {dict.brand.sub_title}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {dict.subscribe.tip}
21 |
22 |
23 |
28 |
35 |
36 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/input/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { KeyboardEvent, useState } from "react";
4 | import { useParams, useRouter } from "next/navigation";
5 |
6 | import { toast } from "sonner";
7 |
8 | export default function ({ dict }: { dict: any }) {
9 | const params = useParams();
10 |
11 | const router = useRouter();
12 | const [email, setEmail] = useState("");
13 |
14 | const handleSubmit = async () => {
15 | if (!email) {
16 | toast.error("please enter your email");
17 | return;
18 | }
19 |
20 | try {
21 | const params = {
22 | email: email,
23 | };
24 | const resp = await fetch("/api/save-user", {
25 | method: "POST",
26 | headers: {
27 | "Content-Type": "application/json",
28 | },
29 | body: JSON.stringify(params),
30 | });
31 | const { code, message, data } = await resp.json();
32 | if (code !== 0) {
33 | toast.error(message);
34 | return;
35 | }
36 | if (data) {
37 | toast.success("subscribe ok");
38 | setEmail("");
39 | }
40 | } catch (e) {
41 | toast.error("subscribe failed");
42 | console.log("subscribe failed", e);
43 | }
44 | };
45 |
46 | const handleInputKeydown = (e: KeyboardEvent) => {
47 | if (e.code === "Enter" && !e.shiftKey) {
48 | if (e.keyCode !== 229) {
49 | e.preventDefault();
50 | handleSubmit();
51 | }
52 | }
53 | };
54 |
55 | return (
56 |
57 | setEmail(e.target.value)}
64 | onKeyDown={handleInputKeydown}
65 | />
66 |
71 | {dict.subscribe.button}
72 |
73 |
74 | );
75 | }
76 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/langswitch/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | Select,
5 | SelectContent,
6 | SelectItem,
7 | SelectTrigger,
8 | SelectValue,
9 | } from "@/components/ui/select";
10 | import { useParams, usePathname, useRouter } from "next/navigation";
11 |
12 | import { localeNames } from "@/services/i18n";
13 |
14 | export default function () {
15 | const params = useParams();
16 | const lang = params.lang as string;
17 | const router = useRouter();
18 | const pathname = usePathname();
19 |
20 | const handleSwitchLanguage = (value: string) => {
21 | if (value !== "lang") {
22 | const newPathName = pathname.replace(lang, value);
23 | router.push(newPathName);
24 | }
25 | };
26 |
27 | return (
28 |
29 |
30 |
31 |
32 |
33 | {Object.keys(localeNames).map((key: string) => {
34 | const name = localeNames[key];
35 | return (
36 |
37 | {name}
38 |
39 | );
40 | })}
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/producthunt/index.tsx:
--------------------------------------------------------------------------------
1 | export default function () {
2 | return (
3 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/social/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { BsBook, BsGithub, BsTwitterX } from "react-icons/bs";
4 |
5 | import { FaProductHunt } from "react-icons/fa";
6 | import { SiBuymeacoffee } from "react-icons/si";
7 | import { useParams } from "next/navigation";
8 |
9 | export default function () {
10 | const params = useParams();
11 | const lang = params.lang as string;
12 |
13 | return (
14 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/tab/index.tsx:
--------------------------------------------------------------------------------
1 | function classNames(...classes: string[]) {
2 | return classes.filter(Boolean).join(" ");
3 | }
4 |
5 | export default function ({
6 | lang,
7 | dict,
8 | cate,
9 | }: {
10 | lang: string;
11 | dict: any;
12 | cate: string;
13 | }) {
14 | const tabs = [
15 | {
16 | name: dict.tab.latest,
17 | href: `/${lang}/videos/latest`,
18 | current: cate === "" || cate === "latest",
19 | },
20 | {
21 | name: dict.tab.featured,
22 | href: `/${lang}/videos/featured`,
23 | current: cate === "featured",
24 | },
25 | {
26 | name: dict.tab.random,
27 | href: `/${lang}/videos/random`,
28 | current: cate === "random",
29 | },
30 | ];
31 |
32 | return (
33 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/_components/videos/index.tsx:
--------------------------------------------------------------------------------
1 | import { Video } from "@/types/video";
2 |
3 | export default ({ lang, videos }: { lang: string; videos: Video[] }) => {
4 | return (
5 |
6 |
7 |
8 | {videos.map((video: Video, idx: number) => {
9 | return (
10 |
49 | );
50 | })}
51 |
52 |
53 |
54 | );
55 | };
56 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/layout.tsx:
--------------------------------------------------------------------------------
1 | import Footer from "./_components/footer";
2 | import Header from "./_components/header";
3 | import { ReactNode } from "react";
4 | import { getDictionary } from "@/services/i18n";
5 |
6 | export default async function ({
7 | children,
8 | params,
9 | }: {
10 | children: ReactNode;
11 | params: { lang: string };
12 | }) {
13 | const dict = await getDictionary(params.lang);
14 |
15 | return (
16 |
17 |
18 | {children}
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/page.tsx:
--------------------------------------------------------------------------------
1 | import Hero from "./_components/hero";
2 | import { Metadata } from "next";
3 | import Tab from "./_components/tab";
4 | import Videos from "./_components/videos";
5 | import { getDictionary } from "@/services/i18n";
6 | import { getLatestVideos } from "@/models/video";
7 |
8 | export async function generateMetadata({
9 | params,
10 | }: {
11 | params: { lang: string };
12 | }): Promise {
13 | return {
14 | alternates: {
15 | canonical: `${process.env.WEB_BASE_URI}/${params.lang}`,
16 | },
17 | };
18 | }
19 |
20 | export default async function ({ params }: { params: { lang: string } }) {
21 | const videos = await getLatestVideos(1, 50);
22 | const dict = await getDictionary(params.lang);
23 |
24 | return (
25 |
26 |
27 |
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/video/[uuid]/page.tsx:
--------------------------------------------------------------------------------
1 | import { findVideoByUuid, getRandomVideos } from "@/models/video";
2 |
3 | import { Metadata } from "next";
4 | import Videos from "../../_components/videos";
5 | import { getDictionary } from "@/services/i18n";
6 |
7 | export async function generateMetadata({
8 | params,
9 | }: {
10 | params: { lang: string; uuid: string };
11 | }): Promise {
12 | const dict = await getDictionary(params.lang);
13 |
14 | let description = "";
15 |
16 | if (params.uuid) {
17 | const video = await findVideoByUuid(params.uuid);
18 | if (video) {
19 | description = video.video_description;
20 | }
21 | }
22 |
23 | return {
24 | description: `${description} ${dict.brand.title} | Sora.FM`,
25 | alternates: {
26 | canonical: `${process.env.WEB_BASE_URI}/${params.lang}/video/${params.uuid}`,
27 | },
28 | };
29 | }
30 |
31 | export default async function ({
32 | params,
33 | }: {
34 | params: { lang: string; uuid: string };
35 | }) {
36 | const dict = await getDictionary(params.lang);
37 | const video = await findVideoByUuid(params.uuid);
38 | const videos = await getRandomVideos(1, 50);
39 |
40 | return (
41 |
42 |
43 |
44 | {dict.showcase.title}
45 |
46 |
47 | {dict.showcase.sub_title}
48 |
49 |
50 | {video && (
51 |
52 |
66 |
67 |
68 |
69 |
79 | Your browser does not support the video tag.
80 |
81 |
82 |
83 |
84 |
85 |
91 |
95 |
96 |
97 |
98 | {video.video_description}
99 |
100 |
101 |
106 |
107 | {video.user_nickname}
108 |
109 |
110 |
111 |
112 |
113 |
114 | )}
115 |
116 |
117 |
118 |
119 | {dict.showcase.more_tip}
120 |
121 |
122 |
123 |
124 |
125 | );
126 | }
127 |
--------------------------------------------------------------------------------
/app/[lang]/(default)/videos/[cate]/page.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | getLatestVideos,
3 | getRandomVideos,
4 | getRecommendedVideos,
5 | } from "@/models/video";
6 |
7 | import Hero from "../../_components/hero";
8 | import { Metadata } from "next";
9 | import Tab from "../../_components/tab";
10 | import { Video } from "@/types/video";
11 | import Videos from "../../_components/videos";
12 | import { getDictionary } from "@/services/i18n";
13 |
14 | export async function generateMetadata({
15 | params,
16 | }: {
17 | params: { lang: string; cate: string };
18 | }): Promise {
19 | return {
20 | alternates: {
21 | canonical: `${process.env.WEB_BASE_URI}/${params.lang}/videos/${params.cate}`,
22 | },
23 | };
24 | }
25 |
26 | export default async function ({
27 | params,
28 | }: {
29 | params: { lang: string; cate: string };
30 | }) {
31 | const cate = params.cate;
32 | const page = 1;
33 | const limit = 50;
34 | const dict = await getDictionary(params.lang);
35 |
36 | let videos: Video[] = [];
37 | if (cate === "featured") {
38 | videos = await getRecommendedVideos(page, limit);
39 | } else if (cate === "random") {
40 | videos = await getRandomVideos(page, limit);
41 | } else {
42 | videos = await getLatestVideos(page, limit);
43 | }
44 |
45 | return (
46 |
47 |
48 |
49 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/app/[lang]/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 20 14.3% 4.1%;
9 | --card: 0 0% 100%;
10 | --card-foreground: 20 14.3% 4.1%;
11 | --popover: 0 0% 100%;
12 | --popover-foreground: 20 14.3% 4.1%;
13 | --primary: 24.6 95% 53.1%;
14 | --primary-foreground: 60 9.1% 97.8%;
15 | --secondary: 60 4.8% 95.9%;
16 | --secondary-foreground: 24 9.8% 10%;
17 | --muted: 60 4.8% 95.9%;
18 | --muted-foreground: 25 5.3% 44.7%;
19 | --accent: 60 4.8% 95.9%;
20 | --accent-foreground: 24 9.8% 10%;
21 | --destructive: 0 84.2% 60.2%;
22 | --destructive-foreground: 60 9.1% 97.8%;
23 | --border: 20 5.9% 90%;
24 | --input: 20 5.9% 90%;
25 | --ring: 24.6 95% 53.1%;
26 | --radius: 0.5rem;
27 | }
28 |
29 | .dark {
30 | --background: 20 14.3% 4.1%;
31 | --foreground: 60 9.1% 97.8%;
32 | --card: 20 14.3% 4.1%;
33 | --card-foreground: 60 9.1% 97.8%;
34 | --popover: 20 14.3% 4.1%;
35 | --popover-foreground: 60 9.1% 97.8%;
36 | --primary: 20.5 90.2% 48.2%;
37 | --primary-foreground: 60 9.1% 97.8%;
38 | --secondary: 12 6.5% 15.1%;
39 | --secondary-foreground: 60 9.1% 97.8%;
40 | --muted: 12 6.5% 15.1%;
41 | --muted-foreground: 24 5.4% 63.9%;
42 | --accent: 12 6.5% 15.1%;
43 | --accent-foreground: 60 9.1% 97.8%;
44 | --destructive: 0 72.2% 50.6%;
45 | --destructive-foreground: 60 9.1% 97.8%;
46 | --border: 12 6.5% 15.1%;
47 | --input: 12 6.5% 15.1%;
48 | --ring: 20.5 90.2% 48.2%;
49 | }
50 | }
51 |
52 | @layer base {
53 | * {
54 | @apply border-border;
55 | }
56 | body {
57 | @apply bg-background text-foreground;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/[lang]/layout.tsx:
--------------------------------------------------------------------------------
1 | import "./globals.css";
2 |
3 | import { Analytics } from "@vercel/analytics/react";
4 | import { Inter } from "next/font/google";
5 | import type { Metadata } from "next";
6 | import { Toaster } from "sonner";
7 | import { getDictionary } from "@/services/i18n";
8 |
9 | const inter = Inter({ subsets: ["latin"] });
10 |
11 | export async function generateMetadata({
12 | params,
13 | }: {
14 | params: { lang: string };
15 | }): Promise {
16 | const dict = await getDictionary(params.lang);
17 |
18 | return {
19 | title: {
20 | template: `%s, ${dict.brand.title} | Sora.FM`,
21 | default: `${dict.brand.title} | Sora.FM`,
22 | },
23 | description: `${dict.brand.title}, ${dict.brand.sub_title}`,
24 | keywords:
25 | "sora,sora fm,sora ai,openai sora,video ai,ai video,sora video,ai video generator,text to video,sora ai video,sora ai video generator,sora webui,sora showcase,sora ai showcases",
26 | };
27 | }
28 |
29 | export default function RootLayout({
30 | children,
31 | params: { lang },
32 | }: Readonly<{
33 | children: React.ReactNode;
34 | params: { lang: string };
35 | }>) {
36 | return (
37 |
38 |
39 |
40 |
41 |
42 |
43 | {children}
44 |
45 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/app/api/gen-video/route.ts:
--------------------------------------------------------------------------------
1 | import { respData, respErr } from "@/lib/resp";
2 |
3 | import { getRandomVideos } from "@/models/video";
4 |
5 | export async function POST(req: Request) {
6 | try {
7 | const { description } = await req.json();
8 | if (!description) {
9 | return respErr("invalid params");
10 | }
11 |
12 | // todo: call openai sora api to generate video
13 |
14 | const videos = await getRandomVideos(1, 1);
15 | if (videos.length === 0) {
16 | return respErr("gen video failed");
17 | }
18 |
19 | return respData(videos[0]);
20 | } catch (e) {
21 | console.log("gen video failed:", e);
22 | return respErr("gen video failed");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/api/process/update-videos/route.ts:
--------------------------------------------------------------------------------
1 | import { respData, respErr } from "@/lib/resp";
2 |
3 | import { getVideosFromFile } from "@/services/video";
4 | import { insertVideo } from "@/models/video";
5 |
6 | export async function POST(req: Request) {
7 | try {
8 | const allVideos = await getVideosFromFile();
9 | const allVideosCount = allVideos.length;
10 |
11 | console.log(`all videos count: ${allVideosCount}`);
12 |
13 | let existCount = 0;
14 | let newCount = 0;
15 | let failedCount = 0;
16 | for (let i = 0; i < allVideosCount; i++) {
17 | const video = allVideos[i];
18 | if (!video.uuid || !video.video_url) {
19 | continue;
20 | }
21 |
22 | video.user_uuid = process.env.ADMIN_USER_UUID;
23 |
24 | if (video.user_nickname === "OpenAI") {
25 | // console.log("video exist: ", video.uuid, video.video_url);
26 | // existCount += 1;
27 | // continue;
28 | }
29 |
30 | try {
31 | // console.log("video", video);
32 | await insertVideo(video);
33 | newCount += 1;
34 | console.log(
35 | "insert new video: ",
36 | video.uuid,
37 | video.video_url,
38 | i,
39 | allVideosCount - i
40 | );
41 | } catch (e) {
42 | failedCount += 1;
43 | console.log("insert video failed: ", video.uuid, video.video_url, i, e);
44 | }
45 | }
46 |
47 | return respData({
48 | all_count: allVideosCount,
49 | exist_count: existCount,
50 | new_count: newCount,
51 | failed_count: failedCount,
52 | });
53 | } catch (e) {
54 | console.log("update videos failed", e);
55 | return respErr("update videos failed");
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/api/save-user/route.ts:
--------------------------------------------------------------------------------
1 | import { respData, respErr } from "@/lib/resp";
2 |
3 | import { User } from "@/types/user";
4 | import { genUuid } from "@/lib";
5 | import { saveUser } from "@/services/user";
6 |
7 | export async function POST(req: Request) {
8 | try {
9 | const { email } = await req.json();
10 | if (!email) {
11 | return respErr("invalid params");
12 | }
13 | if (!email.includes("@")) {
14 | return respErr("invalid email");
15 | }
16 |
17 | const created_at = new Date().toISOString();
18 | const user_uuid = genUuid();
19 |
20 | const user: User = {
21 | email: email,
22 | nickname: "",
23 | avatar_url: "",
24 | created_at: created_at,
25 | uuid: user_uuid,
26 | };
27 | console.log("save user info", user);
28 |
29 | await saveUser(user);
30 |
31 | return respData(user);
32 | } catch (e) {
33 | console.log("save user failed", e);
34 | return respErr("save user failed");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/globals.css",
9 | "baseColor": "slate",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
--------------------------------------------------------------------------------
/components/ui/menubar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as MenubarPrimitive from "@radix-ui/react-menubar"
5 | import { Check, ChevronRight, Circle } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const MenubarMenu = MenubarPrimitive.Menu
10 |
11 | const MenubarGroup = MenubarPrimitive.Group
12 |
13 | const MenubarPortal = MenubarPrimitive.Portal
14 |
15 | const MenubarSub = MenubarPrimitive.Sub
16 |
17 | const MenubarRadioGroup = MenubarPrimitive.RadioGroup
18 |
19 | const Menubar = React.forwardRef<
20 | React.ElementRef,
21 | React.ComponentPropsWithoutRef
22 | >(({ className, ...props }, ref) => (
23 |
31 | ))
32 | Menubar.displayName = MenubarPrimitive.Root.displayName
33 |
34 | const MenubarTrigger = React.forwardRef<
35 | React.ElementRef,
36 | React.ComponentPropsWithoutRef
37 | >(({ className, ...props }, ref) => (
38 |
46 | ))
47 | MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName
48 |
49 | const MenubarSubTrigger = React.forwardRef<
50 | React.ElementRef,
51 | React.ComponentPropsWithoutRef & {
52 | inset?: boolean
53 | }
54 | >(({ className, inset, children, ...props }, ref) => (
55 |
64 | {children}
65 |
66 |
67 | ))
68 | MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName
69 |
70 | const MenubarSubContent = React.forwardRef<
71 | React.ElementRef,
72 | React.ComponentPropsWithoutRef
73 | >(({ className, ...props }, ref) => (
74 |
82 | ))
83 | MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName
84 |
85 | const MenubarContent = React.forwardRef<
86 | React.ElementRef,
87 | React.ComponentPropsWithoutRef
88 | >(
89 | (
90 | { className, align = "start", alignOffset = -4, sideOffset = 8, ...props },
91 | ref
92 | ) => (
93 |
94 |
105 |
106 | )
107 | )
108 | MenubarContent.displayName = MenubarPrimitive.Content.displayName
109 |
110 | const MenubarItem = React.forwardRef<
111 | React.ElementRef,
112 | React.ComponentPropsWithoutRef & {
113 | inset?: boolean
114 | }
115 | >(({ className, inset, ...props }, ref) => (
116 |
125 | ))
126 | MenubarItem.displayName = MenubarPrimitive.Item.displayName
127 |
128 | const MenubarCheckboxItem = React.forwardRef<
129 | React.ElementRef,
130 | React.ComponentPropsWithoutRef
131 | >(({ className, children, checked, ...props }, ref) => (
132 |
141 |
142 |
143 |
144 |
145 |
146 | {children}
147 |
148 | ))
149 | MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName
150 |
151 | const MenubarRadioItem = React.forwardRef<
152 | React.ElementRef,
153 | React.ComponentPropsWithoutRef
154 | >(({ className, children, ...props }, ref) => (
155 |
163 |
164 |
165 |
166 |
167 |
168 | {children}
169 |
170 | ))
171 | MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName
172 |
173 | const MenubarLabel = React.forwardRef<
174 | React.ElementRef,
175 | React.ComponentPropsWithoutRef & {
176 | inset?: boolean
177 | }
178 | >(({ className, inset, ...props }, ref) => (
179 |
188 | ))
189 | MenubarLabel.displayName = MenubarPrimitive.Label.displayName
190 |
191 | const MenubarSeparator = React.forwardRef<
192 | React.ElementRef,
193 | React.ComponentPropsWithoutRef
194 | >(({ className, ...props }, ref) => (
195 |
200 | ))
201 | MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName
202 |
203 | const MenubarShortcut = ({
204 | className,
205 | ...props
206 | }: React.HTMLAttributes) => {
207 | return (
208 |
215 | )
216 | }
217 | MenubarShortcut.displayname = "MenubarShortcut"
218 |
219 | export {
220 | Menubar,
221 | MenubarMenu,
222 | MenubarTrigger,
223 | MenubarContent,
224 | MenubarItem,
225 | MenubarSeparator,
226 | MenubarLabel,
227 | MenubarCheckboxItem,
228 | MenubarRadioGroup,
229 | MenubarRadioItem,
230 | MenubarPortal,
231 | MenubarSubContent,
232 | MenubarSubTrigger,
233 | MenubarGroup,
234 | MenubarSub,
235 | MenubarShortcut,
236 | }
237 |
--------------------------------------------------------------------------------
/components/ui/select.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SelectPrimitive from "@radix-ui/react-select"
5 | import { Check, ChevronDown, ChevronUp } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Select = SelectPrimitive.Root
10 |
11 | const SelectGroup = SelectPrimitive.Group
12 |
13 | const SelectValue = SelectPrimitive.Value
14 |
15 | const SelectTrigger = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, children, ...props }, ref) => (
19 | span]:line-clamp-1",
23 | className
24 | )}
25 | {...props}
26 | >
27 | {children}
28 |
29 |
30 |
31 |
32 | ))
33 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
34 |
35 | const SelectScrollUpButton = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 |
48 |
49 | ))
50 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
51 |
52 | const SelectScrollDownButton = React.forwardRef<
53 | React.ElementRef,
54 | React.ComponentPropsWithoutRef
55 | >(({ className, ...props }, ref) => (
56 |
64 |
65 |
66 | ))
67 | SelectScrollDownButton.displayName =
68 | SelectPrimitive.ScrollDownButton.displayName
69 |
70 | const SelectContent = React.forwardRef<
71 | React.ElementRef,
72 | React.ComponentPropsWithoutRef
73 | >(({ className, children, position = "popper", ...props }, ref) => (
74 |
75 |
86 |
87 |
94 | {children}
95 |
96 |
97 |
98 |
99 | ))
100 | SelectContent.displayName = SelectPrimitive.Content.displayName
101 |
102 | const SelectLabel = React.forwardRef<
103 | React.ElementRef,
104 | React.ComponentPropsWithoutRef
105 | >(({ className, ...props }, ref) => (
106 |
111 | ))
112 | SelectLabel.displayName = SelectPrimitive.Label.displayName
113 |
114 | const SelectItem = React.forwardRef<
115 | React.ElementRef,
116 | React.ComponentPropsWithoutRef
117 | >(({ className, children, ...props }, ref) => (
118 |
126 |
127 |
128 |
129 |
130 |
131 |
132 | {children}
133 |
134 | ))
135 | SelectItem.displayName = SelectPrimitive.Item.displayName
136 |
137 | const SelectSeparator = React.forwardRef<
138 | React.ElementRef,
139 | React.ComponentPropsWithoutRef
140 | >(({ className, ...props }, ref) => (
141 |
146 | ))
147 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName
148 |
149 | export {
150 | Select,
151 | SelectGroup,
152 | SelectValue,
153 | SelectTrigger,
154 | SelectContent,
155 | SelectLabel,
156 | SelectItem,
157 | SelectSeparator,
158 | SelectScrollUpButton,
159 | SelectScrollDownButton,
160 | }
161 |
--------------------------------------------------------------------------------
/data/install.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE users (
2 | id SERIAL PRIMARY KEY,
3 | email VARCHAR(255) UNIQUE NOT NULL,
4 | nickname VARCHAR(50),
5 | avatar_url VARCHAR(255),
6 | created_at timestamptz,
7 | uuid UUID UNIQUE NOT NULL
8 | );
9 |
10 | CREATE TABLE videos (
11 | id SERIAL PRIMARY KEY,
12 | user_uuid VARCHAR(255) NOT NULL,
13 | video_description TEXT,
14 | video_url VARCHAR(255),
15 | cover_url VARCHAR(255),
16 | post_url VARCHAR(255),
17 | user_nickname VARCHAR(50),
18 | user_avatar_url VARCHAR(255),
19 | created_at timestamptz,
20 | uuid UUID UNIQUE NOT NULL,
21 | status INT,
22 | is_recommended BOOLEAN
23 | );
--------------------------------------------------------------------------------
/debug/apitest.http:
--------------------------------------------------------------------------------
1 | @baseUri=http://127.0.0.1:8014/api
2 |
3 | ### gen video
4 | POST {{baseUri}}/gen-video
5 | Content-Type: application/json
6 |
7 | {
8 | "description": "spider man fighting with iron man"
9 | }
10 |
11 | ### update videos
12 | POST {{baseUri}}/process/update-videos
13 | Content-Type: application/json
--------------------------------------------------------------------------------
/deploy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18-alpine AS base
2 |
3 | # Install dependencies only when needed
4 | FROM base AS deps
5 | # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
6 | RUN apk add --no-cache libc6-compat && yarn global add pnpm
7 |
8 | WORKDIR /app
9 |
10 | # Install dependencies based on the preferred package manager
11 | COPY package.json pnpm-lock.yaml* ./
12 | RUN pnpm i --frozen-lockfile
13 |
14 | # Rebuild the source code only when needed
15 | FROM deps AS builder
16 |
17 | WORKDIR /app
18 |
19 | # Install dependencies based on the preferred package manager
20 | COPY . .
21 | RUN pnpm build
22 |
23 | # Production image, copy all the files and run next
24 | FROM base AS runner
25 | WORKDIR /app
26 |
27 | RUN addgroup --system --gid 1001 nodejs && \
28 | adduser --system --uid 1001 nextjs && \
29 | mkdir .next && \
30 | chown nextjs:nodejs .next
31 |
32 | COPY --from=builder /app/public ./public
33 | COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
34 | COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
35 |
36 | USER nextjs
37 |
38 | EXPOSE 8080
39 |
40 | ENV NODE_ENV production
41 |
42 | ENV PORT 8080
43 | ENV HOSTNAME "0.0.0.0"
44 |
45 | # server.js is created by next build from the standalone output
46 | # https://nextjs.org/docs/pages/api-reference/next-config-js/output
47 | CMD ["node", "server.js"]
--------------------------------------------------------------------------------
/deploy/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 |
4 | location / {
5 | proxy_pass http://127.0.0.1:8014/;
6 | proxy_set_header Host $http_host;
7 | proxy_set_header X-Real-IP $remote_addr;
8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
9 | proxy_set_header X-Forwarded-Proto $scheme;
10 | }
11 |
12 | error_log /var/log/nginx/sorafm.error;
13 | }
--------------------------------------------------------------------------------
/dictionaries/de.json:
--------------------------------------------------------------------------------
1 | {
2 | "brand": {
3 | "name": "Sora.FM",
4 | "title": "Sora AI Video Generator",
5 | "sub_title": "Erstelle AI Videos mit Sora, entfessele deine Kreativität."
6 | },
7 | "subscribe": {
8 | "placeholder": "Gib deine E-Mail-Adresse ein",
9 | "button": "Abonnieren",
10 | "tip": "Die Sora Text-zu-Video API ist derzeit nicht verfügbar, wir werden dich informieren, sobald sie live geht."
11 | },
12 | "showcase": {
13 | "title": "Sora AI Video Showcase",
14 | "sub_title": "Dieses Video wurde mit dem Sora Text-zu-Video Modell erstellt",
15 | "more_tip": "Mehr AI Video Showcases"
16 | },
17 | "tab": {
18 | "latest": "Neueste",
19 | "featured": "Empfohlen",
20 | "random": "Zufällig"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/dictionaries/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "brand": {
3 | "name": "Sora.FM",
4 | "title": "Sora AI Video Generator",
5 | "sub_title": "Make AI Video with Sora, Unleash your creativity."
6 | },
7 | "subscribe": {
8 | "placeholder": "Enter your email",
9 | "button": "Subscribe",
10 | "tip": "Sora text-to-video API is not available, we'll get you notified when it's live."
11 | },
12 | "showcase": {
13 | "title": "Sora AI Video Showcase",
14 | "sub_title": "This video is made with Sora text-to-video model",
15 | "more_tip": "More AI Video Showcases"
16 | },
17 | "tab": {
18 | "latest": "Latest",
19 | "featured": "Featured",
20 | "random": "Random"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/dictionaries/fr.json:
--------------------------------------------------------------------------------
1 | {
2 | "brand": {
3 | "name": "Sora.FM",
4 | "title": "Générateur de vidéos Sora AI",
5 | "sub_title": "Créez des vidéos AI avec Sora"
6 | },
7 | "subscribe": {
8 | "placeholder": "Entrez votre email",
9 | "button": "S'abonner",
10 | "tip": "L'API de texte en vidéo Sora n'est pas disponible, nous vous informerons lorsqu'elle sera en ligne."
11 | },
12 | "showcase": {
13 | "title": "Vitrine vidéo Sora AI",
14 | "sub_title": "Cette vidéo a été réalisée avec Sora, par l'équipe rouge d'OpenAI.",
15 | "more_tip": "Plus de vitrines vidéo AI"
16 | },
17 | "tab": {
18 | "latest": "Dernier",
19 | "featured": "En vedette",
20 | "random": "Aléatoire"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/dictionaries/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "brand": {
3 | "name": "Sora.FM",
4 | "title": "Sora AI ビデオジェネレーター",
5 | "sub_title": "Soraを使用してAIビデオを生成する"
6 | },
7 | "subscribe": {
8 | "placeholder": "メールアドレスを入力してください",
9 | "button": "通知を購読する",
10 | "tip": "SoraテキストからビデオへのAPIは利用できませんが、公開され次第お知らせします。"
11 | },
12 | "showcase": {
13 | "title": "Sora AI ビデオショーケース",
14 | "sub_title": "このビデオはOpenAIのレッドチームによってSoraを使って作成されました。",
15 | "more_tip": "さらにAIビデオショーケース"
16 | },
17 | "tab": {
18 | "latest": "最新",
19 | "featured": "注目",
20 | "random": "ランダム"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/dictionaries/ko.json:
--------------------------------------------------------------------------------
1 | {
2 | "brand": {
3 | "name": "Sora.FM",
4 | "title": "Sora AI 비디오 생성기",
5 | "sub_title": "Sora 를 사용하여 AI 비디오를 제작하다"
6 | },
7 | "subscribe": {
8 | "placeholder": "이메일을 입력하세요",
9 | "button": "알림 구독하기",
10 | "tip": "Sora 텍스트에서 비디오로의 API는 사용할 수 없습니다. 서비스가 시작되면 알려드리겠습니다."
11 | },
12 | "showcase": {
13 | "title": "Sora AI 비디오 쇼케이스",
14 | "sub_title": "T이 비디오는 OpenAI의 레드 팀이 Sora를 사용하여 만들었습니다.",
15 | "more_tip": "더 많은 AI 비디오 쇼케이스"
16 | },
17 | "tab": {
18 | "latest": "최신",
19 | "featured": "특집",
20 | "random": "무작위"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/dictionaries/zh.json:
--------------------------------------------------------------------------------
1 | {
2 | "brand": {
3 | "name": "Sora.FM",
4 | "title": "Sora AI 视频生成器",
5 | "sub_title": "使用 Sora 生成 AI 视频,释放你的创造力"
6 | },
7 | "subscribe": {
8 | "placeholder": "输入你的邮箱",
9 | "button": "订阅通知",
10 | "tip": "Sora 文生视频功能尚未发布,等可用后,我们会第一时间通知你"
11 | },
12 | "showcase": {
13 | "title": "Sora AI 视频演示",
14 | "sub_title": "这个视频由 Sora 生成",
15 | "more_tip": "更多由 Sora 生成的视频"
16 | },
17 | "tab": {
18 | "latest": "最新",
19 | "featured": "最热",
20 | "random": "随机"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/index.ts:
--------------------------------------------------------------------------------
1 | import { v4 as uuidv4 } from "uuid";
2 |
3 | export function genUuid(): string {
4 | return uuidv4();
5 | }
6 |
--------------------------------------------------------------------------------
/lib/resp.ts:
--------------------------------------------------------------------------------
1 | export function respData(data: any) {
2 | return respJson(0, "ok", data || []);
3 | }
4 |
5 | export function respOk() {
6 | return respJson(0, "ok");
7 | }
8 |
9 | export function respErr(message: string) {
10 | return respJson(-1, message);
11 | }
12 |
13 | export function respJson(code: number, message: string, data?: any) {
14 | let json = {
15 | code: code,
16 | message: message,
17 | data: data,
18 | };
19 | if (data) {
20 | json["data"] = data;
21 | }
22 |
23 | return Response.json(json);
24 | }
25 |
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/middleware.ts:
--------------------------------------------------------------------------------
1 | import { defaultLocale, getLocale, locales } from "./services/i18n";
2 |
3 | import { NextRequest } from "next/server";
4 |
5 | export function middleware(request: NextRequest) {
6 | const { pathname } = request.nextUrl;
7 | if (pathname === "/favicon.ico" || pathname.startsWith("/api/")) {
8 | return;
9 | }
10 |
11 | const pathnameHasLocale = locales.some(
12 | (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
13 | );
14 |
15 | if (pathnameHasLocale) return;
16 |
17 | const locale = getLocale({
18 | "accept-language": request.headers.get("accept-language"),
19 | });
20 |
21 | request.nextUrl.pathname = `/${locale}${pathname}`;
22 |
23 | return Response.redirect(request.nextUrl);
24 | }
25 |
26 | export const config = {
27 | matcher: ["/((?!_next).*)"],
28 | };
29 |
--------------------------------------------------------------------------------
/models/db.ts:
--------------------------------------------------------------------------------
1 | import { Pool } from "pg";
2 |
3 | let globalPool: Pool;
4 |
5 | export function getDb() {
6 | if (!globalPool) {
7 | const connectionString = process.env.POSTGRES_URL;
8 | console.log("connectionString", connectionString);
9 |
10 | globalPool = new Pool({
11 | connectionString,
12 | });
13 | }
14 |
15 | return globalPool;
16 | }
17 |
--------------------------------------------------------------------------------
/models/user.ts:
--------------------------------------------------------------------------------
1 | import { User } from "@/types/user";
2 | import { getDb } from "@/models/db";
3 |
4 | export async function insertUser(user: User) {
5 | const createdAt: string = new Date().toISOString();
6 |
7 | const db = await getDb();
8 | const res = await db.query(
9 | `INSERT INTO users
10 | (email, nickname, avatar_url, created_at, uuid)
11 | VALUES
12 | ($1, $2, $3, $4, $5)
13 | `,
14 | [user.email, user.nickname, user.avatar_url, createdAt, user.uuid]
15 | );
16 |
17 | return res;
18 | }
19 |
20 | export async function findUserByEmail(
21 | email: string
22 | ): Promise {
23 | const db = getDb();
24 | const res = await db.query(`SELECT * FROM users WHERE email = $1 LIMIT 1`, [
25 | email,
26 | ]);
27 | if (res.rowCount === 0) {
28 | return undefined;
29 | }
30 |
31 | const { rows } = res;
32 | const row = rows[0];
33 | const user: User = {
34 | email: row.email,
35 | nickname: row.nickname,
36 | avatar_url: row.avatar_url,
37 | created_at: row.created_at,
38 | uuid: row.uuid,
39 | };
40 |
41 | return user;
42 | }
43 |
44 | export async function findUserByUuid(uuid: string): Promise {
45 | const db = getDb();
46 | const res = await db.query(`SELECT * FROM users WHERE uuid = $1 LIMIT 1`, [
47 | uuid,
48 | ]);
49 | if (res.rowCount === 0) {
50 | return undefined;
51 | }
52 |
53 | const { rows } = res;
54 | const row = rows[0];
55 | const user: User = {
56 | email: row.email,
57 | nickname: row.nickname,
58 | avatar_url: row.avatar_url,
59 | created_at: row.created_at,
60 | uuid: row.uuid,
61 | };
62 |
63 | return user;
64 | }
65 |
--------------------------------------------------------------------------------
/models/video.ts:
--------------------------------------------------------------------------------
1 | import { QueryResult, QueryResultRow } from "pg";
2 |
3 | import { Video } from "@/types/video";
4 | import { getDb } from "./db";
5 |
6 | export async function insertVideo(video: Video) {
7 | const db = getDb();
8 | const res = await db.query(
9 | `INSERT INTO videos
10 | (user_uuid, video_description, video_url, cover_url, post_url, user_nickname, user_avatar_url, created_at, uuid, status)
11 | VALUES
12 | ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
13 | `,
14 | [
15 | video.user_uuid,
16 | video.video_description,
17 | video.video_url,
18 | video.cover_url,
19 | video.post_url,
20 | video.user_nickname,
21 | video.user_avatar_url,
22 | video.created_at,
23 | video.uuid,
24 | video.status,
25 | ]
26 | );
27 |
28 | return res;
29 | }
30 |
31 | export async function getVideosCount(): Promise {
32 | const db = getDb();
33 | const res = await db.query(`SELECT count(1) as count FROM videos`);
34 | if (res.rowCount === 0) {
35 | return 0;
36 | }
37 |
38 | const { rows } = res;
39 | const row = rows[0];
40 |
41 | return row.count;
42 | }
43 |
44 | export async function getUserVideosCount(user_uuid: string): Promise {
45 | const db = getDb();
46 | const res = await db.query(
47 | `SELECT count(1) as count FROM videos WHERE user_uuid = $1`,
48 | [user_uuid]
49 | );
50 | if (res.rowCount === 0) {
51 | return 0;
52 | }
53 |
54 | const { rows } = res;
55 | const row = rows[0];
56 |
57 | return row.count;
58 | }
59 |
60 | export async function findVideoByUuid(
61 | uuid: string
62 | ): Promise {
63 | const db = getDb();
64 | const res = await db.query(
65 | `select w.*, u.uuid as user_uuid, u.email as user_email, u.nickname as user_name, u.avatar_url as user_avatar from videos as w left join users as u on w.user_uuid = u.uuid::VARCHAR where w.uuid = $1 and w.status = 1`,
66 | [uuid]
67 | );
68 | if (res.rowCount === 0) {
69 | return;
70 | }
71 |
72 | const video = formatVideo(res.rows[0]);
73 |
74 | return video;
75 | }
76 |
77 | export async function getRandomVideos(
78 | page: number,
79 | limit: number
80 | ): Promise {
81 | try {
82 | if (page <= 0) {
83 | page = 1;
84 | }
85 | if (limit <= 0) {
86 | limit = 50;
87 | }
88 | const offset = (page - 1) * limit;
89 |
90 | const db = getDb();
91 | const res = await db.query(
92 | `select w.*, u.uuid as user_uuid, u.email as user_email, u.nickname as user_name, u.avatar_url as user_avatar from videos as w left join users as u on w.user_uuid = u.uuid::VARCHAR where w.status = 1 order by random() limit $1 offset $2`,
93 | [limit, offset]
94 | );
95 |
96 | if (res.rowCount === 0) {
97 | return [];
98 | }
99 |
100 | const videos = getVideosFromSqlResult(res);
101 |
102 | return videos;
103 | } catch (error) {
104 | console.log("get random videos error", error);
105 | return [];
106 | }
107 | }
108 |
109 | export async function getLatestVideos(
110 | page: number,
111 | limit: number
112 | ): Promise {
113 | try {
114 | if (page < 1) {
115 | page = 1;
116 | }
117 | if (limit <= 0) {
118 | limit = 50;
119 | }
120 | const offset = (page - 1) * limit;
121 |
122 | const db = getDb();
123 | const res = await db.query(
124 | `select w.*, u.uuid as user_uuid, u.email as user_email, u.nickname as user_name, u.avatar_url as user_avatar from videos as w left join users as u on w.user_uuid = u.uuid::VARCHAR where w.status = 1 order by w.created_at desc limit $1 offset $2`,
125 | [limit, offset]
126 | );
127 | if (res.rowCount === 0) {
128 | return [];
129 | }
130 |
131 | const videos = getVideosFromSqlResult(res);
132 |
133 | return videos;
134 | } catch (error) {
135 | console.log("get latest videos error", error);
136 | return [];
137 | }
138 | }
139 |
140 | export async function getRecommendedVideos(
141 | page: number,
142 | limit: number
143 | ): Promise {
144 | if (page < 1) {
145 | page = 1;
146 | }
147 | if (limit <= 0) {
148 | limit = 50;
149 | }
150 | const offset = (page - 1) * limit;
151 |
152 | const db = getDb();
153 | const res = await db.query(
154 | `select w.*, u.uuid as user_uuid, u.email as user_email, u.nickname as user_name, u.avatar_url as user_avatar from videos as w left join users as u on w.user_uuid = u.uuid::VARCHAR where is_recommended = true and w.status = 1 order by w.created_at desc limit $1 offset $2`,
155 | [limit, offset]
156 | );
157 | if (res.rowCount === 0) {
158 | return [];
159 | }
160 |
161 | const videos = getVideosFromSqlResult(res);
162 |
163 | return videos;
164 | }
165 |
166 | export function getVideosFromSqlResult(
167 | res: QueryResult
168 | ): Video[] {
169 | if (!res.rowCount || res.rowCount === 0) {
170 | return [];
171 | }
172 |
173 | const videos: Video[] = [];
174 | const { rows } = res;
175 | rows.forEach((row) => {
176 | const video = formatVideo(row);
177 | if (video) {
178 | videos.push(video);
179 | }
180 | });
181 |
182 | return videos;
183 | }
184 |
185 | export function formatVideo(row: QueryResultRow): Video | undefined {
186 | let video: Video = {
187 | user_uuid: row.user_uuid,
188 | video_description: row.video_description,
189 | video_url: row.video_url,
190 | cover_url: row.cover_url,
191 | post_url: row.post_url,
192 | user_nickname: row.user_nickname,
193 | user_avatar_url: row.user_avatar_url,
194 | created_at: row.created_at,
195 | uuid: row.uuid,
196 | status: row.status,
197 | };
198 |
199 | return video;
200 | }
201 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | output: "standalone",
4 | };
5 |
6 | export default nextConfig;
7 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sorafm",
3 | "version": "0.1.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "sorafm",
9 | "version": "0.1.0",
10 | "dependencies": {
11 | "class-variance-authority": "^0.7.0",
12 | "clsx": "^2.1.0",
13 | "lucide-react": "^0.331.0",
14 | "next": "14.1.0",
15 | "react": "^18",
16 | "react-dom": "^18",
17 | "tailwind-merge": "^2.2.1",
18 | "tailwindcss-animate": "^1.0.7"
19 | },
20 | "devDependencies": {
21 | "@types/node": "^20",
22 | "@types/react": "^18",
23 | "@types/react-dom": "^18",
24 | "autoprefixer": "^10.0.1",
25 | "postcss": "^8",
26 | "tailwindcss": "^3.3.0",
27 | "typescript": "^5"
28 | }
29 | },
30 | "node_modules/@alloc/quick-lru": {
31 | "version": "5.2.0",
32 | "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
33 | "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
34 | "engines": {
35 | "node": ">=10"
36 | },
37 | "funding": {
38 | "url": "https://github.com/sponsors/sindresorhus"
39 | }
40 | },
41 | "node_modules/@babel/runtime": {
42 | "version": "7.23.9",
43 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
44 | "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
45 | "dependencies": {
46 | "regenerator-runtime": "^0.14.0"
47 | },
48 | "engines": {
49 | "node": ">=6.9.0"
50 | }
51 | },
52 | "node_modules/@isaacs/cliui": {
53 | "version": "8.0.2",
54 | "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
55 | "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
56 | "dependencies": {
57 | "string-width": "^5.1.2",
58 | "string-width-cjs": "npm:string-width@^4.2.0",
59 | "strip-ansi": "^7.0.1",
60 | "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
61 | "wrap-ansi": "^8.1.0",
62 | "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
63 | },
64 | "engines": {
65 | "node": ">=12"
66 | }
67 | },
68 | "node_modules/@jridgewell/gen-mapping": {
69 | "version": "0.3.3",
70 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
71 | "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
72 | "dependencies": {
73 | "@jridgewell/set-array": "^1.0.1",
74 | "@jridgewell/sourcemap-codec": "^1.4.10",
75 | "@jridgewell/trace-mapping": "^0.3.9"
76 | },
77 | "engines": {
78 | "node": ">=6.0.0"
79 | }
80 | },
81 | "node_modules/@jridgewell/resolve-uri": {
82 | "version": "3.1.2",
83 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
84 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
85 | "engines": {
86 | "node": ">=6.0.0"
87 | }
88 | },
89 | "node_modules/@jridgewell/set-array": {
90 | "version": "1.1.2",
91 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
92 | "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
93 | "engines": {
94 | "node": ">=6.0.0"
95 | }
96 | },
97 | "node_modules/@jridgewell/sourcemap-codec": {
98 | "version": "1.4.15",
99 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
100 | "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg=="
101 | },
102 | "node_modules/@jridgewell/trace-mapping": {
103 | "version": "0.3.22",
104 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz",
105 | "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==",
106 | "dependencies": {
107 | "@jridgewell/resolve-uri": "^3.1.0",
108 | "@jridgewell/sourcemap-codec": "^1.4.14"
109 | }
110 | },
111 | "node_modules/@next/env": {
112 | "version": "14.1.0",
113 | "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz",
114 | "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw=="
115 | },
116 | "node_modules/@next/swc-darwin-arm64": {
117 | "version": "14.1.0",
118 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz",
119 | "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==",
120 | "cpu": [
121 | "arm64"
122 | ],
123 | "optional": true,
124 | "os": [
125 | "darwin"
126 | ],
127 | "engines": {
128 | "node": ">= 10"
129 | }
130 | },
131 | "node_modules/@next/swc-darwin-x64": {
132 | "version": "14.1.0",
133 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz",
134 | "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==",
135 | "cpu": [
136 | "x64"
137 | ],
138 | "optional": true,
139 | "os": [
140 | "darwin"
141 | ],
142 | "engines": {
143 | "node": ">= 10"
144 | }
145 | },
146 | "node_modules/@next/swc-linux-arm64-gnu": {
147 | "version": "14.1.0",
148 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz",
149 | "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==",
150 | "cpu": [
151 | "arm64"
152 | ],
153 | "optional": true,
154 | "os": [
155 | "linux"
156 | ],
157 | "engines": {
158 | "node": ">= 10"
159 | }
160 | },
161 | "node_modules/@next/swc-linux-arm64-musl": {
162 | "version": "14.1.0",
163 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz",
164 | "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==",
165 | "cpu": [
166 | "arm64"
167 | ],
168 | "optional": true,
169 | "os": [
170 | "linux"
171 | ],
172 | "engines": {
173 | "node": ">= 10"
174 | }
175 | },
176 | "node_modules/@next/swc-linux-x64-gnu": {
177 | "version": "14.1.0",
178 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz",
179 | "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==",
180 | "cpu": [
181 | "x64"
182 | ],
183 | "optional": true,
184 | "os": [
185 | "linux"
186 | ],
187 | "engines": {
188 | "node": ">= 10"
189 | }
190 | },
191 | "node_modules/@next/swc-linux-x64-musl": {
192 | "version": "14.1.0",
193 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz",
194 | "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==",
195 | "cpu": [
196 | "x64"
197 | ],
198 | "optional": true,
199 | "os": [
200 | "linux"
201 | ],
202 | "engines": {
203 | "node": ">= 10"
204 | }
205 | },
206 | "node_modules/@next/swc-win32-arm64-msvc": {
207 | "version": "14.1.0",
208 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz",
209 | "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==",
210 | "cpu": [
211 | "arm64"
212 | ],
213 | "optional": true,
214 | "os": [
215 | "win32"
216 | ],
217 | "engines": {
218 | "node": ">= 10"
219 | }
220 | },
221 | "node_modules/@next/swc-win32-ia32-msvc": {
222 | "version": "14.1.0",
223 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz",
224 | "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==",
225 | "cpu": [
226 | "ia32"
227 | ],
228 | "optional": true,
229 | "os": [
230 | "win32"
231 | ],
232 | "engines": {
233 | "node": ">= 10"
234 | }
235 | },
236 | "node_modules/@next/swc-win32-x64-msvc": {
237 | "version": "14.1.0",
238 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz",
239 | "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==",
240 | "cpu": [
241 | "x64"
242 | ],
243 | "optional": true,
244 | "os": [
245 | "win32"
246 | ],
247 | "engines": {
248 | "node": ">= 10"
249 | }
250 | },
251 | "node_modules/@nodelib/fs.scandir": {
252 | "version": "2.1.5",
253 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
254 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
255 | "dependencies": {
256 | "@nodelib/fs.stat": "2.0.5",
257 | "run-parallel": "^1.1.9"
258 | },
259 | "engines": {
260 | "node": ">= 8"
261 | }
262 | },
263 | "node_modules/@nodelib/fs.stat": {
264 | "version": "2.0.5",
265 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
266 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
267 | "engines": {
268 | "node": ">= 8"
269 | }
270 | },
271 | "node_modules/@nodelib/fs.walk": {
272 | "version": "1.2.8",
273 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
274 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
275 | "dependencies": {
276 | "@nodelib/fs.scandir": "2.1.5",
277 | "fastq": "^1.6.0"
278 | },
279 | "engines": {
280 | "node": ">= 8"
281 | }
282 | },
283 | "node_modules/@pkgjs/parseargs": {
284 | "version": "0.11.0",
285 | "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
286 | "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
287 | "optional": true,
288 | "engines": {
289 | "node": ">=14"
290 | }
291 | },
292 | "node_modules/@swc/helpers": {
293 | "version": "0.5.2",
294 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
295 | "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
296 | "dependencies": {
297 | "tslib": "^2.4.0"
298 | }
299 | },
300 | "node_modules/@types/node": {
301 | "version": "20.11.19",
302 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",
303 | "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==",
304 | "dev": true,
305 | "dependencies": {
306 | "undici-types": "~5.26.4"
307 | }
308 | },
309 | "node_modules/@types/prop-types": {
310 | "version": "15.7.11",
311 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
312 | "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
313 | "dev": true
314 | },
315 | "node_modules/@types/react": {
316 | "version": "18.2.55",
317 | "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.55.tgz",
318 | "integrity": "sha512-Y2Tz5P4yz23brwm2d7jNon39qoAtMMmalOQv6+fEFt1mT+FcM3D841wDpoUvFXhaYenuROCy3FZYqdTjM7qVyA==",
319 | "dev": true,
320 | "dependencies": {
321 | "@types/prop-types": "*",
322 | "@types/scheduler": "*",
323 | "csstype": "^3.0.2"
324 | }
325 | },
326 | "node_modules/@types/react-dom": {
327 | "version": "18.2.19",
328 | "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.19.tgz",
329 | "integrity": "sha512-aZvQL6uUbIJpjZk4U8JZGbau9KDeAwMfmhyWorxgBkqDIEf6ROjRozcmPIicqsUwPUjbkDfHKgGee1Lq65APcA==",
330 | "dev": true,
331 | "dependencies": {
332 | "@types/react": "*"
333 | }
334 | },
335 | "node_modules/@types/scheduler": {
336 | "version": "0.16.8",
337 | "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
338 | "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
339 | "dev": true
340 | },
341 | "node_modules/ansi-regex": {
342 | "version": "6.0.1",
343 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
344 | "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
345 | "engines": {
346 | "node": ">=12"
347 | },
348 | "funding": {
349 | "url": "https://github.com/chalk/ansi-regex?sponsor=1"
350 | }
351 | },
352 | "node_modules/ansi-styles": {
353 | "version": "6.2.1",
354 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
355 | "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
356 | "engines": {
357 | "node": ">=12"
358 | },
359 | "funding": {
360 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
361 | }
362 | },
363 | "node_modules/any-promise": {
364 | "version": "1.3.0",
365 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
366 | "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
367 | },
368 | "node_modules/anymatch": {
369 | "version": "3.1.3",
370 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
371 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
372 | "dependencies": {
373 | "normalize-path": "^3.0.0",
374 | "picomatch": "^2.0.4"
375 | },
376 | "engines": {
377 | "node": ">= 8"
378 | }
379 | },
380 | "node_modules/arg": {
381 | "version": "5.0.2",
382 | "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
383 | "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
384 | },
385 | "node_modules/autoprefixer": {
386 | "version": "10.4.17",
387 | "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz",
388 | "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==",
389 | "dev": true,
390 | "funding": [
391 | {
392 | "type": "opencollective",
393 | "url": "https://opencollective.com/postcss/"
394 | },
395 | {
396 | "type": "tidelift",
397 | "url": "https://tidelift.com/funding/github/npm/autoprefixer"
398 | },
399 | {
400 | "type": "github",
401 | "url": "https://github.com/sponsors/ai"
402 | }
403 | ],
404 | "dependencies": {
405 | "browserslist": "^4.22.2",
406 | "caniuse-lite": "^1.0.30001578",
407 | "fraction.js": "^4.3.7",
408 | "normalize-range": "^0.1.2",
409 | "picocolors": "^1.0.0",
410 | "postcss-value-parser": "^4.2.0"
411 | },
412 | "bin": {
413 | "autoprefixer": "bin/autoprefixer"
414 | },
415 | "engines": {
416 | "node": "^10 || ^12 || >=14"
417 | },
418 | "peerDependencies": {
419 | "postcss": "^8.1.0"
420 | }
421 | },
422 | "node_modules/balanced-match": {
423 | "version": "1.0.2",
424 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
425 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
426 | },
427 | "node_modules/binary-extensions": {
428 | "version": "2.2.0",
429 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
430 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
431 | "engines": {
432 | "node": ">=8"
433 | }
434 | },
435 | "node_modules/brace-expansion": {
436 | "version": "2.0.1",
437 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
438 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
439 | "dependencies": {
440 | "balanced-match": "^1.0.0"
441 | }
442 | },
443 | "node_modules/braces": {
444 | "version": "3.0.2",
445 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
446 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
447 | "dependencies": {
448 | "fill-range": "^7.0.1"
449 | },
450 | "engines": {
451 | "node": ">=8"
452 | }
453 | },
454 | "node_modules/browserslist": {
455 | "version": "4.23.0",
456 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
457 | "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
458 | "dev": true,
459 | "funding": [
460 | {
461 | "type": "opencollective",
462 | "url": "https://opencollective.com/browserslist"
463 | },
464 | {
465 | "type": "tidelift",
466 | "url": "https://tidelift.com/funding/github/npm/browserslist"
467 | },
468 | {
469 | "type": "github",
470 | "url": "https://github.com/sponsors/ai"
471 | }
472 | ],
473 | "dependencies": {
474 | "caniuse-lite": "^1.0.30001587",
475 | "electron-to-chromium": "^1.4.668",
476 | "node-releases": "^2.0.14",
477 | "update-browserslist-db": "^1.0.13"
478 | },
479 | "bin": {
480 | "browserslist": "cli.js"
481 | },
482 | "engines": {
483 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
484 | }
485 | },
486 | "node_modules/busboy": {
487 | "version": "1.6.0",
488 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
489 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
490 | "dependencies": {
491 | "streamsearch": "^1.1.0"
492 | },
493 | "engines": {
494 | "node": ">=10.16.0"
495 | }
496 | },
497 | "node_modules/camelcase-css": {
498 | "version": "2.0.1",
499 | "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
500 | "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
501 | "engines": {
502 | "node": ">= 6"
503 | }
504 | },
505 | "node_modules/caniuse-lite": {
506 | "version": "1.0.30001587",
507 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001587.tgz",
508 | "integrity": "sha512-HMFNotUmLXn71BQxg8cijvqxnIAofforZOwGsxyXJ0qugTdspUF4sPSJ2vhgprHCB996tIDzEq1ubumPDV8ULA==",
509 | "funding": [
510 | {
511 | "type": "opencollective",
512 | "url": "https://opencollective.com/browserslist"
513 | },
514 | {
515 | "type": "tidelift",
516 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
517 | },
518 | {
519 | "type": "github",
520 | "url": "https://github.com/sponsors/ai"
521 | }
522 | ]
523 | },
524 | "node_modules/chokidar": {
525 | "version": "3.6.0",
526 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
527 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
528 | "dependencies": {
529 | "anymatch": "~3.1.2",
530 | "braces": "~3.0.2",
531 | "glob-parent": "~5.1.2",
532 | "is-binary-path": "~2.1.0",
533 | "is-glob": "~4.0.1",
534 | "normalize-path": "~3.0.0",
535 | "readdirp": "~3.6.0"
536 | },
537 | "engines": {
538 | "node": ">= 8.10.0"
539 | },
540 | "funding": {
541 | "url": "https://paulmillr.com/funding/"
542 | },
543 | "optionalDependencies": {
544 | "fsevents": "~2.3.2"
545 | }
546 | },
547 | "node_modules/chokidar/node_modules/glob-parent": {
548 | "version": "5.1.2",
549 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
550 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
551 | "dependencies": {
552 | "is-glob": "^4.0.1"
553 | },
554 | "engines": {
555 | "node": ">= 6"
556 | }
557 | },
558 | "node_modules/class-variance-authority": {
559 | "version": "0.7.0",
560 | "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz",
561 | "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==",
562 | "dependencies": {
563 | "clsx": "2.0.0"
564 | },
565 | "funding": {
566 | "url": "https://joebell.co.uk"
567 | }
568 | },
569 | "node_modules/class-variance-authority/node_modules/clsx": {
570 | "version": "2.0.0",
571 | "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
572 | "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
573 | "engines": {
574 | "node": ">=6"
575 | }
576 | },
577 | "node_modules/client-only": {
578 | "version": "0.0.1",
579 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
580 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
581 | },
582 | "node_modules/clsx": {
583 | "version": "2.1.0",
584 | "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
585 | "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
586 | "engines": {
587 | "node": ">=6"
588 | }
589 | },
590 | "node_modules/color-convert": {
591 | "version": "2.0.1",
592 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
593 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
594 | "dependencies": {
595 | "color-name": "~1.1.4"
596 | },
597 | "engines": {
598 | "node": ">=7.0.0"
599 | }
600 | },
601 | "node_modules/color-name": {
602 | "version": "1.1.4",
603 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
604 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
605 | },
606 | "node_modules/commander": {
607 | "version": "4.1.1",
608 | "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
609 | "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
610 | "engines": {
611 | "node": ">= 6"
612 | }
613 | },
614 | "node_modules/cross-spawn": {
615 | "version": "7.0.3",
616 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
617 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
618 | "dependencies": {
619 | "path-key": "^3.1.0",
620 | "shebang-command": "^2.0.0",
621 | "which": "^2.0.1"
622 | },
623 | "engines": {
624 | "node": ">= 8"
625 | }
626 | },
627 | "node_modules/cssesc": {
628 | "version": "3.0.0",
629 | "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
630 | "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
631 | "bin": {
632 | "cssesc": "bin/cssesc"
633 | },
634 | "engines": {
635 | "node": ">=4"
636 | }
637 | },
638 | "node_modules/csstype": {
639 | "version": "3.1.3",
640 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
641 | "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
642 | "dev": true
643 | },
644 | "node_modules/didyoumean": {
645 | "version": "1.2.2",
646 | "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
647 | "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
648 | },
649 | "node_modules/dlv": {
650 | "version": "1.1.3",
651 | "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
652 | "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
653 | },
654 | "node_modules/eastasianwidth": {
655 | "version": "0.2.0",
656 | "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
657 | "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
658 | },
659 | "node_modules/electron-to-chromium": {
660 | "version": "1.4.673",
661 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.673.tgz",
662 | "integrity": "sha512-zjqzx4N7xGdl5468G+vcgzDhaHkaYgVcf9MqgexcTqsl2UHSCmOj/Bi3HAprg4BZCpC7HyD8a6nZl6QAZf72gw==",
663 | "dev": true
664 | },
665 | "node_modules/emoji-regex": {
666 | "version": "9.2.2",
667 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
668 | "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
669 | },
670 | "node_modules/escalade": {
671 | "version": "3.1.2",
672 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
673 | "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
674 | "dev": true,
675 | "engines": {
676 | "node": ">=6"
677 | }
678 | },
679 | "node_modules/fast-glob": {
680 | "version": "3.3.2",
681 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
682 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
683 | "dependencies": {
684 | "@nodelib/fs.stat": "^2.0.2",
685 | "@nodelib/fs.walk": "^1.2.3",
686 | "glob-parent": "^5.1.2",
687 | "merge2": "^1.3.0",
688 | "micromatch": "^4.0.4"
689 | },
690 | "engines": {
691 | "node": ">=8.6.0"
692 | }
693 | },
694 | "node_modules/fast-glob/node_modules/glob-parent": {
695 | "version": "5.1.2",
696 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
697 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
698 | "dependencies": {
699 | "is-glob": "^4.0.1"
700 | },
701 | "engines": {
702 | "node": ">= 6"
703 | }
704 | },
705 | "node_modules/fastq": {
706 | "version": "1.17.1",
707 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
708 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
709 | "dependencies": {
710 | "reusify": "^1.0.4"
711 | }
712 | },
713 | "node_modules/fill-range": {
714 | "version": "7.0.1",
715 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
716 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
717 | "dependencies": {
718 | "to-regex-range": "^5.0.1"
719 | },
720 | "engines": {
721 | "node": ">=8"
722 | }
723 | },
724 | "node_modules/foreground-child": {
725 | "version": "3.1.1",
726 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
727 | "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
728 | "dependencies": {
729 | "cross-spawn": "^7.0.0",
730 | "signal-exit": "^4.0.1"
731 | },
732 | "engines": {
733 | "node": ">=14"
734 | },
735 | "funding": {
736 | "url": "https://github.com/sponsors/isaacs"
737 | }
738 | },
739 | "node_modules/fraction.js": {
740 | "version": "4.3.7",
741 | "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
742 | "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
743 | "dev": true,
744 | "engines": {
745 | "node": "*"
746 | },
747 | "funding": {
748 | "type": "patreon",
749 | "url": "https://github.com/sponsors/rawify"
750 | }
751 | },
752 | "node_modules/fsevents": {
753 | "version": "2.3.3",
754 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
755 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
756 | "hasInstallScript": true,
757 | "optional": true,
758 | "os": [
759 | "darwin"
760 | ],
761 | "engines": {
762 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
763 | }
764 | },
765 | "node_modules/function-bind": {
766 | "version": "1.1.2",
767 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
768 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
769 | "funding": {
770 | "url": "https://github.com/sponsors/ljharb"
771 | }
772 | },
773 | "node_modules/glob": {
774 | "version": "10.3.10",
775 | "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
776 | "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
777 | "dependencies": {
778 | "foreground-child": "^3.1.0",
779 | "jackspeak": "^2.3.5",
780 | "minimatch": "^9.0.1",
781 | "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
782 | "path-scurry": "^1.10.1"
783 | },
784 | "bin": {
785 | "glob": "dist/esm/bin.mjs"
786 | },
787 | "engines": {
788 | "node": ">=16 || 14 >=14.17"
789 | },
790 | "funding": {
791 | "url": "https://github.com/sponsors/isaacs"
792 | }
793 | },
794 | "node_modules/glob-parent": {
795 | "version": "6.0.2",
796 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
797 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
798 | "dependencies": {
799 | "is-glob": "^4.0.3"
800 | },
801 | "engines": {
802 | "node": ">=10.13.0"
803 | }
804 | },
805 | "node_modules/graceful-fs": {
806 | "version": "4.2.11",
807 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
808 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
809 | },
810 | "node_modules/hasown": {
811 | "version": "2.0.1",
812 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
813 | "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
814 | "dependencies": {
815 | "function-bind": "^1.1.2"
816 | },
817 | "engines": {
818 | "node": ">= 0.4"
819 | }
820 | },
821 | "node_modules/is-binary-path": {
822 | "version": "2.1.0",
823 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
824 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
825 | "dependencies": {
826 | "binary-extensions": "^2.0.0"
827 | },
828 | "engines": {
829 | "node": ">=8"
830 | }
831 | },
832 | "node_modules/is-core-module": {
833 | "version": "2.13.1",
834 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
835 | "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
836 | "dependencies": {
837 | "hasown": "^2.0.0"
838 | },
839 | "funding": {
840 | "url": "https://github.com/sponsors/ljharb"
841 | }
842 | },
843 | "node_modules/is-extglob": {
844 | "version": "2.1.1",
845 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
846 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
847 | "engines": {
848 | "node": ">=0.10.0"
849 | }
850 | },
851 | "node_modules/is-fullwidth-code-point": {
852 | "version": "3.0.0",
853 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
854 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
855 | "engines": {
856 | "node": ">=8"
857 | }
858 | },
859 | "node_modules/is-glob": {
860 | "version": "4.0.3",
861 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
862 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
863 | "dependencies": {
864 | "is-extglob": "^2.1.1"
865 | },
866 | "engines": {
867 | "node": ">=0.10.0"
868 | }
869 | },
870 | "node_modules/is-number": {
871 | "version": "7.0.0",
872 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
873 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
874 | "engines": {
875 | "node": ">=0.12.0"
876 | }
877 | },
878 | "node_modules/isexe": {
879 | "version": "2.0.0",
880 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
881 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
882 | },
883 | "node_modules/jackspeak": {
884 | "version": "2.3.6",
885 | "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
886 | "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
887 | "dependencies": {
888 | "@isaacs/cliui": "^8.0.2"
889 | },
890 | "engines": {
891 | "node": ">=14"
892 | },
893 | "funding": {
894 | "url": "https://github.com/sponsors/isaacs"
895 | },
896 | "optionalDependencies": {
897 | "@pkgjs/parseargs": "^0.11.0"
898 | }
899 | },
900 | "node_modules/jiti": {
901 | "version": "1.21.0",
902 | "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
903 | "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
904 | "bin": {
905 | "jiti": "bin/jiti.js"
906 | }
907 | },
908 | "node_modules/js-tokens": {
909 | "version": "4.0.0",
910 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
911 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
912 | },
913 | "node_modules/lilconfig": {
914 | "version": "2.1.0",
915 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
916 | "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
917 | "engines": {
918 | "node": ">=10"
919 | }
920 | },
921 | "node_modules/lines-and-columns": {
922 | "version": "1.2.4",
923 | "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
924 | "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
925 | },
926 | "node_modules/loose-envify": {
927 | "version": "1.4.0",
928 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
929 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
930 | "dependencies": {
931 | "js-tokens": "^3.0.0 || ^4.0.0"
932 | },
933 | "bin": {
934 | "loose-envify": "cli.js"
935 | }
936 | },
937 | "node_modules/lru-cache": {
938 | "version": "10.2.0",
939 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
940 | "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
941 | "engines": {
942 | "node": "14 || >=16.14"
943 | }
944 | },
945 | "node_modules/lucide-react": {
946 | "version": "0.331.0",
947 | "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.331.0.tgz",
948 | "integrity": "sha512-CHFJ0ve9vaZ7bB2VRAl27SlX1ELh6pfNC0jS96qGpPEEzLkLDGq4pDBFU8RhOoRMqsjXqTzLm9U6bZ1OcIHq7Q==",
949 | "peerDependencies": {
950 | "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
951 | }
952 | },
953 | "node_modules/merge2": {
954 | "version": "1.4.1",
955 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
956 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
957 | "engines": {
958 | "node": ">= 8"
959 | }
960 | },
961 | "node_modules/micromatch": {
962 | "version": "4.0.5",
963 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
964 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
965 | "dependencies": {
966 | "braces": "^3.0.2",
967 | "picomatch": "^2.3.1"
968 | },
969 | "engines": {
970 | "node": ">=8.6"
971 | }
972 | },
973 | "node_modules/minimatch": {
974 | "version": "9.0.3",
975 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
976 | "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
977 | "dependencies": {
978 | "brace-expansion": "^2.0.1"
979 | },
980 | "engines": {
981 | "node": ">=16 || 14 >=14.17"
982 | },
983 | "funding": {
984 | "url": "https://github.com/sponsors/isaacs"
985 | }
986 | },
987 | "node_modules/minipass": {
988 | "version": "7.0.4",
989 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
990 | "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
991 | "engines": {
992 | "node": ">=16 || 14 >=14.17"
993 | }
994 | },
995 | "node_modules/mz": {
996 | "version": "2.7.0",
997 | "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
998 | "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
999 | "dependencies": {
1000 | "any-promise": "^1.0.0",
1001 | "object-assign": "^4.0.1",
1002 | "thenify-all": "^1.0.0"
1003 | }
1004 | },
1005 | "node_modules/nanoid": {
1006 | "version": "3.3.7",
1007 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
1008 | "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
1009 | "funding": [
1010 | {
1011 | "type": "github",
1012 | "url": "https://github.com/sponsors/ai"
1013 | }
1014 | ],
1015 | "bin": {
1016 | "nanoid": "bin/nanoid.cjs"
1017 | },
1018 | "engines": {
1019 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1020 | }
1021 | },
1022 | "node_modules/next": {
1023 | "version": "14.1.0",
1024 | "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz",
1025 | "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==",
1026 | "dependencies": {
1027 | "@next/env": "14.1.0",
1028 | "@swc/helpers": "0.5.2",
1029 | "busboy": "1.6.0",
1030 | "caniuse-lite": "^1.0.30001579",
1031 | "graceful-fs": "^4.2.11",
1032 | "postcss": "8.4.31",
1033 | "styled-jsx": "5.1.1"
1034 | },
1035 | "bin": {
1036 | "next": "dist/bin/next"
1037 | },
1038 | "engines": {
1039 | "node": ">=18.17.0"
1040 | },
1041 | "optionalDependencies": {
1042 | "@next/swc-darwin-arm64": "14.1.0",
1043 | "@next/swc-darwin-x64": "14.1.0",
1044 | "@next/swc-linux-arm64-gnu": "14.1.0",
1045 | "@next/swc-linux-arm64-musl": "14.1.0",
1046 | "@next/swc-linux-x64-gnu": "14.1.0",
1047 | "@next/swc-linux-x64-musl": "14.1.0",
1048 | "@next/swc-win32-arm64-msvc": "14.1.0",
1049 | "@next/swc-win32-ia32-msvc": "14.1.0",
1050 | "@next/swc-win32-x64-msvc": "14.1.0"
1051 | },
1052 | "peerDependencies": {
1053 | "@opentelemetry/api": "^1.1.0",
1054 | "react": "^18.2.0",
1055 | "react-dom": "^18.2.0",
1056 | "sass": "^1.3.0"
1057 | },
1058 | "peerDependenciesMeta": {
1059 | "@opentelemetry/api": {
1060 | "optional": true
1061 | },
1062 | "sass": {
1063 | "optional": true
1064 | }
1065 | }
1066 | },
1067 | "node_modules/next/node_modules/postcss": {
1068 | "version": "8.4.31",
1069 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
1070 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
1071 | "funding": [
1072 | {
1073 | "type": "opencollective",
1074 | "url": "https://opencollective.com/postcss/"
1075 | },
1076 | {
1077 | "type": "tidelift",
1078 | "url": "https://tidelift.com/funding/github/npm/postcss"
1079 | },
1080 | {
1081 | "type": "github",
1082 | "url": "https://github.com/sponsors/ai"
1083 | }
1084 | ],
1085 | "dependencies": {
1086 | "nanoid": "^3.3.6",
1087 | "picocolors": "^1.0.0",
1088 | "source-map-js": "^1.0.2"
1089 | },
1090 | "engines": {
1091 | "node": "^10 || ^12 || >=14"
1092 | }
1093 | },
1094 | "node_modules/node-releases": {
1095 | "version": "2.0.14",
1096 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
1097 | "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
1098 | "dev": true
1099 | },
1100 | "node_modules/normalize-path": {
1101 | "version": "3.0.0",
1102 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1103 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1104 | "engines": {
1105 | "node": ">=0.10.0"
1106 | }
1107 | },
1108 | "node_modules/normalize-range": {
1109 | "version": "0.1.2",
1110 | "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
1111 | "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
1112 | "dev": true,
1113 | "engines": {
1114 | "node": ">=0.10.0"
1115 | }
1116 | },
1117 | "node_modules/object-assign": {
1118 | "version": "4.1.1",
1119 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1120 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1121 | "engines": {
1122 | "node": ">=0.10.0"
1123 | }
1124 | },
1125 | "node_modules/object-hash": {
1126 | "version": "3.0.0",
1127 | "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
1128 | "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
1129 | "engines": {
1130 | "node": ">= 6"
1131 | }
1132 | },
1133 | "node_modules/path-key": {
1134 | "version": "3.1.1",
1135 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
1136 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
1137 | "engines": {
1138 | "node": ">=8"
1139 | }
1140 | },
1141 | "node_modules/path-parse": {
1142 | "version": "1.0.7",
1143 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1144 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
1145 | },
1146 | "node_modules/path-scurry": {
1147 | "version": "1.10.1",
1148 | "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
1149 | "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
1150 | "dependencies": {
1151 | "lru-cache": "^9.1.1 || ^10.0.0",
1152 | "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
1153 | },
1154 | "engines": {
1155 | "node": ">=16 || 14 >=14.17"
1156 | },
1157 | "funding": {
1158 | "url": "https://github.com/sponsors/isaacs"
1159 | }
1160 | },
1161 | "node_modules/picocolors": {
1162 | "version": "1.0.0",
1163 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
1164 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
1165 | },
1166 | "node_modules/picomatch": {
1167 | "version": "2.3.1",
1168 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1169 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1170 | "engines": {
1171 | "node": ">=8.6"
1172 | },
1173 | "funding": {
1174 | "url": "https://github.com/sponsors/jonschlinkert"
1175 | }
1176 | },
1177 | "node_modules/pify": {
1178 | "version": "2.3.0",
1179 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
1180 | "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
1181 | "engines": {
1182 | "node": ">=0.10.0"
1183 | }
1184 | },
1185 | "node_modules/pirates": {
1186 | "version": "4.0.6",
1187 | "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
1188 | "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
1189 | "engines": {
1190 | "node": ">= 6"
1191 | }
1192 | },
1193 | "node_modules/postcss": {
1194 | "version": "8.4.35",
1195 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
1196 | "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
1197 | "funding": [
1198 | {
1199 | "type": "opencollective",
1200 | "url": "https://opencollective.com/postcss/"
1201 | },
1202 | {
1203 | "type": "tidelift",
1204 | "url": "https://tidelift.com/funding/github/npm/postcss"
1205 | },
1206 | {
1207 | "type": "github",
1208 | "url": "https://github.com/sponsors/ai"
1209 | }
1210 | ],
1211 | "dependencies": {
1212 | "nanoid": "^3.3.7",
1213 | "picocolors": "^1.0.0",
1214 | "source-map-js": "^1.0.2"
1215 | },
1216 | "engines": {
1217 | "node": "^10 || ^12 || >=14"
1218 | }
1219 | },
1220 | "node_modules/postcss-import": {
1221 | "version": "15.1.0",
1222 | "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
1223 | "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
1224 | "dependencies": {
1225 | "postcss-value-parser": "^4.0.0",
1226 | "read-cache": "^1.0.0",
1227 | "resolve": "^1.1.7"
1228 | },
1229 | "engines": {
1230 | "node": ">=14.0.0"
1231 | },
1232 | "peerDependencies": {
1233 | "postcss": "^8.0.0"
1234 | }
1235 | },
1236 | "node_modules/postcss-js": {
1237 | "version": "4.0.1",
1238 | "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
1239 | "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
1240 | "dependencies": {
1241 | "camelcase-css": "^2.0.1"
1242 | },
1243 | "engines": {
1244 | "node": "^12 || ^14 || >= 16"
1245 | },
1246 | "funding": {
1247 | "type": "opencollective",
1248 | "url": "https://opencollective.com/postcss/"
1249 | },
1250 | "peerDependencies": {
1251 | "postcss": "^8.4.21"
1252 | }
1253 | },
1254 | "node_modules/postcss-load-config": {
1255 | "version": "4.0.2",
1256 | "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
1257 | "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
1258 | "funding": [
1259 | {
1260 | "type": "opencollective",
1261 | "url": "https://opencollective.com/postcss/"
1262 | },
1263 | {
1264 | "type": "github",
1265 | "url": "https://github.com/sponsors/ai"
1266 | }
1267 | ],
1268 | "dependencies": {
1269 | "lilconfig": "^3.0.0",
1270 | "yaml": "^2.3.4"
1271 | },
1272 | "engines": {
1273 | "node": ">= 14"
1274 | },
1275 | "peerDependencies": {
1276 | "postcss": ">=8.0.9",
1277 | "ts-node": ">=9.0.0"
1278 | },
1279 | "peerDependenciesMeta": {
1280 | "postcss": {
1281 | "optional": true
1282 | },
1283 | "ts-node": {
1284 | "optional": true
1285 | }
1286 | }
1287 | },
1288 | "node_modules/postcss-load-config/node_modules/lilconfig": {
1289 | "version": "3.1.0",
1290 | "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.0.tgz",
1291 | "integrity": "sha512-p3cz0JV5vw/XeouBU3Ldnp+ZkBjE+n8ydJ4mcwBrOiXXPqNlrzGBqWs9X4MWF7f+iKUBu794Y8Hh8yawiJbCjw==",
1292 | "engines": {
1293 | "node": ">=14"
1294 | },
1295 | "funding": {
1296 | "url": "https://github.com/sponsors/antonk52"
1297 | }
1298 | },
1299 | "node_modules/postcss-nested": {
1300 | "version": "6.0.1",
1301 | "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
1302 | "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
1303 | "dependencies": {
1304 | "postcss-selector-parser": "^6.0.11"
1305 | },
1306 | "engines": {
1307 | "node": ">=12.0"
1308 | },
1309 | "funding": {
1310 | "type": "opencollective",
1311 | "url": "https://opencollective.com/postcss/"
1312 | },
1313 | "peerDependencies": {
1314 | "postcss": "^8.2.14"
1315 | }
1316 | },
1317 | "node_modules/postcss-selector-parser": {
1318 | "version": "6.0.15",
1319 | "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
1320 | "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
1321 | "dependencies": {
1322 | "cssesc": "^3.0.0",
1323 | "util-deprecate": "^1.0.2"
1324 | },
1325 | "engines": {
1326 | "node": ">=4"
1327 | }
1328 | },
1329 | "node_modules/postcss-value-parser": {
1330 | "version": "4.2.0",
1331 | "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
1332 | "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
1333 | },
1334 | "node_modules/queue-microtask": {
1335 | "version": "1.2.3",
1336 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
1337 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
1338 | "funding": [
1339 | {
1340 | "type": "github",
1341 | "url": "https://github.com/sponsors/feross"
1342 | },
1343 | {
1344 | "type": "patreon",
1345 | "url": "https://www.patreon.com/feross"
1346 | },
1347 | {
1348 | "type": "consulting",
1349 | "url": "https://feross.org/support"
1350 | }
1351 | ]
1352 | },
1353 | "node_modules/react": {
1354 | "version": "18.2.0",
1355 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
1356 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
1357 | "dependencies": {
1358 | "loose-envify": "^1.1.0"
1359 | },
1360 | "engines": {
1361 | "node": ">=0.10.0"
1362 | }
1363 | },
1364 | "node_modules/react-dom": {
1365 | "version": "18.2.0",
1366 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
1367 | "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
1368 | "dependencies": {
1369 | "loose-envify": "^1.1.0",
1370 | "scheduler": "^0.23.0"
1371 | },
1372 | "peerDependencies": {
1373 | "react": "^18.2.0"
1374 | }
1375 | },
1376 | "node_modules/read-cache": {
1377 | "version": "1.0.0",
1378 | "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
1379 | "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
1380 | "dependencies": {
1381 | "pify": "^2.3.0"
1382 | }
1383 | },
1384 | "node_modules/readdirp": {
1385 | "version": "3.6.0",
1386 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1387 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1388 | "dependencies": {
1389 | "picomatch": "^2.2.1"
1390 | },
1391 | "engines": {
1392 | "node": ">=8.10.0"
1393 | }
1394 | },
1395 | "node_modules/regenerator-runtime": {
1396 | "version": "0.14.1",
1397 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
1398 | "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
1399 | },
1400 | "node_modules/resolve": {
1401 | "version": "1.22.8",
1402 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
1403 | "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
1404 | "dependencies": {
1405 | "is-core-module": "^2.13.0",
1406 | "path-parse": "^1.0.7",
1407 | "supports-preserve-symlinks-flag": "^1.0.0"
1408 | },
1409 | "bin": {
1410 | "resolve": "bin/resolve"
1411 | },
1412 | "funding": {
1413 | "url": "https://github.com/sponsors/ljharb"
1414 | }
1415 | },
1416 | "node_modules/reusify": {
1417 | "version": "1.0.4",
1418 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
1419 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
1420 | "engines": {
1421 | "iojs": ">=1.0.0",
1422 | "node": ">=0.10.0"
1423 | }
1424 | },
1425 | "node_modules/run-parallel": {
1426 | "version": "1.2.0",
1427 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
1428 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
1429 | "funding": [
1430 | {
1431 | "type": "github",
1432 | "url": "https://github.com/sponsors/feross"
1433 | },
1434 | {
1435 | "type": "patreon",
1436 | "url": "https://www.patreon.com/feross"
1437 | },
1438 | {
1439 | "type": "consulting",
1440 | "url": "https://feross.org/support"
1441 | }
1442 | ],
1443 | "dependencies": {
1444 | "queue-microtask": "^1.2.2"
1445 | }
1446 | },
1447 | "node_modules/scheduler": {
1448 | "version": "0.23.0",
1449 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
1450 | "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
1451 | "dependencies": {
1452 | "loose-envify": "^1.1.0"
1453 | }
1454 | },
1455 | "node_modules/shebang-command": {
1456 | "version": "2.0.0",
1457 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
1458 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1459 | "dependencies": {
1460 | "shebang-regex": "^3.0.0"
1461 | },
1462 | "engines": {
1463 | "node": ">=8"
1464 | }
1465 | },
1466 | "node_modules/shebang-regex": {
1467 | "version": "3.0.0",
1468 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
1469 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
1470 | "engines": {
1471 | "node": ">=8"
1472 | }
1473 | },
1474 | "node_modules/signal-exit": {
1475 | "version": "4.1.0",
1476 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
1477 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
1478 | "engines": {
1479 | "node": ">=14"
1480 | },
1481 | "funding": {
1482 | "url": "https://github.com/sponsors/isaacs"
1483 | }
1484 | },
1485 | "node_modules/source-map-js": {
1486 | "version": "1.0.2",
1487 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
1488 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
1489 | "engines": {
1490 | "node": ">=0.10.0"
1491 | }
1492 | },
1493 | "node_modules/streamsearch": {
1494 | "version": "1.1.0",
1495 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
1496 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
1497 | "engines": {
1498 | "node": ">=10.0.0"
1499 | }
1500 | },
1501 | "node_modules/string-width": {
1502 | "version": "5.1.2",
1503 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
1504 | "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
1505 | "dependencies": {
1506 | "eastasianwidth": "^0.2.0",
1507 | "emoji-regex": "^9.2.2",
1508 | "strip-ansi": "^7.0.1"
1509 | },
1510 | "engines": {
1511 | "node": ">=12"
1512 | },
1513 | "funding": {
1514 | "url": "https://github.com/sponsors/sindresorhus"
1515 | }
1516 | },
1517 | "node_modules/string-width-cjs": {
1518 | "name": "string-width",
1519 | "version": "4.2.3",
1520 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
1521 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
1522 | "dependencies": {
1523 | "emoji-regex": "^8.0.0",
1524 | "is-fullwidth-code-point": "^3.0.0",
1525 | "strip-ansi": "^6.0.1"
1526 | },
1527 | "engines": {
1528 | "node": ">=8"
1529 | }
1530 | },
1531 | "node_modules/string-width-cjs/node_modules/ansi-regex": {
1532 | "version": "5.0.1",
1533 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
1534 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
1535 | "engines": {
1536 | "node": ">=8"
1537 | }
1538 | },
1539 | "node_modules/string-width-cjs/node_modules/emoji-regex": {
1540 | "version": "8.0.0",
1541 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
1542 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
1543 | },
1544 | "node_modules/string-width-cjs/node_modules/strip-ansi": {
1545 | "version": "6.0.1",
1546 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
1547 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
1548 | "dependencies": {
1549 | "ansi-regex": "^5.0.1"
1550 | },
1551 | "engines": {
1552 | "node": ">=8"
1553 | }
1554 | },
1555 | "node_modules/strip-ansi": {
1556 | "version": "7.1.0",
1557 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
1558 | "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
1559 | "dependencies": {
1560 | "ansi-regex": "^6.0.1"
1561 | },
1562 | "engines": {
1563 | "node": ">=12"
1564 | },
1565 | "funding": {
1566 | "url": "https://github.com/chalk/strip-ansi?sponsor=1"
1567 | }
1568 | },
1569 | "node_modules/strip-ansi-cjs": {
1570 | "name": "strip-ansi",
1571 | "version": "6.0.1",
1572 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
1573 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
1574 | "dependencies": {
1575 | "ansi-regex": "^5.0.1"
1576 | },
1577 | "engines": {
1578 | "node": ">=8"
1579 | }
1580 | },
1581 | "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
1582 | "version": "5.0.1",
1583 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
1584 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
1585 | "engines": {
1586 | "node": ">=8"
1587 | }
1588 | },
1589 | "node_modules/styled-jsx": {
1590 | "version": "5.1.1",
1591 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
1592 | "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
1593 | "dependencies": {
1594 | "client-only": "0.0.1"
1595 | },
1596 | "engines": {
1597 | "node": ">= 12.0.0"
1598 | },
1599 | "peerDependencies": {
1600 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
1601 | },
1602 | "peerDependenciesMeta": {
1603 | "@babel/core": {
1604 | "optional": true
1605 | },
1606 | "babel-plugin-macros": {
1607 | "optional": true
1608 | }
1609 | }
1610 | },
1611 | "node_modules/sucrase": {
1612 | "version": "3.35.0",
1613 | "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
1614 | "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
1615 | "dependencies": {
1616 | "@jridgewell/gen-mapping": "^0.3.2",
1617 | "commander": "^4.0.0",
1618 | "glob": "^10.3.10",
1619 | "lines-and-columns": "^1.1.6",
1620 | "mz": "^2.7.0",
1621 | "pirates": "^4.0.1",
1622 | "ts-interface-checker": "^0.1.9"
1623 | },
1624 | "bin": {
1625 | "sucrase": "bin/sucrase",
1626 | "sucrase-node": "bin/sucrase-node"
1627 | },
1628 | "engines": {
1629 | "node": ">=16 || 14 >=14.17"
1630 | }
1631 | },
1632 | "node_modules/supports-preserve-symlinks-flag": {
1633 | "version": "1.0.0",
1634 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
1635 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
1636 | "engines": {
1637 | "node": ">= 0.4"
1638 | },
1639 | "funding": {
1640 | "url": "https://github.com/sponsors/ljharb"
1641 | }
1642 | },
1643 | "node_modules/tailwind-merge": {
1644 | "version": "2.2.1",
1645 | "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz",
1646 | "integrity": "sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==",
1647 | "dependencies": {
1648 | "@babel/runtime": "^7.23.7"
1649 | },
1650 | "funding": {
1651 | "type": "github",
1652 | "url": "https://github.com/sponsors/dcastil"
1653 | }
1654 | },
1655 | "node_modules/tailwindcss": {
1656 | "version": "3.4.1",
1657 | "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
1658 | "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
1659 | "dependencies": {
1660 | "@alloc/quick-lru": "^5.2.0",
1661 | "arg": "^5.0.2",
1662 | "chokidar": "^3.5.3",
1663 | "didyoumean": "^1.2.2",
1664 | "dlv": "^1.1.3",
1665 | "fast-glob": "^3.3.0",
1666 | "glob-parent": "^6.0.2",
1667 | "is-glob": "^4.0.3",
1668 | "jiti": "^1.19.1",
1669 | "lilconfig": "^2.1.0",
1670 | "micromatch": "^4.0.5",
1671 | "normalize-path": "^3.0.0",
1672 | "object-hash": "^3.0.0",
1673 | "picocolors": "^1.0.0",
1674 | "postcss": "^8.4.23",
1675 | "postcss-import": "^15.1.0",
1676 | "postcss-js": "^4.0.1",
1677 | "postcss-load-config": "^4.0.1",
1678 | "postcss-nested": "^6.0.1",
1679 | "postcss-selector-parser": "^6.0.11",
1680 | "resolve": "^1.22.2",
1681 | "sucrase": "^3.32.0"
1682 | },
1683 | "bin": {
1684 | "tailwind": "lib/cli.js",
1685 | "tailwindcss": "lib/cli.js"
1686 | },
1687 | "engines": {
1688 | "node": ">=14.0.0"
1689 | }
1690 | },
1691 | "node_modules/tailwindcss-animate": {
1692 | "version": "1.0.7",
1693 | "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz",
1694 | "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==",
1695 | "peerDependencies": {
1696 | "tailwindcss": ">=3.0.0 || insiders"
1697 | }
1698 | },
1699 | "node_modules/thenify": {
1700 | "version": "3.3.1",
1701 | "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
1702 | "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
1703 | "dependencies": {
1704 | "any-promise": "^1.0.0"
1705 | }
1706 | },
1707 | "node_modules/thenify-all": {
1708 | "version": "1.6.0",
1709 | "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
1710 | "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
1711 | "dependencies": {
1712 | "thenify": ">= 3.1.0 < 4"
1713 | },
1714 | "engines": {
1715 | "node": ">=0.8"
1716 | }
1717 | },
1718 | "node_modules/to-regex-range": {
1719 | "version": "5.0.1",
1720 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1721 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1722 | "dependencies": {
1723 | "is-number": "^7.0.0"
1724 | },
1725 | "engines": {
1726 | "node": ">=8.0"
1727 | }
1728 | },
1729 | "node_modules/ts-interface-checker": {
1730 | "version": "0.1.13",
1731 | "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
1732 | "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
1733 | },
1734 | "node_modules/tslib": {
1735 | "version": "2.6.2",
1736 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
1737 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
1738 | },
1739 | "node_modules/typescript": {
1740 | "version": "5.3.3",
1741 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
1742 | "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
1743 | "dev": true,
1744 | "bin": {
1745 | "tsc": "bin/tsc",
1746 | "tsserver": "bin/tsserver"
1747 | },
1748 | "engines": {
1749 | "node": ">=14.17"
1750 | }
1751 | },
1752 | "node_modules/undici-types": {
1753 | "version": "5.26.5",
1754 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
1755 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
1756 | "dev": true
1757 | },
1758 | "node_modules/update-browserslist-db": {
1759 | "version": "1.0.13",
1760 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
1761 | "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
1762 | "dev": true,
1763 | "funding": [
1764 | {
1765 | "type": "opencollective",
1766 | "url": "https://opencollective.com/browserslist"
1767 | },
1768 | {
1769 | "type": "tidelift",
1770 | "url": "https://tidelift.com/funding/github/npm/browserslist"
1771 | },
1772 | {
1773 | "type": "github",
1774 | "url": "https://github.com/sponsors/ai"
1775 | }
1776 | ],
1777 | "dependencies": {
1778 | "escalade": "^3.1.1",
1779 | "picocolors": "^1.0.0"
1780 | },
1781 | "bin": {
1782 | "update-browserslist-db": "cli.js"
1783 | },
1784 | "peerDependencies": {
1785 | "browserslist": ">= 4.21.0"
1786 | }
1787 | },
1788 | "node_modules/util-deprecate": {
1789 | "version": "1.0.2",
1790 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1791 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
1792 | },
1793 | "node_modules/which": {
1794 | "version": "2.0.2",
1795 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
1796 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
1797 | "dependencies": {
1798 | "isexe": "^2.0.0"
1799 | },
1800 | "bin": {
1801 | "node-which": "bin/node-which"
1802 | },
1803 | "engines": {
1804 | "node": ">= 8"
1805 | }
1806 | },
1807 | "node_modules/wrap-ansi": {
1808 | "version": "8.1.0",
1809 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
1810 | "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
1811 | "dependencies": {
1812 | "ansi-styles": "^6.1.0",
1813 | "string-width": "^5.0.1",
1814 | "strip-ansi": "^7.0.1"
1815 | },
1816 | "engines": {
1817 | "node": ">=12"
1818 | },
1819 | "funding": {
1820 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
1821 | }
1822 | },
1823 | "node_modules/wrap-ansi-cjs": {
1824 | "name": "wrap-ansi",
1825 | "version": "7.0.0",
1826 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
1827 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
1828 | "dependencies": {
1829 | "ansi-styles": "^4.0.0",
1830 | "string-width": "^4.1.0",
1831 | "strip-ansi": "^6.0.0"
1832 | },
1833 | "engines": {
1834 | "node": ">=10"
1835 | },
1836 | "funding": {
1837 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
1838 | }
1839 | },
1840 | "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
1841 | "version": "5.0.1",
1842 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
1843 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
1844 | "engines": {
1845 | "node": ">=8"
1846 | }
1847 | },
1848 | "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
1849 | "version": "4.3.0",
1850 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
1851 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
1852 | "dependencies": {
1853 | "color-convert": "^2.0.1"
1854 | },
1855 | "engines": {
1856 | "node": ">=8"
1857 | },
1858 | "funding": {
1859 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
1860 | }
1861 | },
1862 | "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
1863 | "version": "8.0.0",
1864 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
1865 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
1866 | },
1867 | "node_modules/wrap-ansi-cjs/node_modules/string-width": {
1868 | "version": "4.2.3",
1869 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
1870 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
1871 | "dependencies": {
1872 | "emoji-regex": "^8.0.0",
1873 | "is-fullwidth-code-point": "^3.0.0",
1874 | "strip-ansi": "^6.0.1"
1875 | },
1876 | "engines": {
1877 | "node": ">=8"
1878 | }
1879 | },
1880 | "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
1881 | "version": "6.0.1",
1882 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
1883 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
1884 | "dependencies": {
1885 | "ansi-regex": "^5.0.1"
1886 | },
1887 | "engines": {
1888 | "node": ">=8"
1889 | }
1890 | },
1891 | "node_modules/yaml": {
1892 | "version": "2.3.4",
1893 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
1894 | "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
1895 | "engines": {
1896 | "node": ">= 14"
1897 | }
1898 | }
1899 | }
1900 | }
1901 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sorafm",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@formatjs/intl-localematcher": "^0.5.4",
13 | "@headlessui/react": "^1.7.18",
14 | "@heroicons/react": "^2.1.1",
15 | "@radix-ui/react-menubar": "^1.0.4",
16 | "@radix-ui/react-select": "^2.0.0",
17 | "@vercel/analytics": "^1.2.2",
18 | "class-variance-authority": "^0.7.0",
19 | "clsx": "^2.1.0",
20 | "lucide-react": "^0.331.0",
21 | "moment": "^2.30.1",
22 | "negotiator": "^0.6.3",
23 | "next": "14.1.0",
24 | "pg": "^8.11.3",
25 | "react": "^18",
26 | "react-dom": "^18",
27 | "react-icons": "^5.0.1",
28 | "sonner": "^1.4.0",
29 | "tailwind-merge": "^2.2.1",
30 | "tailwindcss-animate": "^1.0.7",
31 | "uuid": "^9.0.1"
32 | },
33 | "devDependencies": {
34 | "@types/negotiator": "^0.6.3",
35 | "@types/node": "^20.11.19",
36 | "@types/pg": "^8.11.0",
37 | "@types/react": "^18",
38 | "@types/react-dom": "^18",
39 | "@types/uuid": "^9.0.8",
40 | "autoprefixer": "^10.0.1",
41 | "postcss": "^8",
42 | "tailwindcss": "^3.3.0",
43 | "typescript": "^5"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/all-in-aigc/sorafm/064000503771d7a189588fec8e89125ae25ae563/preview.png
--------------------------------------------------------------------------------
/preview_cn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/all-in-aigc/sorafm/064000503771d7a189588fec8e89125ae25ae563/preview_cn.png
--------------------------------------------------------------------------------
/public/email.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/all-in-aigc/sorafm/064000503771d7a189588fec8e89125ae25ae563/public/favicon.ico
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/services/i18n.ts:
--------------------------------------------------------------------------------
1 | import Negotiator from "negotiator";
2 | import { match } from "@formatjs/intl-localematcher";
3 |
4 | export const locales = ["en", "en-US", "zh", "zh-CN", "ja", "ko", "fr", "de"];
5 | export const localeNames: any = {
6 | en: "🇺🇸 English",
7 | zh: "🇨🇳 中文",
8 | ja: "🇯🇵 日本語",
9 | ko: "🇰🇷 한국어",
10 | fr: "🇫🇷 Français",
11 | de: "🇩🇪 Deutsch",
12 | };
13 | export const defaultLocale = "en";
14 |
15 | export function getLocale(headers: any): string {
16 | let languages = new Negotiator({ headers }).languages();
17 |
18 | return match(languages, locales, defaultLocale);
19 | }
20 |
21 | const dictionaries: any = {
22 | en: () => import("@/dictionaries/en.json").then((module) => module.default),
23 | zh: () => import("@/dictionaries/zh.json").then((module) => module.default),
24 | ja: () => import("@/dictionaries/ja.json").then((module) => module.default),
25 | ko: () => import("@/dictionaries/ko.json").then((module) => module.default),
26 | fr: () => import("@/dictionaries/fr.json").then((module) => module.default),
27 | de: () => import("@/dictionaries/de.json").then((module) => module.default),
28 | };
29 |
30 | export const getDictionary = async (locale: string) => {
31 | if (["zh-CN", "zh-TW", "zh-HK"].includes(locale)) {
32 | locale = "zh";
33 | }
34 |
35 | if (!Object.keys(dictionaries).includes(locale)) {
36 | locale = "en";
37 | }
38 |
39 | return dictionaries[locale]();
40 | };
41 |
--------------------------------------------------------------------------------
/services/user.ts:
--------------------------------------------------------------------------------
1 | import { findUserByEmail, insertUser } from "@/models/user";
2 |
3 | import { User } from "@/types/user";
4 |
5 | export async function saveUser(user: User) {
6 | try {
7 | const existUser = await findUserByEmail(user.email);
8 | if (!existUser) {
9 | await insertUser(user);
10 | }
11 | } catch (e) {
12 | console.log("save user failed: ", e);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/services/video.ts:
--------------------------------------------------------------------------------
1 | import { Video } from "@/types/video";
2 | import fs from "fs";
3 | import { genUuid } from "@/lib";
4 | import moment from "moment";
5 |
6 | export const getVideosFromFile = async (): Promise => {
7 | try {
8 | const dataFile = process.env.VIDEOS_DATA_FILE;
9 | if (!dataFile) {
10 | return [];
11 | }
12 |
13 | const data = fs.readFileSync(dataFile, "utf8");
14 | const jsonData = JSON.parse(data);
15 |
16 | let videos: Video[] = [];
17 | jsonData.map((v: any) => {
18 | videos.push({
19 | uuid: genUuid(),
20 | video_description: v["prompt"],
21 | video_url: v["videoLink"],
22 | cover_url: v["poster"],
23 | user_nickname: v["sourceName"],
24 | user_avatar_url: v["sourceLogo"],
25 | created_at: moment(v["createTime"], "YYYY-MM-DD HH:mm").toISOString(),
26 | post_url: v["sourceUrl"],
27 | });
28 | });
29 |
30 | return videos;
31 | } catch (err) {
32 | console.error("Error loading JSON file:", err);
33 | return [];
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss"
2 |
3 | const config = {
4 | darkMode: ["class"],
5 | content: [
6 | './pages/**/*.{ts,tsx}',
7 | './components/**/*.{ts,tsx}',
8 | './app/**/*.{ts,tsx}',
9 | './src/**/*.{ts,tsx}',
10 | ],
11 | prefix: "",
12 | theme: {
13 | container: {
14 | center: true,
15 | padding: "2rem",
16 | screens: {
17 | "2xl": "1400px",
18 | },
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border))",
23 | input: "hsl(var(--input))",
24 | ring: "hsl(var(--ring))",
25 | background: "hsl(var(--background))",
26 | foreground: "hsl(var(--foreground))",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary))",
29 | foreground: "hsl(var(--primary-foreground))",
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary))",
33 | foreground: "hsl(var(--secondary-foreground))",
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive))",
37 | foreground: "hsl(var(--destructive-foreground))",
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted))",
41 | foreground: "hsl(var(--muted-foreground))",
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent))",
45 | foreground: "hsl(var(--accent-foreground))",
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover))",
49 | foreground: "hsl(var(--popover-foreground))",
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card))",
53 | foreground: "hsl(var(--card-foreground))",
54 | },
55 | },
56 | borderRadius: {
57 | lg: "var(--radius)",
58 | md: "calc(var(--radius) - 2px)",
59 | sm: "calc(var(--radius) - 4px)",
60 | },
61 | keyframes: {
62 | "accordion-down": {
63 | from: { height: "0" },
64 | to: { height: "var(--radix-accordion-content-height)" },
65 | },
66 | "accordion-up": {
67 | from: { height: "var(--radix-accordion-content-height)" },
68 | to: { height: "0" },
69 | },
70 | },
71 | animation: {
72 | "accordion-down": "accordion-down 0.2s ease-out",
73 | "accordion-up": "accordion-up 0.2s ease-out",
74 | },
75 | },
76 | },
77 | plugins: [require("tailwindcss-animate")],
78 | } satisfies Config
79 |
80 | export default config
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": [
25 | "next-env.d.ts",
26 | "**/*.ts",
27 | "**/*.tsx",
28 | ".next/types/**/*.ts",
29 | "app/[lang]/dictionaries.js"
30 | ],
31 | "exclude": ["node_modules"]
32 | }
33 |
--------------------------------------------------------------------------------
/types/nav.d.ts:
--------------------------------------------------------------------------------
1 | export interface Nav {
2 | title: string;
3 | name?: string;
4 | url?: string;
5 | target?: string;
6 | }
7 |
--------------------------------------------------------------------------------
/types/user.d.ts:
--------------------------------------------------------------------------------
1 | export interface User {
2 | id?: number;
3 | email: string;
4 | nickname: string;
5 | avatar_url: string;
6 | created_at?: string;
7 | credits?: UserCredits;
8 | uuid: string;
9 | }
10 |
11 | export interface UserCredits {
12 | one_time_credits: number;
13 | monthly_credits: number;
14 | total_credits: number;
15 | used_credits: number;
16 | left_credits: number;
17 | }
18 |
--------------------------------------------------------------------------------
/types/video.d.ts:
--------------------------------------------------------------------------------
1 | export interface Video {
2 | user_uuid?: string;
3 | video_description: string;
4 | video_url: string;
5 | cover_url?: string;
6 | post_url?: string;
7 | user_nickname?: string;
8 | user_avatar_url?: string;
9 | created_at: string;
10 | uuid: string;
11 | status?: number;
12 | }
13 |
--------------------------------------------------------------------------------