ChandimaPrabath commited on
Commit
9eea1c5
·
1 Parent(s): 35877e1
frontend/.gitignore ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # next.js
17
+ /.next/
18
+ /out/
19
+
20
+ # production
21
+ /build
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # env files (can opt-in for committing if needed)
34
+ .env*
35
+
36
+ # vercel
37
+ .vercel
38
+
39
+ # typescript
40
+ *.tsbuildinfo
41
+ next-env.d.ts
frontend/README.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2
+
3
+ ## Getting Started
4
+
5
+ First, run the development server:
6
+
7
+ ```bash
8
+ npm run dev
9
+ # or
10
+ yarn dev
11
+ # or
12
+ pnpm dev
13
+ # or
14
+ bun dev
15
+ ```
16
+
17
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18
+
19
+ You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
20
+
21
+ This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22
+
23
+ ## Learn More
24
+
25
+ To learn more about Next.js, take a look at the following resources:
26
+
27
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29
+
30
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31
+
32
+ ## Deploy on Vercel
33
+
34
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35
+
36
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
frontend/app/Sidebar.js ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Sidebar.js
2
+ import { useState } from 'react';
3
+ import { useRouter } from 'next/navigation';
4
+
5
+ export default function Sidebar({
6
+ searchQuery,
7
+ setSearchQuery,
8
+ filteredUsers,
9
+ handleUserSelect,
10
+ setFilteredUsers,
11
+ }) {
12
+ const handleSearchChange = async (e) => {
13
+ const query = e.target.value;
14
+ setSearchQuery(query);
15
+
16
+ if (query.length > 2) {
17
+ try {
18
+ const response = await fetch(`https://Hans-R-D-nexus-services.hf.space/search?query=${query}`);
19
+ const data = await response.json();
20
+ setFilteredUsers(data);
21
+ } catch (error) {
22
+ console.error('Error fetching search results:', error);
23
+ }
24
+ } else {
25
+ setFilteredUsers([]);
26
+ }
27
+ };
28
+
29
+ return (
30
+ <div style={{ width: '250px', backgroundColor: '#333', padding: '20px' }}>
31
+ <h3 style={{ color: '#fff' }}>Select Recipient</h3>
32
+ <input
33
+ type="text"
34
+ placeholder="Search Users..."
35
+ value={searchQuery}
36
+ onChange={handleSearchChange}
37
+ style={searchInputStyle}
38
+ />
39
+ <ul style={{ listStyleType: 'none', padding: 0 }}>
40
+ {filteredUsers.map((recipient, index) => (
41
+ <li key={index} style={{ color: '#fff', cursor: 'pointer' }}>
42
+ <span
43
+ style={{ textDecoration: 'none', color: '#fff' }}
44
+ onClick={() => handleUserSelect(recipient)}
45
+ >
46
+ {recipient}
47
+ </span>
48
+ </li>
49
+ ))}
50
+ </ul>
51
+ </div>
52
+ );
53
+ }
54
+
55
+ const searchInputStyle = {
56
+ width: '100%',
57
+ padding: '10px',
58
+ fontSize: '16px',
59
+ borderRadius: '5px',
60
+ border: '1px solid #4b2e83',
61
+ backgroundColor: '#2e2e4f',
62
+ color: '#fff',
63
+ marginBottom: '15px',
64
+ };
frontend/app/WebSocketContext.js ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { createContext, useContext, useState, useEffect } from 'react';
2
+
3
+ const WebSocketContext = createContext(null);
4
+
5
+ export const useWebSocket = () => {
6
+ return useContext(WebSocketContext);
7
+ };
8
+
9
+ export const WebSocketProvider = ({ children }) => {
10
+ const [ws, setWs] = useState(null);
11
+ const [status, setStatus] = useState('Disconnected');
12
+
13
+ useEffect(() => {
14
+ const socket = new WebSocket('ws://Hans-R-D-nexus-relay.hf.space/ws');
15
+
16
+ socket.onopen = () => {
17
+ console.log('WebSocket connection opened.');
18
+ setStatus('Connected');
19
+ };
20
+
21
+ socket.onclose = () => {
22
+ console.log('WebSocket connection closed.');
23
+ setStatus('Disconnected');
24
+ };
25
+
26
+ socket.onerror = (error) => {
27
+ console.error('WebSocket error:', error);
28
+ setStatus('Error');
29
+ };
30
+
31
+ setWs(socket);
32
+
33
+ return () => {
34
+ socket.close();
35
+ };
36
+ }, []);
37
+
38
+ return (
39
+ <WebSocketContext.Provider value={{ ws, status }}>
40
+ {children}
41
+ </WebSocketContext.Provider>
42
+ );
43
+ };
frontend/app/[recipientusername]/page.js ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+ import { useState, useEffect } from "react";
3
+ import { useParams } from "next/navigation";
4
+ import { useWebSocket } from "../WebSocketContext"; // Import the WebSocket hook
5
+
6
+ export default function ChatPage() {
7
+ const { recipientusername } = useParams(); // Use useParams to access the dynamic route parameter
8
+ const [recipient, setRecipient] = useState(null);
9
+ const [message, setMessage] = useState("");
10
+ const [chatLog, setChatLog] = useState([]);
11
+ const { ws } = useWebSocket(); // Access WebSocket from context
12
+
13
+ // Update recipient state when recipientusername changes
14
+ useEffect(() => {
15
+ if (recipientusername) {
16
+ setRecipient(recipientusername);
17
+ }
18
+ }, [recipientusername]);
19
+
20
+ // Load chat history from localStorage when recipient changes
21
+ useEffect(() => {
22
+ if (recipient) {
23
+ const storedMessages = JSON.parse(localStorage.getItem(recipient)) || [];
24
+ setChatLog(storedMessages);
25
+ }
26
+ }, [recipient]);
27
+
28
+ // Re-fetch the chat log every 2 seconds
29
+ useEffect(() => {
30
+ const interval = setInterval(() => {
31
+ if (recipient) {
32
+ const updatedMessages = JSON.parse(localStorage.getItem(recipient)) || [];
33
+ setChatLog(updatedMessages);
34
+ }
35
+ }, 2000); // Fetch chat log every 2 seconds
36
+
37
+ // Cleanup the interval when component unmounts
38
+ return () => clearInterval(interval);
39
+ }, [recipient]);
40
+
41
+ const handleSendMessage = () => {
42
+ // Check if the WebSocket is connected and the message is not empty
43
+ if (!ws) {
44
+ alert("WebSocket is not connected.");
45
+ return;
46
+ }
47
+
48
+ if (!message.trim()) {
49
+ alert("Message cannot be empty.");
50
+ return;
51
+ }
52
+
53
+ if (recipient && ws) {
54
+ const msgData = JSON.stringify({ recipient, message });
55
+ ws.send(msgData);
56
+
57
+ setChatLog((prev) => [
58
+ ...prev,
59
+ { from: "You", message, timestamp: new Date().toLocaleString() },
60
+ ]);
61
+ setMessage("");
62
+
63
+ // Update localStorage
64
+ const updatedChatLog = [
65
+ ...chatLog,
66
+ { from: "You", message, timestamp: new Date().toLocaleString() },
67
+ ];
68
+ localStorage.setItem(recipient, JSON.stringify(updatedChatLog));
69
+ }
70
+ };
71
+
72
+ return (
73
+ <div
74
+ style={{
75
+ backgroundColor: "#1a1a2e",
76
+ minHeight: "100vh",
77
+ fontFamily: "Segoe UI, Tahoma, Geneva, Verdana, sans-serif",
78
+ padding: "20px",
79
+ color: "#fff",
80
+ }}
81
+ >
82
+ {recipient ? (
83
+ <>
84
+ <h1 style={{ color: "#d4a5ff" }}>Chat with {recipient}</h1>
85
+
86
+ <div style={{ width: "100%", maxWidth: "600px" }}>
87
+ <div style={{ marginBottom: "10px" }}>
88
+ <input
89
+ type="text"
90
+ placeholder="Message"
91
+ value={message}
92
+ onChange={(e) => setMessage(e.target.value)}
93
+ style={inputStyle}
94
+ />
95
+ <button onClick={handleSendMessage} style={sendButtonStyle}>
96
+ Send
97
+ </button>
98
+ </div>
99
+
100
+ <div
101
+ style={{
102
+ maxHeight: "400px",
103
+ overflowY: "auto",
104
+ marginTop: "20px",
105
+ padding: "10px",
106
+ backgroundColor: "#2e2e4f",
107
+ borderRadius: "10px",
108
+ boxShadow: "0 2px 5px rgba(0, 0, 0, 0.2)",
109
+ }}
110
+ >
111
+ {chatLog.map((entry, index) => (
112
+ <div
113
+ key={index}
114
+ style={{
115
+ backgroundColor: entry.from === "You" ? "#4b2e83" : "#3d3d6b",
116
+ color: "#fff",
117
+ padding: "10px",
118
+ marginBottom: "10px",
119
+ borderRadius: "10px",
120
+ maxWidth: "80%",
121
+ marginLeft: entry.from === "You" ? "auto" : "0",
122
+ wordWrap: "break-word",
123
+ }}
124
+ >
125
+ <p
126
+ style={{
127
+ margin: 0,
128
+ fontWeight: "bold",
129
+ fontSize: "14px",
130
+ color: "#e0b0ff",
131
+ }}
132
+ >
133
+ {entry.from}
134
+ </p>
135
+ <p style={{ margin: "5px 0" }}>{entry.message}</p>
136
+ <small style={{ fontSize: "12px", color: "#a1a1c7" }}>
137
+ {entry.timestamp}
138
+ </small>
139
+ </div>
140
+ ))}
141
+ </div>
142
+ </div>
143
+ </>
144
+ ) : (
145
+ <p>Loading recipient...</p>
146
+ )}
147
+ </div>
148
+ );
149
+ }
150
+
151
+ const inputStyle = {
152
+ width: "100%",
153
+ padding: "10px",
154
+ margin: "5px 0",
155
+ borderRadius: "5px",
156
+ border: "1px solid #4b2e83",
157
+ fontSize: "16px",
158
+ backgroundColor: "#2e2e4f",
159
+ color: "#fff",
160
+ };
161
+
162
+ const sendButtonStyle = {
163
+ padding: "12px 20px",
164
+ backgroundColor: "#6a4c9c",
165
+ color: "#fff",
166
+ border: "none",
167
+ borderRadius: "5px",
168
+ fontSize: "16px",
169
+ cursor: "pointer",
170
+ marginLeft: "10px",
171
+ transition: "background-color 0.3s",
172
+ };
frontend/app/favicon.ico ADDED
frontend/app/globals.css ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --background: #ffffff;
3
+ --foreground: #171717;
4
+ }
5
+
6
+ @media (prefers-color-scheme: dark) {
7
+ :root {
8
+ --background: #0a0a0a;
9
+ --foreground: #ededed;
10
+ }
11
+ }
12
+
13
+ html,
14
+ body {
15
+ max-width: 100vw;
16
+ overflow-x: hidden;
17
+ }
18
+
19
+ body {
20
+ color: var(--foreground);
21
+ background: var(--background);
22
+ font-family: Arial, Helvetica, sans-serif;
23
+ -webkit-font-smoothing: antialiased;
24
+ -moz-osx-font-smoothing: grayscale;
25
+ }
26
+
27
+ * {
28
+ box-sizing: border-box;
29
+ padding: 0;
30
+ margin: 0;
31
+ }
32
+
33
+ a {
34
+ color: inherit;
35
+ text-decoration: none;
36
+ }
37
+
38
+ @media (prefers-color-scheme: dark) {
39
+ html {
40
+ color-scheme: dark;
41
+ }
42
+ }
frontend/app/layout.js ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+ import { useState, useEffect } from 'react';
3
+ import { useRouter } from 'next/navigation';
4
+ import { WebSocketProvider, useWebSocket } from './WebSocketContext';
5
+ import './globals.css';
6
+ import Sidebar from './Sidebar'; // Import Sidebar Component
7
+
8
+ export default function RootLayout({ children }) {
9
+ const [username, setUsername] = useState('');
10
+ const [password, setPassword] = useState('');
11
+ const [isLoading, setIsLoading] = useState(false);
12
+ const [recipientList, setRecipientList] = useState([]);
13
+ const [searchQuery, setSearchQuery] = useState('');
14
+ const [filteredUsers, setFilteredUsers] = useState([]);
15
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
16
+
17
+ return (
18
+ <html lang="en">
19
+ <body>
20
+ <WebSocketProvider>
21
+ <WebSocketLayout
22
+ username={username}
23
+ setUsername={setUsername}
24
+ password={password}
25
+ setPassword={setPassword}
26
+ isLoading={isLoading}
27
+ setIsLoading={setIsLoading}
28
+ recipientList={recipientList}
29
+ setRecipientList={setRecipientList}
30
+ searchQuery={searchQuery}
31
+ setSearchQuery={setSearchQuery}
32
+ filteredUsers={filteredUsers}
33
+ setFilteredUsers={setFilteredUsers}
34
+ isLoggedIn={isLoggedIn}
35
+ setIsLoggedIn={setIsLoggedIn}
36
+ >
37
+ {children}
38
+ </WebSocketLayout>
39
+ </WebSocketProvider>
40
+ </body>
41
+ </html>
42
+ );
43
+ }
44
+
45
+ function WebSocketLayout({
46
+ username,
47
+ setUsername,
48
+ password,
49
+ setPassword,
50
+ isLoading,
51
+ setIsLoading,
52
+ recipientList,
53
+ setRecipientList,
54
+ searchQuery,
55
+ setSearchQuery,
56
+ filteredUsers,
57
+ setFilteredUsers,
58
+ isLoggedIn,
59
+ setIsLoggedIn,
60
+ children,
61
+ }) {
62
+ const { ws, status } = useWebSocket();
63
+ const router = useRouter();
64
+
65
+ const connectAndAuthenticate = () => {
66
+ if (!username || !password) {
67
+ alert('Please enter both username and password');
68
+ return;
69
+ }
70
+
71
+ setIsLoading(true);
72
+
73
+ const loginData = JSON.stringify({ username, password });
74
+ if (ws && ws.readyState === WebSocket.OPEN) {
75
+ ws.send(loginData);
76
+ }
77
+ };
78
+
79
+ useEffect(() => {
80
+ if (ws) {
81
+ ws.onmessage = (event) => {
82
+ const data = JSON.parse(event.data);
83
+ console.log('Received message:', data);
84
+
85
+ if (data.status === 'success') {
86
+ setIsLoading(false);
87
+ setIsLoggedIn(true);
88
+ setRecipientList(data.recipients || []);
89
+ } else if (data.status === 'error') {
90
+ console.error(data.message);
91
+ alert(data.message);
92
+ setIsLoading(false);
93
+ } else {
94
+ const { from: recipient, message } = data;
95
+
96
+ if (recipient && message) {
97
+ try {
98
+ const storedMessages = JSON.parse(localStorage.getItem(recipient)) || [];
99
+ storedMessages.push({ message, timestamp: data.timestamp });
100
+ localStorage.setItem(recipient, JSON.stringify(storedMessages));
101
+ console.log('LocalStorage after saving:', localStorage.getItem(recipient));
102
+ } catch (error) {
103
+ console.error('Error accessing localStorage:', error);
104
+ }
105
+ } else {
106
+ console.error('Received message is missing recipient or message.');
107
+ }
108
+ }
109
+ };
110
+ }
111
+ }, [ws]);
112
+
113
+ const handleUserSelect = (recipient) => {
114
+ router.push(`/${recipient}`);
115
+ };
116
+
117
+ return (
118
+ <div>
119
+ {isLoggedIn ? (
120
+ <div style={{ display: 'flex', minHeight: '100vh' }}>
121
+ <Sidebar
122
+ searchQuery={searchQuery}
123
+ setSearchQuery={setSearchQuery}
124
+ setFilteredUsers={setFilteredUsers}
125
+ filteredUsers={filteredUsers}
126
+ handleUserSelect={handleUserSelect}
127
+ />
128
+ <div style={{ flex: 1, padding: '20px' }}>
129
+ {children}
130
+ </div>
131
+ </div>
132
+ ) : (
133
+ <div style={loginContainerStyle}>
134
+ <h2 style={headingStyle}>Login</h2>
135
+ <div style={inputContainerStyle}>
136
+ <input
137
+ type="text"
138
+ placeholder="Username"
139
+ value={username}
140
+ onChange={(e) => setUsername(e.target.value)}
141
+ style={inputStyle}
142
+ />
143
+ <input
144
+ type="password"
145
+ placeholder="Password"
146
+ value={password}
147
+ onChange={(e) => setPassword(e.target.value)}
148
+ style={inputStyle}
149
+ />
150
+ <button onClick={connectAndAuthenticate} style={loginButtonStyle}>
151
+ {isLoading ? 'Connecting...' : 'Login'}
152
+ </button>
153
+ </div>
154
+ {status === 'Error' && <p style={{ color: 'red' }}>Connection failed. Please try again.</p>}
155
+ </div>
156
+ )}
157
+ </div>
158
+ );
159
+ }
160
+
161
+ const loginContainerStyle = {
162
+ display: 'flex',
163
+ justifyContent: 'center',
164
+ alignItems: 'center',
165
+ height: '100vh',
166
+ backgroundColor: '#2e2e4f',
167
+ };
168
+
169
+ const headingStyle = {
170
+ color: '#d4a5ff',
171
+ fontSize: '36px',
172
+ };
173
+
174
+ const inputContainerStyle = {
175
+ display: 'flex',
176
+ flexDirection: 'column',
177
+ gap: '15px',
178
+ maxWidth: '400px',
179
+ width: '100%',
180
+ };
181
+
182
+ const inputStyle = {
183
+ padding: '10px',
184
+ fontSize: '16px',
185
+ borderRadius: '5px',
186
+ border: '1px solid #4b2e83',
187
+ marginBottom: '10px',
188
+ backgroundColor: '#2e2e4f',
189
+ color: '#fff',
190
+ };
191
+
192
+ const loginButtonStyle = {
193
+ padding: '12px 20px',
194
+ backgroundColor: '#6a4c9c',
195
+ color: '#fff',
196
+ border: 'none',
197
+ borderRadius: '5px',
198
+ fontSize: '16px',
199
+ cursor: 'pointer',
200
+ transition: 'background-color 0.3s',
201
+ };
frontend/app/page.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ export default function ChatPage() {
6
+
7
+
8
+ return (
9
+ <div style={{ width: "100%", maxWidth: "600px" }}>
10
+ </div>
11
+ );
12
+ }
frontend/app/page.module.css ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .page {
2
+ --gray-rgb: 0, 0, 0;
3
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
4
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
5
+
6
+ --button-primary-hover: #383838;
7
+ --button-secondary-hover: #f2f2f2;
8
+
9
+ display: grid;
10
+ grid-template-rows: 20px 1fr 20px;
11
+ align-items: center;
12
+ justify-items: center;
13
+ min-height: 100svh;
14
+ padding: 80px;
15
+ gap: 64px;
16
+ font-family: var(--font-geist-sans);
17
+ }
18
+
19
+ @media (prefers-color-scheme: dark) {
20
+ .page {
21
+ --gray-rgb: 255, 255, 255;
22
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
23
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
24
+
25
+ --button-primary-hover: #ccc;
26
+ --button-secondary-hover: #1a1a1a;
27
+ }
28
+ }
29
+
30
+ .main {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 32px;
34
+ grid-row-start: 2;
35
+ }
36
+
37
+ .main ol {
38
+ font-family: var(--font-geist-mono);
39
+ padding-left: 0;
40
+ margin: 0;
41
+ font-size: 14px;
42
+ line-height: 24px;
43
+ letter-spacing: -0.01em;
44
+ list-style-position: inside;
45
+ }
46
+
47
+ .main li:not(:last-of-type) {
48
+ margin-bottom: 8px;
49
+ }
50
+
51
+ .main code {
52
+ font-family: inherit;
53
+ background: var(--gray-alpha-100);
54
+ padding: 2px 4px;
55
+ border-radius: 4px;
56
+ font-weight: 600;
57
+ }
58
+
59
+ .ctas {
60
+ display: flex;
61
+ gap: 16px;
62
+ }
63
+
64
+ .ctas a {
65
+ appearance: none;
66
+ border-radius: 128px;
67
+ height: 48px;
68
+ padding: 0 20px;
69
+ border: none;
70
+ border: 1px solid transparent;
71
+ transition:
72
+ background 0.2s,
73
+ color 0.2s,
74
+ border-color 0.2s;
75
+ cursor: pointer;
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ font-size: 16px;
80
+ line-height: 20px;
81
+ font-weight: 500;
82
+ }
83
+
84
+ a.primary {
85
+ background: var(--foreground);
86
+ color: var(--background);
87
+ gap: 8px;
88
+ }
89
+
90
+ a.secondary {
91
+ border-color: var(--gray-alpha-200);
92
+ min-width: 180px;
93
+ }
94
+
95
+ .footer {
96
+ grid-row-start: 3;
97
+ display: flex;
98
+ gap: 24px;
99
+ }
100
+
101
+ .footer a {
102
+ display: flex;
103
+ align-items: center;
104
+ gap: 8px;
105
+ }
106
+
107
+ .footer img {
108
+ flex-shrink: 0;
109
+ }
110
+
111
+ /* Enable hover only on non-touch devices */
112
+ @media (hover: hover) and (pointer: fine) {
113
+ a.primary:hover {
114
+ background: var(--button-primary-hover);
115
+ border-color: transparent;
116
+ }
117
+
118
+ a.secondary:hover {
119
+ background: var(--button-secondary-hover);
120
+ border-color: transparent;
121
+ }
122
+
123
+ .footer a:hover {
124
+ text-decoration: underline;
125
+ text-underline-offset: 4px;
126
+ }
127
+ }
128
+
129
+ @media (max-width: 600px) {
130
+ .page {
131
+ padding: 32px;
132
+ padding-bottom: 80px;
133
+ }
134
+
135
+ .main {
136
+ align-items: center;
137
+ }
138
+
139
+ .main ol {
140
+ text-align: center;
141
+ }
142
+
143
+ .ctas {
144
+ flex-direction: column;
145
+ }
146
+
147
+ .ctas a {
148
+ font-size: 14px;
149
+ height: 40px;
150
+ padding: 0 16px;
151
+ }
152
+
153
+ a.secondary {
154
+ min-width: auto;
155
+ }
156
+
157
+ .footer {
158
+ flex-wrap: wrap;
159
+ align-items: center;
160
+ justify-content: center;
161
+ }
162
+ }
163
+
164
+ @media (prefers-color-scheme: dark) {
165
+ .logo {
166
+ filter: invert();
167
+ }
168
+ }
frontend/jsconfig.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "@/*": ["./*"]
5
+ }
6
+ }
7
+ }
frontend/next.config.mjs ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {};
3
+
4
+ export default nextConfig;
frontend/package-lock.json ADDED
@@ -0,0 +1,878 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "nex-com",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "nex-com",
9
+ "version": "0.1.0",
10
+ "dependencies": {
11
+ "next": "15.1.4",
12
+ "react": "^19.0.0",
13
+ "react-dom": "^19.0.0"
14
+ }
15
+ },
16
+ "node_modules/@emnapi/runtime": {
17
+ "version": "1.3.1",
18
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
19
+ "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
20
+ "license": "MIT",
21
+ "optional": true,
22
+ "dependencies": {
23
+ "tslib": "^2.4.0"
24
+ }
25
+ },
26
+ "node_modules/@img/sharp-darwin-arm64": {
27
+ "version": "0.33.5",
28
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
29
+ "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
30
+ "cpu": [
31
+ "arm64"
32
+ ],
33
+ "license": "Apache-2.0",
34
+ "optional": true,
35
+ "os": [
36
+ "darwin"
37
+ ],
38
+ "engines": {
39
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
40
+ },
41
+ "funding": {
42
+ "url": "https://opencollective.com/libvips"
43
+ },
44
+ "optionalDependencies": {
45
+ "@img/sharp-libvips-darwin-arm64": "1.0.4"
46
+ }
47
+ },
48
+ "node_modules/@img/sharp-darwin-x64": {
49
+ "version": "0.33.5",
50
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
51
+ "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
52
+ "cpu": [
53
+ "x64"
54
+ ],
55
+ "license": "Apache-2.0",
56
+ "optional": true,
57
+ "os": [
58
+ "darwin"
59
+ ],
60
+ "engines": {
61
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
62
+ },
63
+ "funding": {
64
+ "url": "https://opencollective.com/libvips"
65
+ },
66
+ "optionalDependencies": {
67
+ "@img/sharp-libvips-darwin-x64": "1.0.4"
68
+ }
69
+ },
70
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
71
+ "version": "1.0.4",
72
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
73
+ "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
74
+ "cpu": [
75
+ "arm64"
76
+ ],
77
+ "license": "LGPL-3.0-or-later",
78
+ "optional": true,
79
+ "os": [
80
+ "darwin"
81
+ ],
82
+ "funding": {
83
+ "url": "https://opencollective.com/libvips"
84
+ }
85
+ },
86
+ "node_modules/@img/sharp-libvips-darwin-x64": {
87
+ "version": "1.0.4",
88
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
89
+ "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
90
+ "cpu": [
91
+ "x64"
92
+ ],
93
+ "license": "LGPL-3.0-or-later",
94
+ "optional": true,
95
+ "os": [
96
+ "darwin"
97
+ ],
98
+ "funding": {
99
+ "url": "https://opencollective.com/libvips"
100
+ }
101
+ },
102
+ "node_modules/@img/sharp-libvips-linux-arm": {
103
+ "version": "1.0.5",
104
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
105
+ "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
106
+ "cpu": [
107
+ "arm"
108
+ ],
109
+ "license": "LGPL-3.0-or-later",
110
+ "optional": true,
111
+ "os": [
112
+ "linux"
113
+ ],
114
+ "funding": {
115
+ "url": "https://opencollective.com/libvips"
116
+ }
117
+ },
118
+ "node_modules/@img/sharp-libvips-linux-arm64": {
119
+ "version": "1.0.4",
120
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
121
+ "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
122
+ "cpu": [
123
+ "arm64"
124
+ ],
125
+ "license": "LGPL-3.0-or-later",
126
+ "optional": true,
127
+ "os": [
128
+ "linux"
129
+ ],
130
+ "funding": {
131
+ "url": "https://opencollective.com/libvips"
132
+ }
133
+ },
134
+ "node_modules/@img/sharp-libvips-linux-s390x": {
135
+ "version": "1.0.4",
136
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
137
+ "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
138
+ "cpu": [
139
+ "s390x"
140
+ ],
141
+ "license": "LGPL-3.0-or-later",
142
+ "optional": true,
143
+ "os": [
144
+ "linux"
145
+ ],
146
+ "funding": {
147
+ "url": "https://opencollective.com/libvips"
148
+ }
149
+ },
150
+ "node_modules/@img/sharp-libvips-linux-x64": {
151
+ "version": "1.0.4",
152
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
153
+ "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
154
+ "cpu": [
155
+ "x64"
156
+ ],
157
+ "license": "LGPL-3.0-or-later",
158
+ "optional": true,
159
+ "os": [
160
+ "linux"
161
+ ],
162
+ "funding": {
163
+ "url": "https://opencollective.com/libvips"
164
+ }
165
+ },
166
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
167
+ "version": "1.0.4",
168
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
169
+ "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
170
+ "cpu": [
171
+ "arm64"
172
+ ],
173
+ "license": "LGPL-3.0-or-later",
174
+ "optional": true,
175
+ "os": [
176
+ "linux"
177
+ ],
178
+ "funding": {
179
+ "url": "https://opencollective.com/libvips"
180
+ }
181
+ },
182
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
183
+ "version": "1.0.4",
184
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
185
+ "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
186
+ "cpu": [
187
+ "x64"
188
+ ],
189
+ "license": "LGPL-3.0-or-later",
190
+ "optional": true,
191
+ "os": [
192
+ "linux"
193
+ ],
194
+ "funding": {
195
+ "url": "https://opencollective.com/libvips"
196
+ }
197
+ },
198
+ "node_modules/@img/sharp-linux-arm": {
199
+ "version": "0.33.5",
200
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
201
+ "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
202
+ "cpu": [
203
+ "arm"
204
+ ],
205
+ "license": "Apache-2.0",
206
+ "optional": true,
207
+ "os": [
208
+ "linux"
209
+ ],
210
+ "engines": {
211
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
212
+ },
213
+ "funding": {
214
+ "url": "https://opencollective.com/libvips"
215
+ },
216
+ "optionalDependencies": {
217
+ "@img/sharp-libvips-linux-arm": "1.0.5"
218
+ }
219
+ },
220
+ "node_modules/@img/sharp-linux-arm64": {
221
+ "version": "0.33.5",
222
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
223
+ "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
224
+ "cpu": [
225
+ "arm64"
226
+ ],
227
+ "license": "Apache-2.0",
228
+ "optional": true,
229
+ "os": [
230
+ "linux"
231
+ ],
232
+ "engines": {
233
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
234
+ },
235
+ "funding": {
236
+ "url": "https://opencollective.com/libvips"
237
+ },
238
+ "optionalDependencies": {
239
+ "@img/sharp-libvips-linux-arm64": "1.0.4"
240
+ }
241
+ },
242
+ "node_modules/@img/sharp-linux-s390x": {
243
+ "version": "0.33.5",
244
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
245
+ "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
246
+ "cpu": [
247
+ "s390x"
248
+ ],
249
+ "license": "Apache-2.0",
250
+ "optional": true,
251
+ "os": [
252
+ "linux"
253
+ ],
254
+ "engines": {
255
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
256
+ },
257
+ "funding": {
258
+ "url": "https://opencollective.com/libvips"
259
+ },
260
+ "optionalDependencies": {
261
+ "@img/sharp-libvips-linux-s390x": "1.0.4"
262
+ }
263
+ },
264
+ "node_modules/@img/sharp-linux-x64": {
265
+ "version": "0.33.5",
266
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
267
+ "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
268
+ "cpu": [
269
+ "x64"
270
+ ],
271
+ "license": "Apache-2.0",
272
+ "optional": true,
273
+ "os": [
274
+ "linux"
275
+ ],
276
+ "engines": {
277
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
278
+ },
279
+ "funding": {
280
+ "url": "https://opencollective.com/libvips"
281
+ },
282
+ "optionalDependencies": {
283
+ "@img/sharp-libvips-linux-x64": "1.0.4"
284
+ }
285
+ },
286
+ "node_modules/@img/sharp-linuxmusl-arm64": {
287
+ "version": "0.33.5",
288
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
289
+ "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
290
+ "cpu": [
291
+ "arm64"
292
+ ],
293
+ "license": "Apache-2.0",
294
+ "optional": true,
295
+ "os": [
296
+ "linux"
297
+ ],
298
+ "engines": {
299
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
300
+ },
301
+ "funding": {
302
+ "url": "https://opencollective.com/libvips"
303
+ },
304
+ "optionalDependencies": {
305
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
306
+ }
307
+ },
308
+ "node_modules/@img/sharp-linuxmusl-x64": {
309
+ "version": "0.33.5",
310
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
311
+ "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
312
+ "cpu": [
313
+ "x64"
314
+ ],
315
+ "license": "Apache-2.0",
316
+ "optional": true,
317
+ "os": [
318
+ "linux"
319
+ ],
320
+ "engines": {
321
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
322
+ },
323
+ "funding": {
324
+ "url": "https://opencollective.com/libvips"
325
+ },
326
+ "optionalDependencies": {
327
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
328
+ }
329
+ },
330
+ "node_modules/@img/sharp-wasm32": {
331
+ "version": "0.33.5",
332
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
333
+ "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
334
+ "cpu": [
335
+ "wasm32"
336
+ ],
337
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
338
+ "optional": true,
339
+ "dependencies": {
340
+ "@emnapi/runtime": "^1.2.0"
341
+ },
342
+ "engines": {
343
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
344
+ },
345
+ "funding": {
346
+ "url": "https://opencollective.com/libvips"
347
+ }
348
+ },
349
+ "node_modules/@img/sharp-win32-ia32": {
350
+ "version": "0.33.5",
351
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
352
+ "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
353
+ "cpu": [
354
+ "ia32"
355
+ ],
356
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
357
+ "optional": true,
358
+ "os": [
359
+ "win32"
360
+ ],
361
+ "engines": {
362
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
363
+ },
364
+ "funding": {
365
+ "url": "https://opencollective.com/libvips"
366
+ }
367
+ },
368
+ "node_modules/@img/sharp-win32-x64": {
369
+ "version": "0.33.5",
370
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
371
+ "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
372
+ "cpu": [
373
+ "x64"
374
+ ],
375
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
376
+ "optional": true,
377
+ "os": [
378
+ "win32"
379
+ ],
380
+ "engines": {
381
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
382
+ },
383
+ "funding": {
384
+ "url": "https://opencollective.com/libvips"
385
+ }
386
+ },
387
+ "node_modules/@next/env": {
388
+ "version": "15.1.4",
389
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.4.tgz",
390
+ "integrity": "sha512-2fZ5YZjedi5AGaeoaC0B20zGntEHRhi2SdWcu61i48BllODcAmmtj8n7YarSPt4DaTsJaBFdxQAVEVzgmx2Zpw==",
391
+ "license": "MIT"
392
+ },
393
+ "node_modules/@next/swc-darwin-arm64": {
394
+ "version": "15.1.4",
395
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.4.tgz",
396
+ "integrity": "sha512-wBEMBs+np+R5ozN1F8Y8d/Dycns2COhRnkxRc+rvnbXke5uZBHkUGFgWxfTXn5rx7OLijuUhyfB+gC/ap58dDw==",
397
+ "cpu": [
398
+ "arm64"
399
+ ],
400
+ "license": "MIT",
401
+ "optional": true,
402
+ "os": [
403
+ "darwin"
404
+ ],
405
+ "engines": {
406
+ "node": ">= 10"
407
+ }
408
+ },
409
+ "node_modules/@next/swc-darwin-x64": {
410
+ "version": "15.1.4",
411
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.4.tgz",
412
+ "integrity": "sha512-7sgf5rM7Z81V9w48F02Zz6DgEJulavC0jadab4ZsJ+K2sxMNK0/BtF8J8J3CxnsJN3DGcIdC260wEKssKTukUw==",
413
+ "cpu": [
414
+ "x64"
415
+ ],
416
+ "license": "MIT",
417
+ "optional": true,
418
+ "os": [
419
+ "darwin"
420
+ ],
421
+ "engines": {
422
+ "node": ">= 10"
423
+ }
424
+ },
425
+ "node_modules/@next/swc-linux-arm64-gnu": {
426
+ "version": "15.1.4",
427
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.4.tgz",
428
+ "integrity": "sha512-JaZlIMNaJenfd55kjaLWMfok+vWBlcRxqnRoZrhFQrhM1uAehP3R0+Aoe+bZOogqlZvAz53nY/k3ZyuKDtT2zQ==",
429
+ "cpu": [
430
+ "arm64"
431
+ ],
432
+ "license": "MIT",
433
+ "optional": true,
434
+ "os": [
435
+ "linux"
436
+ ],
437
+ "engines": {
438
+ "node": ">= 10"
439
+ }
440
+ },
441
+ "node_modules/@next/swc-linux-arm64-musl": {
442
+ "version": "15.1.4",
443
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.4.tgz",
444
+ "integrity": "sha512-7EBBjNoyTO2ipMDgCiORpwwOf5tIueFntKjcN3NK+GAQD7OzFJe84p7a2eQUeWdpzZvhVXuAtIen8QcH71ZCOQ==",
445
+ "cpu": [
446
+ "arm64"
447
+ ],
448
+ "license": "MIT",
449
+ "optional": true,
450
+ "os": [
451
+ "linux"
452
+ ],
453
+ "engines": {
454
+ "node": ">= 10"
455
+ }
456
+ },
457
+ "node_modules/@next/swc-linux-x64-gnu": {
458
+ "version": "15.1.4",
459
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.4.tgz",
460
+ "integrity": "sha512-9TGEgOycqZFuADyFqwmK/9g6S0FYZ3tphR4ebcmCwhL8Y12FW8pIBKJvSwV+UBjMkokstGNH+9F8F031JZKpHw==",
461
+ "cpu": [
462
+ "x64"
463
+ ],
464
+ "license": "MIT",
465
+ "optional": true,
466
+ "os": [
467
+ "linux"
468
+ ],
469
+ "engines": {
470
+ "node": ">= 10"
471
+ }
472
+ },
473
+ "node_modules/@next/swc-linux-x64-musl": {
474
+ "version": "15.1.4",
475
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.4.tgz",
476
+ "integrity": "sha512-0578bLRVDJOh+LdIoKvgNDz77+Bd85c5JrFgnlbI1SM3WmEQvsjxTA8ATu9Z9FCiIS/AliVAW2DV/BDwpXbtiQ==",
477
+ "cpu": [
478
+ "x64"
479
+ ],
480
+ "license": "MIT",
481
+ "optional": true,
482
+ "os": [
483
+ "linux"
484
+ ],
485
+ "engines": {
486
+ "node": ">= 10"
487
+ }
488
+ },
489
+ "node_modules/@next/swc-win32-arm64-msvc": {
490
+ "version": "15.1.4",
491
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.4.tgz",
492
+ "integrity": "sha512-JgFCiV4libQavwII+kncMCl30st0JVxpPOtzWcAI2jtum4HjYaclobKhj+JsRu5tFqMtA5CJIa0MvYyuu9xjjQ==",
493
+ "cpu": [
494
+ "arm64"
495
+ ],
496
+ "license": "MIT",
497
+ "optional": true,
498
+ "os": [
499
+ "win32"
500
+ ],
501
+ "engines": {
502
+ "node": ">= 10"
503
+ }
504
+ },
505
+ "node_modules/@next/swc-win32-x64-msvc": {
506
+ "version": "15.1.4",
507
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.4.tgz",
508
+ "integrity": "sha512-xxsJy9wzq7FR5SqPCUqdgSXiNXrMuidgckBa8nH9HtjjxsilgcN6VgXF6tZ3uEWuVEadotQJI8/9EQ6guTC4Yw==",
509
+ "cpu": [
510
+ "x64"
511
+ ],
512
+ "license": "MIT",
513
+ "optional": true,
514
+ "os": [
515
+ "win32"
516
+ ],
517
+ "engines": {
518
+ "node": ">= 10"
519
+ }
520
+ },
521
+ "node_modules/@swc/counter": {
522
+ "version": "0.1.3",
523
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
524
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
525
+ "license": "Apache-2.0"
526
+ },
527
+ "node_modules/@swc/helpers": {
528
+ "version": "0.5.15",
529
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
530
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
531
+ "license": "Apache-2.0",
532
+ "dependencies": {
533
+ "tslib": "^2.8.0"
534
+ }
535
+ },
536
+ "node_modules/busboy": {
537
+ "version": "1.6.0",
538
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
539
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
540
+ "dependencies": {
541
+ "streamsearch": "^1.1.0"
542
+ },
543
+ "engines": {
544
+ "node": ">=10.16.0"
545
+ }
546
+ },
547
+ "node_modules/caniuse-lite": {
548
+ "version": "1.0.30001692",
549
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
550
+ "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
551
+ "funding": [
552
+ {
553
+ "type": "opencollective",
554
+ "url": "https://opencollective.com/browserslist"
555
+ },
556
+ {
557
+ "type": "tidelift",
558
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
559
+ },
560
+ {
561
+ "type": "github",
562
+ "url": "https://github.com/sponsors/ai"
563
+ }
564
+ ],
565
+ "license": "CC-BY-4.0"
566
+ },
567
+ "node_modules/client-only": {
568
+ "version": "0.0.1",
569
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
570
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
571
+ "license": "MIT"
572
+ },
573
+ "node_modules/color": {
574
+ "version": "4.2.3",
575
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
576
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
577
+ "license": "MIT",
578
+ "optional": true,
579
+ "dependencies": {
580
+ "color-convert": "^2.0.1",
581
+ "color-string": "^1.9.0"
582
+ },
583
+ "engines": {
584
+ "node": ">=12.5.0"
585
+ }
586
+ },
587
+ "node_modules/color-convert": {
588
+ "version": "2.0.1",
589
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
590
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
591
+ "license": "MIT",
592
+ "optional": true,
593
+ "dependencies": {
594
+ "color-name": "~1.1.4"
595
+ },
596
+ "engines": {
597
+ "node": ">=7.0.0"
598
+ }
599
+ },
600
+ "node_modules/color-name": {
601
+ "version": "1.1.4",
602
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
603
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
604
+ "license": "MIT",
605
+ "optional": true
606
+ },
607
+ "node_modules/color-string": {
608
+ "version": "1.9.1",
609
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
610
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
611
+ "license": "MIT",
612
+ "optional": true,
613
+ "dependencies": {
614
+ "color-name": "^1.0.0",
615
+ "simple-swizzle": "^0.2.2"
616
+ }
617
+ },
618
+ "node_modules/detect-libc": {
619
+ "version": "2.0.3",
620
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
621
+ "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
622
+ "license": "Apache-2.0",
623
+ "optional": true,
624
+ "engines": {
625
+ "node": ">=8"
626
+ }
627
+ },
628
+ "node_modules/is-arrayish": {
629
+ "version": "0.3.2",
630
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
631
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
632
+ "license": "MIT",
633
+ "optional": true
634
+ },
635
+ "node_modules/nanoid": {
636
+ "version": "3.3.8",
637
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
638
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
639
+ "funding": [
640
+ {
641
+ "type": "github",
642
+ "url": "https://github.com/sponsors/ai"
643
+ }
644
+ ],
645
+ "license": "MIT",
646
+ "bin": {
647
+ "nanoid": "bin/nanoid.cjs"
648
+ },
649
+ "engines": {
650
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
651
+ }
652
+ },
653
+ "node_modules/next": {
654
+ "version": "15.1.4",
655
+ "resolved": "https://registry.npmjs.org/next/-/next-15.1.4.tgz",
656
+ "integrity": "sha512-mTaq9dwaSuwwOrcu3ebjDYObekkxRnXpuVL21zotM8qE2W0HBOdVIdg2Li9QjMEZrj73LN96LcWcz62V19FjAg==",
657
+ "license": "MIT",
658
+ "dependencies": {
659
+ "@next/env": "15.1.4",
660
+ "@swc/counter": "0.1.3",
661
+ "@swc/helpers": "0.5.15",
662
+ "busboy": "1.6.0",
663
+ "caniuse-lite": "^1.0.30001579",
664
+ "postcss": "8.4.31",
665
+ "styled-jsx": "5.1.6"
666
+ },
667
+ "bin": {
668
+ "next": "dist/bin/next"
669
+ },
670
+ "engines": {
671
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
672
+ },
673
+ "optionalDependencies": {
674
+ "@next/swc-darwin-arm64": "15.1.4",
675
+ "@next/swc-darwin-x64": "15.1.4",
676
+ "@next/swc-linux-arm64-gnu": "15.1.4",
677
+ "@next/swc-linux-arm64-musl": "15.1.4",
678
+ "@next/swc-linux-x64-gnu": "15.1.4",
679
+ "@next/swc-linux-x64-musl": "15.1.4",
680
+ "@next/swc-win32-arm64-msvc": "15.1.4",
681
+ "@next/swc-win32-x64-msvc": "15.1.4",
682
+ "sharp": "^0.33.5"
683
+ },
684
+ "peerDependencies": {
685
+ "@opentelemetry/api": "^1.1.0",
686
+ "@playwright/test": "^1.41.2",
687
+ "babel-plugin-react-compiler": "*",
688
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
689
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
690
+ "sass": "^1.3.0"
691
+ },
692
+ "peerDependenciesMeta": {
693
+ "@opentelemetry/api": {
694
+ "optional": true
695
+ },
696
+ "@playwright/test": {
697
+ "optional": true
698
+ },
699
+ "babel-plugin-react-compiler": {
700
+ "optional": true
701
+ },
702
+ "sass": {
703
+ "optional": true
704
+ }
705
+ }
706
+ },
707
+ "node_modules/picocolors": {
708
+ "version": "1.1.1",
709
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
710
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
711
+ "license": "ISC"
712
+ },
713
+ "node_modules/postcss": {
714
+ "version": "8.4.31",
715
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
716
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
717
+ "funding": [
718
+ {
719
+ "type": "opencollective",
720
+ "url": "https://opencollective.com/postcss/"
721
+ },
722
+ {
723
+ "type": "tidelift",
724
+ "url": "https://tidelift.com/funding/github/npm/postcss"
725
+ },
726
+ {
727
+ "type": "github",
728
+ "url": "https://github.com/sponsors/ai"
729
+ }
730
+ ],
731
+ "license": "MIT",
732
+ "dependencies": {
733
+ "nanoid": "^3.3.6",
734
+ "picocolors": "^1.0.0",
735
+ "source-map-js": "^1.0.2"
736
+ },
737
+ "engines": {
738
+ "node": "^10 || ^12 || >=14"
739
+ }
740
+ },
741
+ "node_modules/react": {
742
+ "version": "19.0.0",
743
+ "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
744
+ "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
745
+ "license": "MIT",
746
+ "engines": {
747
+ "node": ">=0.10.0"
748
+ }
749
+ },
750
+ "node_modules/react-dom": {
751
+ "version": "19.0.0",
752
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
753
+ "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
754
+ "license": "MIT",
755
+ "dependencies": {
756
+ "scheduler": "^0.25.0"
757
+ },
758
+ "peerDependencies": {
759
+ "react": "^19.0.0"
760
+ }
761
+ },
762
+ "node_modules/scheduler": {
763
+ "version": "0.25.0",
764
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
765
+ "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
766
+ "license": "MIT"
767
+ },
768
+ "node_modules/semver": {
769
+ "version": "7.6.3",
770
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
771
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
772
+ "license": "ISC",
773
+ "optional": true,
774
+ "bin": {
775
+ "semver": "bin/semver.js"
776
+ },
777
+ "engines": {
778
+ "node": ">=10"
779
+ }
780
+ },
781
+ "node_modules/sharp": {
782
+ "version": "0.33.5",
783
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
784
+ "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
785
+ "hasInstallScript": true,
786
+ "license": "Apache-2.0",
787
+ "optional": true,
788
+ "dependencies": {
789
+ "color": "^4.2.3",
790
+ "detect-libc": "^2.0.3",
791
+ "semver": "^7.6.3"
792
+ },
793
+ "engines": {
794
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
795
+ },
796
+ "funding": {
797
+ "url": "https://opencollective.com/libvips"
798
+ },
799
+ "optionalDependencies": {
800
+ "@img/sharp-darwin-arm64": "0.33.5",
801
+ "@img/sharp-darwin-x64": "0.33.5",
802
+ "@img/sharp-libvips-darwin-arm64": "1.0.4",
803
+ "@img/sharp-libvips-darwin-x64": "1.0.4",
804
+ "@img/sharp-libvips-linux-arm": "1.0.5",
805
+ "@img/sharp-libvips-linux-arm64": "1.0.4",
806
+ "@img/sharp-libvips-linux-s390x": "1.0.4",
807
+ "@img/sharp-libvips-linux-x64": "1.0.4",
808
+ "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
809
+ "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
810
+ "@img/sharp-linux-arm": "0.33.5",
811
+ "@img/sharp-linux-arm64": "0.33.5",
812
+ "@img/sharp-linux-s390x": "0.33.5",
813
+ "@img/sharp-linux-x64": "0.33.5",
814
+ "@img/sharp-linuxmusl-arm64": "0.33.5",
815
+ "@img/sharp-linuxmusl-x64": "0.33.5",
816
+ "@img/sharp-wasm32": "0.33.5",
817
+ "@img/sharp-win32-ia32": "0.33.5",
818
+ "@img/sharp-win32-x64": "0.33.5"
819
+ }
820
+ },
821
+ "node_modules/simple-swizzle": {
822
+ "version": "0.2.2",
823
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
824
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
825
+ "license": "MIT",
826
+ "optional": true,
827
+ "dependencies": {
828
+ "is-arrayish": "^0.3.1"
829
+ }
830
+ },
831
+ "node_modules/source-map-js": {
832
+ "version": "1.2.1",
833
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
834
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
835
+ "license": "BSD-3-Clause",
836
+ "engines": {
837
+ "node": ">=0.10.0"
838
+ }
839
+ },
840
+ "node_modules/streamsearch": {
841
+ "version": "1.1.0",
842
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
843
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
844
+ "engines": {
845
+ "node": ">=10.0.0"
846
+ }
847
+ },
848
+ "node_modules/styled-jsx": {
849
+ "version": "5.1.6",
850
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
851
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
852
+ "license": "MIT",
853
+ "dependencies": {
854
+ "client-only": "0.0.1"
855
+ },
856
+ "engines": {
857
+ "node": ">= 12.0.0"
858
+ },
859
+ "peerDependencies": {
860
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
861
+ },
862
+ "peerDependenciesMeta": {
863
+ "@babel/core": {
864
+ "optional": true
865
+ },
866
+ "babel-plugin-macros": {
867
+ "optional": true
868
+ }
869
+ }
870
+ },
871
+ "node_modules/tslib": {
872
+ "version": "2.8.1",
873
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
874
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
875
+ "license": "0BSD"
876
+ }
877
+ }
878
+ }
frontend/package.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "nex-com",
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
+ "react": "^19.0.0",
13
+ "react-dom": "^19.0.0",
14
+ "next": "15.1.4"
15
+ }
16
+ }
frontend/public/file.svg ADDED
frontend/public/globe.svg ADDED
frontend/public/next.svg ADDED
frontend/public/vercel.svg ADDED
frontend/public/window.svg ADDED