zobro-cli 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +74 -25
- package/package.json +2 -2
- package/shashi.txt +0 -386
package/index.js
CHANGED
|
@@ -1,26 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require('fs');
|
|
3
|
+
const fs = require('fs'); // Node.js File System
|
|
4
|
+
const path = require('path'); // Node.js Path module
|
|
4
5
|
const yargs = require('yargs/yargs');
|
|
5
6
|
const { hideBin } = require('yargs/helpers');
|
|
6
7
|
|
|
7
8
|
// ⚠️ Paste your official Google Gemini API key here.
|
|
8
9
|
const API_KEY = "AIzaSyAGMfEj6xZbqr3d48P-oDdjpe-85p7fws8";
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Generates a full project structure based on a prompt.
|
|
13
|
+
* @param {string} prompt - The user's project request.
|
|
14
|
+
* @param {string} directoryName - The name of the folder to create the project in.
|
|
15
|
+
*/
|
|
16
|
+
async function generateProject(prompt, directoryName) {
|
|
12
17
|
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${API_KEY}`;
|
|
13
18
|
|
|
19
|
+
// --- This is the new, "smarter" prompt ---
|
|
20
|
+
// We are asking the AI to act as a file structure generator
|
|
21
|
+
// and return a specific JSON format.
|
|
22
|
+
const newPrompt = `
|
|
23
|
+
You are an expert software developer and project scaffolder.
|
|
24
|
+
Based on the user's prompt, generate a complete file structure as a JSON array.
|
|
25
|
+
Each object in the array must have two keys:
|
|
26
|
+
1. "filename": (string) The full relative path for the file (e.g., "index.html", "src/styles.css", "js/app.js").
|
|
27
|
+
2. "code": (string) The complete code for that file.
|
|
28
|
+
|
|
29
|
+
Only return the raw JSON array, with no other text, explanations, or markdown fences.
|
|
30
|
+
|
|
31
|
+
User Prompt: "${prompt}"
|
|
32
|
+
`;
|
|
33
|
+
|
|
14
34
|
const body = {
|
|
15
|
-
contents: [{
|
|
16
|
-
parts: [{
|
|
17
|
-
text: `You are an expert code generation assistant. Provide only the code for the following prompt, without any extra explanation or markdown fences. Prompt: "${prompt}"`
|
|
18
|
-
}]
|
|
19
|
-
}]
|
|
35
|
+
contents: [{ parts: [{ text: newPrompt }] }]
|
|
20
36
|
};
|
|
21
37
|
|
|
22
38
|
try {
|
|
23
|
-
console.log(
|
|
39
|
+
console.log(`🤖 Generating project structure for "${prompt}"...`);
|
|
24
40
|
|
|
25
41
|
const response = await fetch(url, {
|
|
26
42
|
method: 'POST',
|
|
@@ -35,44 +51,77 @@ async function generateCode(prompt, outputFile) {
|
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
const data = await response.json();
|
|
38
|
-
|
|
54
|
+
let responseText = data.candidates[0].content.parts[0].text;
|
|
55
|
+
|
|
56
|
+
// --- NEW JSON PARSING & FILE CREATION LOGIC ---
|
|
57
|
+
console.log("🤖 AI response received. Building project...");
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} else {
|
|
44
|
-
console.log(code);
|
|
59
|
+
// Clean up potential markdown fences from the AI's response
|
|
60
|
+
if (responseText.startsWith("```json")) {
|
|
61
|
+
responseText = responseText.substring(7, responseText.length - 3).trim();
|
|
45
62
|
}
|
|
46
63
|
|
|
64
|
+
let files;
|
|
65
|
+
try {
|
|
66
|
+
files = JSON.parse(responseText);
|
|
67
|
+
if (!Array.isArray(files)) throw new Error("AI did not return a JSON array.");
|
|
68
|
+
} catch (parseError) {
|
|
69
|
+
console.error("❌ ERROR: Failed to parse the AI's response. The response was not valid JSON.");
|
|
70
|
+
console.error("Raw AI Response:", responseText);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Create the main project directory
|
|
75
|
+
fs.mkdirSync(directoryName, { recursive: true });
|
|
76
|
+
console.log(`📁 Created base directory: ${directoryName}`);
|
|
77
|
+
|
|
78
|
+
// Loop through the files array and create each file
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
const filePath = path.join(directoryName, file.filename);
|
|
81
|
+
const fileDir = path.dirname(filePath);
|
|
82
|
+
|
|
83
|
+
// Create subdirectories if they don't exist
|
|
84
|
+
if (!fs.existsSync(fileDir)) {
|
|
85
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Write the code to the file
|
|
89
|
+
fs.writeFileSync(filePath, file.code);
|
|
90
|
+
console.log(`✅ Created file: ${filePath}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(`\n🎉 Project "${directoryName}" created successfully!`);
|
|
94
|
+
|
|
47
95
|
} catch (error) {
|
|
48
96
|
console.error('❌ An error occurred:', error.message);
|
|
49
97
|
}
|
|
50
98
|
}
|
|
51
99
|
|
|
52
|
-
//
|
|
100
|
+
// --- NEW YARGS SETUP ---
|
|
53
101
|
yargs(hideBin(process.argv))
|
|
54
102
|
.command(
|
|
55
|
-
'$0 <prompt>',
|
|
56
|
-
'Generates
|
|
103
|
+
'$0 <prompt>', // The default command
|
|
104
|
+
'Generates a full project structure from a text prompt.',
|
|
57
105
|
(yargs) => {
|
|
58
106
|
return yargs
|
|
59
107
|
.positional('prompt', {
|
|
60
|
-
describe: 'The
|
|
108
|
+
describe: 'The project you want to generate',
|
|
61
109
|
type: 'string',
|
|
62
110
|
})
|
|
63
|
-
.option('
|
|
64
|
-
alias: '
|
|
65
|
-
describe: '
|
|
111
|
+
.option('directory', { // Replaces the old 'output' flag
|
|
112
|
+
alias: 'd',
|
|
113
|
+
describe: 'The name of the new directory to create the project in',
|
|
66
114
|
type: 'string',
|
|
115
|
+
demandOption: true, // This flag is now required
|
|
67
116
|
});
|
|
68
117
|
},
|
|
69
118
|
(argv) => {
|
|
70
119
|
if (!API_KEY || API_KEY === "YOUR_GEMINI_API_KEY_HERE") {
|
|
71
|
-
console.error('❌ ERROR: Please
|
|
120
|
+
console.error('❌ ERROR: Please add your API key to the index.js file.');
|
|
72
121
|
return;
|
|
73
122
|
}
|
|
74
|
-
|
|
123
|
+
generateProject(argv.prompt, argv.directory);
|
|
75
124
|
}
|
|
76
125
|
)
|
|
77
|
-
.demandCommand(1, '
|
|
126
|
+
.demandCommand(1, 'Please provide a prompt.')
|
|
78
127
|
.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zobro-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"zobro-cli": "./index.js"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"author": "Secondary chor",
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"type": "commonjs",
|
|
15
|
-
"description": "No cheat in exam for world",
|
|
15
|
+
"description": "No cheat in exam for world ",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"yargs": "^17.7.2"
|
|
18
18
|
}
|
package/shashi.txt
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
```javascript
|
|
2
|
-
// server.js
|
|
3
|
-
|
|
4
|
-
// --- 1. Import Dependencies ---
|
|
5
|
-
const express = require('express');
|
|
6
|
-
const session = require('express-session');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const multer = require('multer'); // For file uploads, though not strictly part of 'login' itself, included as requested.
|
|
9
|
-
|
|
10
|
-
// --- 2. Initialize Express App ---
|
|
11
|
-
const app = express();
|
|
12
|
-
const PORT = process.env.PORT || 3000;
|
|
13
|
-
|
|
14
|
-
// --- 3. Mock User Data (In-memory for simplicity) ---
|
|
15
|
-
// In a real application, this would come from a database.
|
|
16
|
-
const users = [
|
|
17
|
-
{ id: 1, username: 'user1', password: 'password1' },
|
|
18
|
-
{ id: 2, username: 'user2', password: 'password2' }
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
// --- 4. Configure Express Middleware ---
|
|
22
|
-
|
|
23
|
-
// Set EJS as the view engine
|
|
24
|
-
app.set('view engine', 'ejs');
|
|
25
|
-
app.set('views', path.join(__dirname, 'views'));
|
|
26
|
-
|
|
27
|
-
// Serve static files (e.g., CSS, JS, images)
|
|
28
|
-
app.use(express.static(path.join(__dirname, 'public')));
|
|
29
|
-
|
|
30
|
-
// Parse URL-encoded bodies (form data)
|
|
31
|
-
app.use(express.urlencoded({ extended: true }));
|
|
32
|
-
|
|
33
|
-
// Parse JSON bodies (for API requests if any)
|
|
34
|
-
app.use(express.json());
|
|
35
|
-
|
|
36
|
-
// Configure express-session
|
|
37
|
-
app.use(session({
|
|
38
|
-
secret: 'aVerySecretKeyForThisDemoApp!123', // Replace with a strong, random string in production
|
|
39
|
-
resave: false, // Don't save session if unmodified
|
|
40
|
-
saveUninitialized: false, // Don't create session until something is stored
|
|
41
|
-
cookie: {
|
|
42
|
-
maxAge: 1000 * 60 * 60 * 24, // Session lasts 1 day
|
|
43
|
-
httpOnly: true, // Prevent client-side JS from accessing the cookie
|
|
44
|
-
secure: false // Set to true if using HTTPS (for production)
|
|
45
|
-
}
|
|
46
|
-
}));
|
|
47
|
-
|
|
48
|
-
// Configure Multer for file uploads
|
|
49
|
-
// Note: This is a basic setup. For production, consider file size limits, types, and error handling.
|
|
50
|
-
const storage = multer.diskStorage({
|
|
51
|
-
destination: function (req, file, cb) {
|
|
52
|
-
cb(null, 'uploads/'); // Files will be saved in the 'uploads/' directory
|
|
53
|
-
},
|
|
54
|
-
filename: function (req, file, cb) {
|
|
55
|
-
cb(null, Date.now() + '-' + file.originalname);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const upload = multer({ storage: storage });
|
|
60
|
-
|
|
61
|
-
// Create 'uploads' directory if it doesn't exist
|
|
62
|
-
const fs = require('fs');
|
|
63
|
-
const uploadsDir = path.join(__dirname, 'uploads');
|
|
64
|
-
if (!fs.existsSync(uploadsDir)) {
|
|
65
|
-
fs.mkdirSync(uploadsDir);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// --- 5. Custom Middleware for Authentication Check ---
|
|
70
|
-
function isAuthenticated(req, res, next) {
|
|
71
|
-
if (req.session.userId) {
|
|
72
|
-
return next(); // User is authenticated, proceed to the next middleware/route handler
|
|
73
|
-
}
|
|
74
|
-
res.redirect('/login'); // User is not authenticated, redirect to login page
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// --- 6. Routes ---
|
|
78
|
-
|
|
79
|
-
// Default route - Redirect to login if not authenticated, otherwise to dashboard
|
|
80
|
-
app.get('/', (req, res) => {
|
|
81
|
-
if (req.session.userId) {
|
|
82
|
-
res.redirect('/dashboard');
|
|
83
|
-
} else {
|
|
84
|
-
res.redirect('/login');
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Login Page (GET)
|
|
89
|
-
app.get('/login', (req, res) => {
|
|
90
|
-
// If user is already logged in, redirect to dashboard
|
|
91
|
-
if (req.session.userId) {
|
|
92
|
-
return res.redirect('/dashboard');
|
|
93
|
-
}
|
|
94
|
-
res.render('login', { error: req.query.error });
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Login Submission (POST)
|
|
98
|
-
app.post('/login', (req, res) => {
|
|
99
|
-
const { username, password } = req.body;
|
|
100
|
-
|
|
101
|
-
const user = users.find(u => u.username === username && u.password === password);
|
|
102
|
-
|
|
103
|
-
if (user) {
|
|
104
|
-
req.session.userId = user.id; // Store user ID in session
|
|
105
|
-
req.session.username = user.username; // Store username in session
|
|
106
|
-
res.redirect('/dashboard');
|
|
107
|
-
} else {
|
|
108
|
-
res.redirect('/login?error=Invalid username or password');
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// Register Page (GET) - Optional, for demo purposes
|
|
113
|
-
app.get('/register', (req, res) => {
|
|
114
|
-
res.render('register', { error: req.query.error });
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Register Submission (POST) - Optional, for demo purposes
|
|
118
|
-
app.post('/register', (req, res) => {
|
|
119
|
-
const { username, password } = req.body;
|
|
120
|
-
|
|
121
|
-
if (!username || !password) {
|
|
122
|
-
return res.redirect('/register?error=Username and password are required');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (users.some(u => u.username === username)) {
|
|
126
|
-
return res.redirect('/register?error=Username already exists');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const newId = users.length > 0 ? Math.max(...users.map(u => u.id)) + 1 : 1;
|
|
130
|
-
users.push({ id: newId, username, password });
|
|
131
|
-
console.log('New user registered:', { id: newId, username, password });
|
|
132
|
-
res.redirect('/login?message=Registration successful, please login');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// Dashboard Page (GET) - Protected route
|
|
137
|
-
app.get('/dashboard', isAuthenticated, (req, res) => {
|
|
138
|
-
const currentUser = users.find(u => u.id === req.session.userId);
|
|
139
|
-
if (currentUser) {
|
|
140
|
-
res.render('dashboard', { username: currentUser.username });
|
|
141
|
-
} else {
|
|
142
|
-
// This case should ideally not happen if isAuthenticated works, but for safety
|
|
143
|
-
req.session.destroy(() => {
|
|
144
|
-
res.redirect('/login?error=Session corrupted, please login again.');
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Logout (POST)
|
|
150
|
-
app.post('/logout', (req, res) => {
|
|
151
|
-
req.session.destroy(err => {
|
|
152
|
-
if (err) {
|
|
153
|
-
console.error('Error destroying session:', err);
|
|
154
|
-
return res.redirect('/dashboard'); // Or handle error
|
|
155
|
-
}
|
|
156
|
-
res.redirect('/login?message=Logged out successfully');
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Example route for file upload using multer (not directly tied to login, but demonstrates multer)
|
|
161
|
-
app.post('/upload-profile-picture', isAuthenticated, upload.single('profilePicture'), (req, res) => {
|
|
162
|
-
if (!req.file) {
|
|
163
|
-
return res.status(400).send('No file uploaded.');
|
|
164
|
-
}
|
|
165
|
-
// Here you would save the file path to the user's profile in your database
|
|
166
|
-
console.log(`File uploaded by user ${req.session.username}:`, req.file.path);
|
|
167
|
-
res.send(`File uploaded successfully! Path: ${req.file.path}`);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// --- 7. Start the Server ---
|
|
172
|
-
app.listen(PORT, () => {
|
|
173
|
-
console.log(`Server is running on http://localhost:${PORT}`);
|
|
174
|
-
console.log('Try logging in with user: user1, pass: password1');
|
|
175
|
-
});
|
|
176
|
-
```
|
|
177
|
-
```html
|
|
178
|
-
<!-- views/login.ejs -->
|
|
179
|
-
<!DOCTYPE html>
|
|
180
|
-
<html lang="en">
|
|
181
|
-
<head>
|
|
182
|
-
<meta charset="UTF-8">
|
|
183
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
184
|
-
<title>Login</title>
|
|
185
|
-
<link rel="stylesheet" href="/style.css">
|
|
186
|
-
</head>
|
|
187
|
-
<body>
|
|
188
|
-
<div class="container">
|
|
189
|
-
<h2>Login</h2>
|
|
190
|
-
<% if (error) { %>
|
|
191
|
-
<p class="error-message"><%= error %></p>
|
|
192
|
-
<% } %>
|
|
193
|
-
<% if (typeof message !== 'undefined' && message) { %>
|
|
194
|
-
<p class="success-message"><%= message %></p>
|
|
195
|
-
<% } %>
|
|
196
|
-
<form action="/login" method="POST">
|
|
197
|
-
<div class="form-group">
|
|
198
|
-
<label for="username">Username:</label>
|
|
199
|
-
<input type="text" id="username" name="username" required>
|
|
200
|
-
</div>
|
|
201
|
-
<div class="form-group">
|
|
202
|
-
<label for="password">Password:</label>
|
|
203
|
-
<input type="password" id="password" name="password" required>
|
|
204
|
-
</div>
|
|
205
|
-
<button type="submit">Login</button>
|
|
206
|
-
</form>
|
|
207
|
-
<p>Don't have an account? <a href="/register">Register here</a>.</p>
|
|
208
|
-
</div>
|
|
209
|
-
</body>
|
|
210
|
-
</html>
|
|
211
|
-
```
|
|
212
|
-
```html
|
|
213
|
-
<!-- views/register.ejs -->
|
|
214
|
-
<!DOCTYPE html>
|
|
215
|
-
<html lang="en">
|
|
216
|
-
<head>
|
|
217
|
-
<meta charset="UTF-8">
|
|
218
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
219
|
-
<title>Register</title>
|
|
220
|
-
<link rel="stylesheet" href="/style.css">
|
|
221
|
-
</head>
|
|
222
|
-
<body>
|
|
223
|
-
<div class="container">
|
|
224
|
-
<h2>Register</h2>
|
|
225
|
-
<% if (error) { %>
|
|
226
|
-
<p class="error-message"><%= error %></p>
|
|
227
|
-
<% } %>
|
|
228
|
-
<form action="/register" method="POST">
|
|
229
|
-
<div class="form-group">
|
|
230
|
-
<label for="username">Username:</label>
|
|
231
|
-
<input type="text" id="username" name="username" required>
|
|
232
|
-
</div>
|
|
233
|
-
<div class="form-group">
|
|
234
|
-
<label for="password">Password:</label>
|
|
235
|
-
<input type="password" id="password" name="password" required>
|
|
236
|
-
</div>
|
|
237
|
-
<button type="submit">Register</button>
|
|
238
|
-
</form>
|
|
239
|
-
<p>Already have an account? <a href="/login">Login here</a>.</p>
|
|
240
|
-
</div>
|
|
241
|
-
</body>
|
|
242
|
-
</html>
|
|
243
|
-
```
|
|
244
|
-
```html
|
|
245
|
-
<!-- views/dashboard.ejs -->
|
|
246
|
-
<!DOCTYPE html>
|
|
247
|
-
<html lang="en">
|
|
248
|
-
<head>
|
|
249
|
-
<meta charset="UTF-8">
|
|
250
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
251
|
-
<title>Dashboard</title>
|
|
252
|
-
<link rel="stylesheet" href="/style.css">
|
|
253
|
-
</head>
|
|
254
|
-
<body>
|
|
255
|
-
<div class="container">
|
|
256
|
-
<h2>Welcome to the Dashboard, <%= username %>!</h2>
|
|
257
|
-
<p>This is a protected page, accessible only after successful login.</p>
|
|
258
|
-
|
|
259
|
-
<h3>Upload Profile Picture</h3>
|
|
260
|
-
<form action="/upload-profile-picture" method="POST" enctype="multipart/form-data">
|
|
261
|
-
<div class="form-group">
|
|
262
|
-
<label for="profilePicture">Select an image:</label>
|
|
263
|
-
<input type="file" id="profilePicture" name="profilePicture" accept="image/*" required>
|
|
264
|
-
</div>
|
|
265
|
-
<button type="submit">Upload</button>
|
|
266
|
-
</form>
|
|
267
|
-
|
|
268
|
-
<form action="/logout" method="POST" class="logout-form">
|
|
269
|
-
<button type="submit">Logout</button>
|
|
270
|
-
</form>
|
|
271
|
-
</div>
|
|
272
|
-
</body>
|
|
273
|
-
</html>
|
|
274
|
-
```
|
|
275
|
-
```css
|
|
276
|
-
/* public/style.css */
|
|
277
|
-
body {
|
|
278
|
-
font-family: Arial, sans-serif;
|
|
279
|
-
background-color: #f4f4f4;
|
|
280
|
-
display: flex;
|
|
281
|
-
justify-content: center;
|
|
282
|
-
align-items: center;
|
|
283
|
-
min-height: 100vh;
|
|
284
|
-
margin: 0;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
.container {
|
|
288
|
-
background-color: #fff;
|
|
289
|
-
padding: 30px;
|
|
290
|
-
border-radius: 8px;
|
|
291
|
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
292
|
-
width: 100%;
|
|
293
|
-
max-width: 400px;
|
|
294
|
-
text-align: center;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
h2 {
|
|
298
|
-
color: #333;
|
|
299
|
-
margin-bottom: 20px;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
.form-group {
|
|
303
|
-
margin-bottom: 15px;
|
|
304
|
-
text-align: left;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
label {
|
|
308
|
-
display: block;
|
|
309
|
-
margin-bottom: 5px;
|
|
310
|
-
color: #555;
|
|
311
|
-
font-weight: bold;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
input[type="text"],
|
|
315
|
-
input[type="password"],
|
|
316
|
-
input[type="file"] {
|
|
317
|
-
width: calc(100% - 20px);
|
|
318
|
-
padding: 10px;
|
|
319
|
-
border: 1px solid #ddd;
|
|
320
|
-
border-radius: 4px;
|
|
321
|
-
font-size: 16px;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
button[type="submit"] {
|
|
325
|
-
background-color: #007bff;
|
|
326
|
-
color: white;
|
|
327
|
-
padding: 10px 15px;
|
|
328
|
-
border: none;
|
|
329
|
-
border-radius: 4px;
|
|
330
|
-
font-size: 16px;
|
|
331
|
-
cursor: pointer;
|
|
332
|
-
transition: background-color 0.3s ease;
|
|
333
|
-
width: 100%;
|
|
334
|
-
margin-top: 10px;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
button[type="submit"]:hover {
|
|
338
|
-
background-color: #0056b3;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.error-message {
|
|
342
|
-
color: #dc3545;
|
|
343
|
-
background-color: #f8d7da;
|
|
344
|
-
border: 1px solid #f5c6cb;
|
|
345
|
-
padding: 10px;
|
|
346
|
-
border-radius: 4px;
|
|
347
|
-
margin-bottom: 15px;
|
|
348
|
-
font-size: 0.9em;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.success-message {
|
|
352
|
-
color: #28a745;
|
|
353
|
-
background-color: #d4edda;
|
|
354
|
-
border: 1px solid #c3e6cb;
|
|
355
|
-
padding: 10px;
|
|
356
|
-
border-radius: 4px;
|
|
357
|
-
margin-bottom: 15px;
|
|
358
|
-
font-size: 0.9em;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
p {
|
|
362
|
-
margin-top: 20px;
|
|
363
|
-
color: #666;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
a {
|
|
367
|
-
color: #007bff;
|
|
368
|
-
text-decoration: none;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
a:hover {
|
|
372
|
-
text-decoration: underline;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
.logout-form {
|
|
376
|
-
margin-top: 30px;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
.logout-form button {
|
|
380
|
-
background-color: #6c757d;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
.logout-form button:hover {
|
|
384
|
-
background-color: #5a6268;
|
|
385
|
-
}
|
|
386
|
-
```
|