Spaces:
Running
Running
fixing the UI issues
Browse files- app/api/claude/generate-flutter/route.ts +0 -716
- app/api/claude/generate-latex/route.ts +0 -650
- app/components/Desktop.tsx +31 -12
- app/components/Dock.tsx +3 -2
- app/components/DraggableDesktopIcon.tsx +29 -10
- app/components/FileManager.tsx +46 -8
- app/components/FlutterCodeEditor.tsx +26 -18
- app/components/FlutterRunner.tsx +10 -65
- app/components/LaTeXEditor.tsx +251 -512
- app/components/SpotlightSearch.tsx +25 -11
app/api/claude/generate-flutter/route.ts
DELETED
|
@@ -1,716 +0,0 @@
|
|
| 1 |
-
import { NextRequest, NextResponse } from 'next/server'
|
| 2 |
-
|
| 3 |
-
export async function POST(request: NextRequest) {
|
| 4 |
-
try {
|
| 5 |
-
const { prompt } = await request.json()
|
| 6 |
-
|
| 7 |
-
if (!prompt) {
|
| 8 |
-
return NextResponse.json(
|
| 9 |
-
{ error: 'Prompt is required' },
|
| 10 |
-
{ status: 400 }
|
| 11 |
-
)
|
| 12 |
-
}
|
| 13 |
-
|
| 14 |
-
// Check if we have an API key
|
| 15 |
-
const apiKey = process.env.ANTHROPIC_API_KEY
|
| 16 |
-
|
| 17 |
-
if (apiKey) {
|
| 18 |
-
// Call Claude API
|
| 19 |
-
try {
|
| 20 |
-
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
| 21 |
-
method: 'POST',
|
| 22 |
-
headers: {
|
| 23 |
-
'Content-Type': 'application/json',
|
| 24 |
-
'x-api-key': apiKey,
|
| 25 |
-
'anthropic-version': '2023-06-01'
|
| 26 |
-
},
|
| 27 |
-
body: JSON.stringify({
|
| 28 |
-
model: 'claude-3-haiku-20240307',
|
| 29 |
-
max_tokens: 2048,
|
| 30 |
-
messages: [
|
| 31 |
-
{
|
| 32 |
-
role: 'user',
|
| 33 |
-
content: `Generate clean, complete Flutter/Dart code for the following request.
|
| 34 |
-
Return ONLY the code without any explanations, markdown formatting, or comments about the code.
|
| 35 |
-
The code should be ready to run in DartPad.
|
| 36 |
-
|
| 37 |
-
Request: ${prompt}`
|
| 38 |
-
}
|
| 39 |
-
],
|
| 40 |
-
system: `You are a Flutter code generator. Generate clean, working Flutter/Dart code that can run directly in DartPad.
|
| 41 |
-
- Always include necessary imports
|
| 42 |
-
- Use Material Design components
|
| 43 |
-
- Ensure the code is complete and runnable
|
| 44 |
-
- Do not include pubspec.yaml as DartPad handles dependencies
|
| 45 |
-
- Do not include any explanations or markdown formatting
|
| 46 |
-
- Return only pure Dart/Flutter code`
|
| 47 |
-
})
|
| 48 |
-
})
|
| 49 |
-
|
| 50 |
-
if (response.ok) {
|
| 51 |
-
const data = await response.json()
|
| 52 |
-
const code = data.content[0].text
|
| 53 |
-
|
| 54 |
-
// Clean the code if it has any markdown formatting
|
| 55 |
-
const cleanedCode = code
|
| 56 |
-
.replace(/```dart\n?/g, '')
|
| 57 |
-
.replace(/```flutter\n?/g, '')
|
| 58 |
-
.replace(/```\n?/g, '')
|
| 59 |
-
.trim()
|
| 60 |
-
|
| 61 |
-
return NextResponse.json({ code: cleanedCode })
|
| 62 |
-
}
|
| 63 |
-
} catch (error) {
|
| 64 |
-
console.error('Claude API error:', error)
|
| 65 |
-
}
|
| 66 |
-
}
|
| 67 |
-
|
| 68 |
-
// Fallback: Generate mock Flutter code based on the prompt
|
| 69 |
-
const mockCode = generateMockFlutterCode(prompt)
|
| 70 |
-
return NextResponse.json({ code: mockCode })
|
| 71 |
-
|
| 72 |
-
} catch (error) {
|
| 73 |
-
console.error('Error generating Flutter code:', error)
|
| 74 |
-
return NextResponse.json(
|
| 75 |
-
{ error: 'Failed to generate Flutter code' },
|
| 76 |
-
{ status: 500 }
|
| 77 |
-
)
|
| 78 |
-
}
|
| 79 |
-
}
|
| 80 |
-
|
| 81 |
-
function generateMockFlutterCode(prompt: string): string {
|
| 82 |
-
// Extract key words from the prompt to customize the generated code
|
| 83 |
-
const promptLower = prompt.toLowerCase()
|
| 84 |
-
|
| 85 |
-
// Check for specific UI elements mentioned
|
| 86 |
-
const hasButton = promptLower.includes('button')
|
| 87 |
-
const hasList = promptLower.includes('list')
|
| 88 |
-
const hasForm = promptLower.includes('form') || promptLower.includes('input')
|
| 89 |
-
const hasCounter = promptLower.includes('counter')
|
| 90 |
-
const hasTabs = promptLower.includes('tab')
|
| 91 |
-
const hasDrawer = promptLower.includes('drawer') || promptLower.includes('menu')
|
| 92 |
-
|
| 93 |
-
// Generate appropriate code based on keywords
|
| 94 |
-
if (hasForm) {
|
| 95 |
-
return generateFormCode(prompt)
|
| 96 |
-
} else if (hasList) {
|
| 97 |
-
return generateListCode(prompt)
|
| 98 |
-
} else if (hasTabs) {
|
| 99 |
-
return generateTabsCode(prompt)
|
| 100 |
-
} else if (hasDrawer) {
|
| 101 |
-
return generateDrawerCode(prompt)
|
| 102 |
-
} else if (hasCounter) {
|
| 103 |
-
return generateCounterCode(prompt)
|
| 104 |
-
} else {
|
| 105 |
-
return generateBasicCode(prompt)
|
| 106 |
-
}
|
| 107 |
-
}
|
| 108 |
-
|
| 109 |
-
function generateBasicCode(prompt: string): string {
|
| 110 |
-
return `import 'package:flutter/material.dart';
|
| 111 |
-
|
| 112 |
-
void main() => runApp(MyApp());
|
| 113 |
-
|
| 114 |
-
class MyApp extends StatelessWidget {
|
| 115 |
-
@override
|
| 116 |
-
Widget build(BuildContext context) {
|
| 117 |
-
return MaterialApp(
|
| 118 |
-
title: '${prompt}',
|
| 119 |
-
theme: ThemeData(
|
| 120 |
-
primarySwatch: Colors.blue,
|
| 121 |
-
visualDensity: VisualDensity.adaptivePlatformDensity,
|
| 122 |
-
),
|
| 123 |
-
home: HomePage(),
|
| 124 |
-
);
|
| 125 |
-
}
|
| 126 |
-
}
|
| 127 |
-
|
| 128 |
-
class HomePage extends StatelessWidget {
|
| 129 |
-
@override
|
| 130 |
-
Widget build(BuildContext context) {
|
| 131 |
-
return Scaffold(
|
| 132 |
-
appBar: AppBar(
|
| 133 |
-
title: Text('${prompt}'),
|
| 134 |
-
elevation: 0,
|
| 135 |
-
),
|
| 136 |
-
body: Center(
|
| 137 |
-
child: Column(
|
| 138 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 139 |
-
children: [
|
| 140 |
-
Icon(
|
| 141 |
-
Icons.flutter_dash,
|
| 142 |
-
size: 100,
|
| 143 |
-
color: Colors.blue,
|
| 144 |
-
),
|
| 145 |
-
SizedBox(height: 20),
|
| 146 |
-
Text(
|
| 147 |
-
'${prompt}',
|
| 148 |
-
style: Theme.of(context).textTheme.headline4,
|
| 149 |
-
textAlign: TextAlign.center,
|
| 150 |
-
),
|
| 151 |
-
SizedBox(height: 20),
|
| 152 |
-
ElevatedButton(
|
| 153 |
-
onPressed: () {
|
| 154 |
-
ScaffoldMessenger.of(context).showSnackBar(
|
| 155 |
-
SnackBar(content: Text('Button clicked!')),
|
| 156 |
-
);
|
| 157 |
-
},
|
| 158 |
-
child: Text('Get Started'),
|
| 159 |
-
),
|
| 160 |
-
],
|
| 161 |
-
),
|
| 162 |
-
),
|
| 163 |
-
);
|
| 164 |
-
}
|
| 165 |
-
}`
|
| 166 |
-
}
|
| 167 |
-
|
| 168 |
-
function generateCounterCode(prompt: string): string {
|
| 169 |
-
return `import 'package:flutter/material.dart';
|
| 170 |
-
|
| 171 |
-
void main() => runApp(MyApp());
|
| 172 |
-
|
| 173 |
-
class MyApp extends StatelessWidget {
|
| 174 |
-
@override
|
| 175 |
-
Widget build(BuildContext context) {
|
| 176 |
-
return MaterialApp(
|
| 177 |
-
title: 'Counter App',
|
| 178 |
-
theme: ThemeData(
|
| 179 |
-
primarySwatch: Colors.purple,
|
| 180 |
-
),
|
| 181 |
-
home: CounterPage(),
|
| 182 |
-
);
|
| 183 |
-
}
|
| 184 |
-
}
|
| 185 |
-
|
| 186 |
-
class CounterPage extends StatefulWidget {
|
| 187 |
-
@override
|
| 188 |
-
_CounterPageState createState() => _CounterPageState();
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
class _CounterPageState extends State<CounterPage> {
|
| 192 |
-
int _counter = 0;
|
| 193 |
-
|
| 194 |
-
void _incrementCounter() {
|
| 195 |
-
setState(() {
|
| 196 |
-
_counter++;
|
| 197 |
-
});
|
| 198 |
-
}
|
| 199 |
-
|
| 200 |
-
void _decrementCounter() {
|
| 201 |
-
setState(() {
|
| 202 |
-
_counter--;
|
| 203 |
-
});
|
| 204 |
-
}
|
| 205 |
-
|
| 206 |
-
void _resetCounter() {
|
| 207 |
-
setState(() {
|
| 208 |
-
_counter = 0;
|
| 209 |
-
});
|
| 210 |
-
}
|
| 211 |
-
|
| 212 |
-
@override
|
| 213 |
-
Widget build(BuildContext context) {
|
| 214 |
-
return Scaffold(
|
| 215 |
-
appBar: AppBar(
|
| 216 |
-
title: Text('${prompt}'),
|
| 217 |
-
),
|
| 218 |
-
body: Center(
|
| 219 |
-
child: Column(
|
| 220 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 221 |
-
children: [
|
| 222 |
-
Text(
|
| 223 |
-
'Counter Value:',
|
| 224 |
-
style: TextStyle(fontSize: 20),
|
| 225 |
-
),
|
| 226 |
-
Text(
|
| 227 |
-
'$_counter',
|
| 228 |
-
style: TextStyle(fontSize: 72, fontWeight: FontWeight.bold),
|
| 229 |
-
),
|
| 230 |
-
SizedBox(height: 40),
|
| 231 |
-
Row(
|
| 232 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 233 |
-
children: [
|
| 234 |
-
FloatingActionButton(
|
| 235 |
-
onPressed: _decrementCounter,
|
| 236 |
-
tooltip: 'Decrement',
|
| 237 |
-
child: Icon(Icons.remove),
|
| 238 |
-
),
|
| 239 |
-
SizedBox(width: 20),
|
| 240 |
-
FloatingActionButton(
|
| 241 |
-
onPressed: _resetCounter,
|
| 242 |
-
tooltip: 'Reset',
|
| 243 |
-
child: Icon(Icons.refresh),
|
| 244 |
-
),
|
| 245 |
-
SizedBox(width: 20),
|
| 246 |
-
FloatingActionButton(
|
| 247 |
-
onPressed: _incrementCounter,
|
| 248 |
-
tooltip: 'Increment',
|
| 249 |
-
child: Icon(Icons.add),
|
| 250 |
-
),
|
| 251 |
-
],
|
| 252 |
-
),
|
| 253 |
-
],
|
| 254 |
-
),
|
| 255 |
-
),
|
| 256 |
-
);
|
| 257 |
-
}
|
| 258 |
-
}`
|
| 259 |
-
}
|
| 260 |
-
|
| 261 |
-
function generateListCode(prompt: string): string {
|
| 262 |
-
return `import 'package:flutter/material.dart';
|
| 263 |
-
|
| 264 |
-
void main() => runApp(MyApp());
|
| 265 |
-
|
| 266 |
-
class MyApp extends StatelessWidget {
|
| 267 |
-
@override
|
| 268 |
-
Widget build(BuildContext context) {
|
| 269 |
-
return MaterialApp(
|
| 270 |
-
title: 'List App',
|
| 271 |
-
theme: ThemeData(
|
| 272 |
-
primarySwatch: Colors.indigo,
|
| 273 |
-
),
|
| 274 |
-
home: ListPage(),
|
| 275 |
-
);
|
| 276 |
-
}
|
| 277 |
-
}
|
| 278 |
-
|
| 279 |
-
class ListPage extends StatefulWidget {
|
| 280 |
-
@override
|
| 281 |
-
_ListPageState createState() => _ListPageState();
|
| 282 |
-
}
|
| 283 |
-
|
| 284 |
-
class _ListPageState extends State<ListPage> {
|
| 285 |
-
final List<String> items = List.generate(20, (i) => 'Item \${i + 1}');
|
| 286 |
-
|
| 287 |
-
@override
|
| 288 |
-
Widget build(BuildContext context) {
|
| 289 |
-
return Scaffold(
|
| 290 |
-
appBar: AppBar(
|
| 291 |
-
title: Text('${prompt}'),
|
| 292 |
-
actions: [
|
| 293 |
-
IconButton(
|
| 294 |
-
icon: Icon(Icons.search),
|
| 295 |
-
onPressed: () {},
|
| 296 |
-
),
|
| 297 |
-
],
|
| 298 |
-
),
|
| 299 |
-
body: ListView.builder(
|
| 300 |
-
itemCount: items.length,
|
| 301 |
-
itemBuilder: (context, index) {
|
| 302 |
-
return Card(
|
| 303 |
-
margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
| 304 |
-
child: ListTile(
|
| 305 |
-
leading: CircleAvatar(
|
| 306 |
-
child: Text('\${index + 1}'),
|
| 307 |
-
backgroundColor: Colors.indigo,
|
| 308 |
-
),
|
| 309 |
-
title: Text(items[index]),
|
| 310 |
-
subtitle: Text('Description for item \${index + 1}'),
|
| 311 |
-
trailing: Icon(Icons.arrow_forward_ios),
|
| 312 |
-
onTap: () {
|
| 313 |
-
ScaffoldMessenger.of(context).showSnackBar(
|
| 314 |
-
SnackBar(content: Text('Tapped on \${items[index]}')),
|
| 315 |
-
);
|
| 316 |
-
},
|
| 317 |
-
),
|
| 318 |
-
);
|
| 319 |
-
},
|
| 320 |
-
),
|
| 321 |
-
floatingActionButton: FloatingActionButton(
|
| 322 |
-
onPressed: () {
|
| 323 |
-
setState(() {
|
| 324 |
-
items.add('Item \${items.length + 1}');
|
| 325 |
-
});
|
| 326 |
-
},
|
| 327 |
-
child: Icon(Icons.add),
|
| 328 |
-
),
|
| 329 |
-
);
|
| 330 |
-
}
|
| 331 |
-
}`
|
| 332 |
-
}
|
| 333 |
-
|
| 334 |
-
function generateFormCode(prompt: string): string {
|
| 335 |
-
return `import 'package:flutter/material.dart';
|
| 336 |
-
|
| 337 |
-
void main() => runApp(MyApp());
|
| 338 |
-
|
| 339 |
-
class MyApp extends StatelessWidget {
|
| 340 |
-
@override
|
| 341 |
-
Widget build(BuildContext context) {
|
| 342 |
-
return MaterialApp(
|
| 343 |
-
title: 'Form App',
|
| 344 |
-
theme: ThemeData(
|
| 345 |
-
primarySwatch: Colors.teal,
|
| 346 |
-
),
|
| 347 |
-
home: FormPage(),
|
| 348 |
-
);
|
| 349 |
-
}
|
| 350 |
-
}
|
| 351 |
-
|
| 352 |
-
class FormPage extends StatefulWidget {
|
| 353 |
-
@override
|
| 354 |
-
_FormPageState createState() => _FormPageState();
|
| 355 |
-
}
|
| 356 |
-
|
| 357 |
-
class _FormPageState extends State<FormPage> {
|
| 358 |
-
final _formKey = GlobalKey<FormState>();
|
| 359 |
-
final _nameController = TextEditingController();
|
| 360 |
-
final _emailController = TextEditingController();
|
| 361 |
-
final _messageController = TextEditingController();
|
| 362 |
-
bool _agreeToTerms = false;
|
| 363 |
-
|
| 364 |
-
@override
|
| 365 |
-
Widget build(BuildContext context) {
|
| 366 |
-
return Scaffold(
|
| 367 |
-
appBar: AppBar(
|
| 368 |
-
title: Text('${prompt}'),
|
| 369 |
-
),
|
| 370 |
-
body: SingleChildScrollView(
|
| 371 |
-
padding: EdgeInsets.all(16),
|
| 372 |
-
child: Form(
|
| 373 |
-
key: _formKey,
|
| 374 |
-
child: Column(
|
| 375 |
-
crossAxisAlignment: CrossAxisAlignment.stretch,
|
| 376 |
-
children: [
|
| 377 |
-
TextFormField(
|
| 378 |
-
controller: _nameController,
|
| 379 |
-
decoration: InputDecoration(
|
| 380 |
-
labelText: 'Name',
|
| 381 |
-
prefixIcon: Icon(Icons.person),
|
| 382 |
-
border: OutlineInputBorder(),
|
| 383 |
-
),
|
| 384 |
-
validator: (value) {
|
| 385 |
-
if (value == null || value.isEmpty) {
|
| 386 |
-
return 'Please enter your name';
|
| 387 |
-
}
|
| 388 |
-
return null;
|
| 389 |
-
},
|
| 390 |
-
),
|
| 391 |
-
SizedBox(height: 16),
|
| 392 |
-
TextFormField(
|
| 393 |
-
controller: _emailController,
|
| 394 |
-
decoration: InputDecoration(
|
| 395 |
-
labelText: 'Email',
|
| 396 |
-
prefixIcon: Icon(Icons.email),
|
| 397 |
-
border: OutlineInputBorder(),
|
| 398 |
-
),
|
| 399 |
-
keyboardType: TextInputType.emailAddress,
|
| 400 |
-
validator: (value) {
|
| 401 |
-
if (value == null || value.isEmpty) {
|
| 402 |
-
return 'Please enter your email';
|
| 403 |
-
}
|
| 404 |
-
if (!value.contains('@')) {
|
| 405 |
-
return 'Please enter a valid email';
|
| 406 |
-
}
|
| 407 |
-
return null;
|
| 408 |
-
},
|
| 409 |
-
),
|
| 410 |
-
SizedBox(height: 16),
|
| 411 |
-
TextFormField(
|
| 412 |
-
controller: _messageController,
|
| 413 |
-
decoration: InputDecoration(
|
| 414 |
-
labelText: 'Message',
|
| 415 |
-
prefixIcon: Icon(Icons.message),
|
| 416 |
-
border: OutlineInputBorder(),
|
| 417 |
-
),
|
| 418 |
-
maxLines: 4,
|
| 419 |
-
validator: (value) {
|
| 420 |
-
if (value == null || value.isEmpty) {
|
| 421 |
-
return 'Please enter a message';
|
| 422 |
-
}
|
| 423 |
-
return null;
|
| 424 |
-
},
|
| 425 |
-
),
|
| 426 |
-
SizedBox(height: 16),
|
| 427 |
-
CheckboxListTile(
|
| 428 |
-
title: Text('I agree to the terms and conditions'),
|
| 429 |
-
value: _agreeToTerms,
|
| 430 |
-
onChanged: (value) {
|
| 431 |
-
setState(() {
|
| 432 |
-
_agreeToTerms = value ?? false;
|
| 433 |
-
});
|
| 434 |
-
},
|
| 435 |
-
),
|
| 436 |
-
SizedBox(height: 24),
|
| 437 |
-
ElevatedButton(
|
| 438 |
-
onPressed: () {
|
| 439 |
-
if (_formKey.currentState!.validate() && _agreeToTerms) {
|
| 440 |
-
ScaffoldMessenger.of(context).showSnackBar(
|
| 441 |
-
SnackBar(
|
| 442 |
-
content: Text('Form submitted successfully!'),
|
| 443 |
-
backgroundColor: Colors.green,
|
| 444 |
-
),
|
| 445 |
-
);
|
| 446 |
-
} else if (!_agreeToTerms) {
|
| 447 |
-
ScaffoldMessenger.of(context).showSnackBar(
|
| 448 |
-
SnackBar(
|
| 449 |
-
content: Text('Please agree to terms and conditions'),
|
| 450 |
-
backgroundColor: Colors.red,
|
| 451 |
-
),
|
| 452 |
-
);
|
| 453 |
-
}
|
| 454 |
-
},
|
| 455 |
-
child: Padding(
|
| 456 |
-
padding: EdgeInsets.all(16),
|
| 457 |
-
child: Text('Submit'),
|
| 458 |
-
),
|
| 459 |
-
),
|
| 460 |
-
],
|
| 461 |
-
),
|
| 462 |
-
),
|
| 463 |
-
),
|
| 464 |
-
);
|
| 465 |
-
}
|
| 466 |
-
|
| 467 |
-
@override
|
| 468 |
-
void dispose() {
|
| 469 |
-
_nameController.dispose();
|
| 470 |
-
_emailController.dispose();
|
| 471 |
-
_messageController.dispose();
|
| 472 |
-
super.dispose();
|
| 473 |
-
}
|
| 474 |
-
}`
|
| 475 |
-
}
|
| 476 |
-
|
| 477 |
-
function generateTabsCode(prompt: string): string {
|
| 478 |
-
return `import 'package:flutter/material.dart';
|
| 479 |
-
|
| 480 |
-
void main() => runApp(MyApp());
|
| 481 |
-
|
| 482 |
-
class MyApp extends StatelessWidget {
|
| 483 |
-
@override
|
| 484 |
-
Widget build(BuildContext context) {
|
| 485 |
-
return MaterialApp(
|
| 486 |
-
title: 'Tabs App',
|
| 487 |
-
theme: ThemeData(
|
| 488 |
-
primarySwatch: Colors.deepPurple,
|
| 489 |
-
),
|
| 490 |
-
home: TabsPage(),
|
| 491 |
-
);
|
| 492 |
-
}
|
| 493 |
-
}
|
| 494 |
-
|
| 495 |
-
class TabsPage extends StatelessWidget {
|
| 496 |
-
@override
|
| 497 |
-
Widget build(BuildContext context) {
|
| 498 |
-
return DefaultTabController(
|
| 499 |
-
length: 3,
|
| 500 |
-
child: Scaffold(
|
| 501 |
-
appBar: AppBar(
|
| 502 |
-
title: Text('${prompt}'),
|
| 503 |
-
bottom: TabBar(
|
| 504 |
-
tabs: [
|
| 505 |
-
Tab(icon: Icon(Icons.home), text: 'Home'),
|
| 506 |
-
Tab(icon: Icon(Icons.star), text: 'Favorites'),
|
| 507 |
-
Tab(icon: Icon(Icons.settings), text: 'Settings'),
|
| 508 |
-
],
|
| 509 |
-
),
|
| 510 |
-
),
|
| 511 |
-
body: TabBarView(
|
| 512 |
-
children: [
|
| 513 |
-
// Home Tab
|
| 514 |
-
Center(
|
| 515 |
-
child: Column(
|
| 516 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 517 |
-
children: [
|
| 518 |
-
Icon(Icons.home, size: 100, color: Colors.deepPurple),
|
| 519 |
-
SizedBox(height: 20),
|
| 520 |
-
Text('Home Page', style: TextStyle(fontSize: 24)),
|
| 521 |
-
],
|
| 522 |
-
),
|
| 523 |
-
),
|
| 524 |
-
// Favorites Tab
|
| 525 |
-
ListView.builder(
|
| 526 |
-
itemCount: 10,
|
| 527 |
-
itemBuilder: (context, index) {
|
| 528 |
-
return ListTile(
|
| 529 |
-
leading: Icon(Icons.star, color: Colors.amber),
|
| 530 |
-
title: Text('Favorite Item \${index + 1}'),
|
| 531 |
-
subtitle: Text('This is your favorite item'),
|
| 532 |
-
);
|
| 533 |
-
},
|
| 534 |
-
),
|
| 535 |
-
// Settings Tab
|
| 536 |
-
ListView(
|
| 537 |
-
children: [
|
| 538 |
-
SwitchListTile(
|
| 539 |
-
title: Text('Enable Notifications'),
|
| 540 |
-
subtitle: Text('Receive push notifications'),
|
| 541 |
-
value: true,
|
| 542 |
-
onChanged: (value) {},
|
| 543 |
-
),
|
| 544 |
-
ListTile(
|
| 545 |
-
leading: Icon(Icons.language),
|
| 546 |
-
title: Text('Language'),
|
| 547 |
-
subtitle: Text('English'),
|
| 548 |
-
trailing: Icon(Icons.arrow_forward_ios),
|
| 549 |
-
),
|
| 550 |
-
ListTile(
|
| 551 |
-
leading: Icon(Icons.dark_mode),
|
| 552 |
-
title: Text('Dark Mode'),
|
| 553 |
-
subtitle: Text('Toggle dark theme'),
|
| 554 |
-
trailing: Switch(
|
| 555 |
-
value: false,
|
| 556 |
-
onChanged: (value) {},
|
| 557 |
-
),
|
| 558 |
-
),
|
| 559 |
-
ListTile(
|
| 560 |
-
leading: Icon(Icons.info),
|
| 561 |
-
title: Text('About'),
|
| 562 |
-
subtitle: Text('Version 1.0.0'),
|
| 563 |
-
),
|
| 564 |
-
],
|
| 565 |
-
),
|
| 566 |
-
],
|
| 567 |
-
),
|
| 568 |
-
),
|
| 569 |
-
);
|
| 570 |
-
}
|
| 571 |
-
}`
|
| 572 |
-
}
|
| 573 |
-
|
| 574 |
-
function generateDrawerCode(prompt: string): string {
|
| 575 |
-
return `import 'package:flutter/material.dart';
|
| 576 |
-
|
| 577 |
-
void main() => runApp(MyApp());
|
| 578 |
-
|
| 579 |
-
class MyApp extends StatelessWidget {
|
| 580 |
-
@override
|
| 581 |
-
Widget build(BuildContext context) {
|
| 582 |
-
return MaterialApp(
|
| 583 |
-
title: 'Navigation Drawer App',
|
| 584 |
-
theme: ThemeData(
|
| 585 |
-
primarySwatch: Colors.orange,
|
| 586 |
-
),
|
| 587 |
-
home: DrawerPage(),
|
| 588 |
-
);
|
| 589 |
-
}
|
| 590 |
-
}
|
| 591 |
-
|
| 592 |
-
class DrawerPage extends StatefulWidget {
|
| 593 |
-
@override
|
| 594 |
-
_DrawerPageState createState() => _DrawerPageState();
|
| 595 |
-
}
|
| 596 |
-
|
| 597 |
-
class _DrawerPageState extends State<DrawerPage> {
|
| 598 |
-
int _selectedIndex = 0;
|
| 599 |
-
|
| 600 |
-
final List<Widget> _pages = [
|
| 601 |
-
Center(
|
| 602 |
-
child: Column(
|
| 603 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 604 |
-
children: [
|
| 605 |
-
Icon(Icons.home, size: 100, color: Colors.orange),
|
| 606 |
-
Text('Home Page', style: TextStyle(fontSize: 24)),
|
| 607 |
-
],
|
| 608 |
-
),
|
| 609 |
-
),
|
| 610 |
-
Center(
|
| 611 |
-
child: Column(
|
| 612 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 613 |
-
children: [
|
| 614 |
-
Icon(Icons.person, size: 100, color: Colors.orange),
|
| 615 |
-
Text('Profile Page', style: TextStyle(fontSize: 24)),
|
| 616 |
-
],
|
| 617 |
-
),
|
| 618 |
-
),
|
| 619 |
-
Center(
|
| 620 |
-
child: Column(
|
| 621 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 622 |
-
children: [
|
| 623 |
-
Icon(Icons.settings, size: 100, color: Colors.orange),
|
| 624 |
-
Text('Settings Page', style: TextStyle(fontSize: 24)),
|
| 625 |
-
],
|
| 626 |
-
),
|
| 627 |
-
),
|
| 628 |
-
];
|
| 629 |
-
|
| 630 |
-
@override
|
| 631 |
-
Widget build(BuildContext context) {
|
| 632 |
-
return Scaffold(
|
| 633 |
-
appBar: AppBar(
|
| 634 |
-
title: Text('${prompt}'),
|
| 635 |
-
),
|
| 636 |
-
drawer: Drawer(
|
| 637 |
-
child: ListView(
|
| 638 |
-
padding: EdgeInsets.zero,
|
| 639 |
-
children: [
|
| 640 |
-
DrawerHeader(
|
| 641 |
-
decoration: BoxDecoration(
|
| 642 |
-
color: Colors.orange,
|
| 643 |
-
),
|
| 644 |
-
child: Column(
|
| 645 |
-
crossAxisAlignment: CrossAxisAlignment.start,
|
| 646 |
-
mainAxisAlignment: MainAxisAlignment.end,
|
| 647 |
-
children: [
|
| 648 |
-
CircleAvatar(
|
| 649 |
-
radius: 30,
|
| 650 |
-
backgroundColor: Colors.white,
|
| 651 |
-
child: Icon(Icons.person, size: 40, color: Colors.orange),
|
| 652 |
-
),
|
| 653 |
-
SizedBox(height: 10),
|
| 654 |
-
Text(
|
| 655 |
-
'User Name',
|
| 656 |
-
style: TextStyle(color: Colors.white, fontSize: 18),
|
| 657 |
-
),
|
| 658 |
-
Text(
|
| 659 |
-
'user@example.com',
|
| 660 |
-
style: TextStyle(color: Colors.white70, fontSize: 14),
|
| 661 |
-
),
|
| 662 |
-
],
|
| 663 |
-
),
|
| 664 |
-
),
|
| 665 |
-
ListTile(
|
| 666 |
-
leading: Icon(Icons.home),
|
| 667 |
-
title: Text('Home'),
|
| 668 |
-
selected: _selectedIndex == 0,
|
| 669 |
-
onTap: () {
|
| 670 |
-
setState(() {
|
| 671 |
-
_selectedIndex = 0;
|
| 672 |
-
});
|
| 673 |
-
Navigator.pop(context);
|
| 674 |
-
},
|
| 675 |
-
),
|
| 676 |
-
ListTile(
|
| 677 |
-
leading: Icon(Icons.person),
|
| 678 |
-
title: Text('Profile'),
|
| 679 |
-
selected: _selectedIndex == 1,
|
| 680 |
-
onTap: () {
|
| 681 |
-
setState(() {
|
| 682 |
-
_selectedIndex = 1;
|
| 683 |
-
});
|
| 684 |
-
Navigator.pop(context);
|
| 685 |
-
},
|
| 686 |
-
),
|
| 687 |
-
ListTile(
|
| 688 |
-
leading: Icon(Icons.settings),
|
| 689 |
-
title: Text('Settings'),
|
| 690 |
-
selected: _selectedIndex == 2,
|
| 691 |
-
onTap: () {
|
| 692 |
-
setState(() {
|
| 693 |
-
_selectedIndex = 2;
|
| 694 |
-
});
|
| 695 |
-
Navigator.pop(context);
|
| 696 |
-
},
|
| 697 |
-
),
|
| 698 |
-
Divider(),
|
| 699 |
-
ListTile(
|
| 700 |
-
leading: Icon(Icons.logout),
|
| 701 |
-
title: Text('Logout'),
|
| 702 |
-
onTap: () {
|
| 703 |
-
Navigator.pop(context);
|
| 704 |
-
ScaffoldMessenger.of(context).showSnackBar(
|
| 705 |
-
SnackBar(content: Text('Logged out')),
|
| 706 |
-
);
|
| 707 |
-
},
|
| 708 |
-
),
|
| 709 |
-
],
|
| 710 |
-
),
|
| 711 |
-
),
|
| 712 |
-
body: _pages[_selectedIndex],
|
| 713 |
-
);
|
| 714 |
-
}
|
| 715 |
-
}`
|
| 716 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/api/claude/generate-latex/route.ts
DELETED
|
@@ -1,650 +0,0 @@
|
|
| 1 |
-
import { NextRequest, NextResponse } from 'next/server'
|
| 2 |
-
|
| 3 |
-
export async function POST(request: NextRequest) {
|
| 4 |
-
try {
|
| 5 |
-
const { prompt, sessionId } = await request.json()
|
| 6 |
-
|
| 7 |
-
if (!prompt) {
|
| 8 |
-
return NextResponse.json(
|
| 9 |
-
{ error: 'Prompt is required' },
|
| 10 |
-
{ status: 400 }
|
| 11 |
-
)
|
| 12 |
-
}
|
| 13 |
-
|
| 14 |
-
const apiKey = process.env.ANTHROPIC_API_KEY
|
| 15 |
-
|
| 16 |
-
if (apiKey) {
|
| 17 |
-
try {
|
| 18 |
-
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
| 19 |
-
method: 'POST',
|
| 20 |
-
headers: {
|
| 21 |
-
'Content-Type': 'application/json',
|
| 22 |
-
'x-api-key': apiKey,
|
| 23 |
-
'anthropic-version': '2023-06-01'
|
| 24 |
-
},
|
| 25 |
-
body: JSON.stringify({
|
| 26 |
-
model: 'claude-3-haiku-20240307',
|
| 27 |
-
max_tokens: 2048,
|
| 28 |
-
messages: [
|
| 29 |
-
{
|
| 30 |
-
role: 'user',
|
| 31 |
-
content: `Generate a complete LaTeX document for the following request.
|
| 32 |
-
Return ONLY the LaTeX code without any explanations or markdown formatting.
|
| 33 |
-
Include all necessary packages and document structure.
|
| 34 |
-
|
| 35 |
-
Request: ${prompt}`
|
| 36 |
-
}
|
| 37 |
-
],
|
| 38 |
-
system: `You are a LaTeX document generator. Generate clean, complete LaTeX documents.
|
| 39 |
-
- Always include \\documentclass and necessary packages
|
| 40 |
-
- Use appropriate document structure (title, sections, etc.)
|
| 41 |
-
- Include mathematical packages when needed (amsmath, amssymb, etc.)
|
| 42 |
-
- Return only pure LaTeX code, no explanations or markdown
|
| 43 |
-
- Ensure the document compiles without errors`
|
| 44 |
-
})
|
| 45 |
-
})
|
| 46 |
-
|
| 47 |
-
if (response.ok) {
|
| 48 |
-
const data = await response.json()
|
| 49 |
-
const latex = data.content[0].text
|
| 50 |
-
.replace(/```latex\n?/g, '')
|
| 51 |
-
.replace(/```tex\n?/g, '')
|
| 52 |
-
.replace(/```\n?/g, '')
|
| 53 |
-
.trim()
|
| 54 |
-
|
| 55 |
-
return NextResponse.json({ latex, sessionId })
|
| 56 |
-
}
|
| 57 |
-
} catch (error) {
|
| 58 |
-
console.error('Claude API error:', error)
|
| 59 |
-
}
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
// Fallback: Generate appropriate LaTeX based on keywords
|
| 63 |
-
const latex = generateLaTeXFromPrompt(prompt)
|
| 64 |
-
return NextResponse.json({ latex, sessionId })
|
| 65 |
-
|
| 66 |
-
} catch (error) {
|
| 67 |
-
console.error('Error generating LaTeX:', error)
|
| 68 |
-
return NextResponse.json(
|
| 69 |
-
{ error: 'Failed to generate LaTeX' },
|
| 70 |
-
{ status: 500 }
|
| 71 |
-
)
|
| 72 |
-
}
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
function generateLaTeXFromPrompt(prompt: string): string {
|
| 76 |
-
const promptLower = prompt.toLowerCase()
|
| 77 |
-
|
| 78 |
-
if (promptLower.includes('resume') || promptLower.includes('cv')) {
|
| 79 |
-
return generateResumeLaTeX()
|
| 80 |
-
} else if (promptLower.includes('calculus') || promptLower.includes('derivative') || promptLower.includes('integral')) {
|
| 81 |
-
return generateCalculusLaTeX()
|
| 82 |
-
} else if (promptLower.includes('physics')) {
|
| 83 |
-
return generatePhysicsLaTeX()
|
| 84 |
-
} else if (promptLower.includes('chemistry')) {
|
| 85 |
-
return generateChemistryLaTeX()
|
| 86 |
-
} else if (promptLower.includes('thesis') || promptLower.includes('paper')) {
|
| 87 |
-
return generateThesisLaTeX(prompt)
|
| 88 |
-
} else if (promptLower.includes('presentation') || promptLower.includes('beamer')) {
|
| 89 |
-
return generatePresentationLaTeX(prompt)
|
| 90 |
-
} else {
|
| 91 |
-
return generateGeneralLaTeX(prompt)
|
| 92 |
-
}
|
| 93 |
-
}
|
| 94 |
-
|
| 95 |
-
function generateGeneralLaTeX(prompt: string): string {
|
| 96 |
-
return `\\documentclass[12pt,a4paper]{article}
|
| 97 |
-
\\usepackage{amsmath,amssymb,amsthm}
|
| 98 |
-
\\usepackage{graphicx}
|
| 99 |
-
\\usepackage{hyperref}
|
| 100 |
-
\\usepackage{enumitem}
|
| 101 |
-
|
| 102 |
-
\\title{${prompt}}
|
| 103 |
-
\\author{Generated Document}
|
| 104 |
-
\\date{\\today}
|
| 105 |
-
|
| 106 |
-
\\begin{document}
|
| 107 |
-
|
| 108 |
-
\\maketitle
|
| 109 |
-
|
| 110 |
-
\\begin{abstract}
|
| 111 |
-
This document was generated based on the request: "${prompt}".
|
| 112 |
-
\\end{abstract}
|
| 113 |
-
|
| 114 |
-
\\section{Introduction}
|
| 115 |
-
This document provides comprehensive information about ${prompt}.
|
| 116 |
-
|
| 117 |
-
\\section{Main Content}
|
| 118 |
-
\\subsection{Overview}
|
| 119 |
-
The topic "${prompt}" encompasses various aspects that we'll explore in this document.
|
| 120 |
-
|
| 121 |
-
\\subsection{Key Points}
|
| 122 |
-
\\begin{itemize}
|
| 123 |
-
\\item First important point about ${prompt}
|
| 124 |
-
\\item Second key aspect to consider
|
| 125 |
-
\\item Additional relevant information
|
| 126 |
-
\\end{itemize}
|
| 127 |
-
|
| 128 |
-
\\section{Mathematical Formulation}
|
| 129 |
-
When discussing ${prompt}, we can consider:
|
| 130 |
-
\\[
|
| 131 |
-
f(x) = \\sum_{n=0}^{\\infty} \\frac{x^n}{n!}
|
| 132 |
-
\\]
|
| 133 |
-
|
| 134 |
-
\\section{Conclusion}
|
| 135 |
-
This document has provided an overview of ${prompt}, covering its main aspects and implications.
|
| 136 |
-
|
| 137 |
-
\\bibliographystyle{plain}
|
| 138 |
-
\\end{document}`
|
| 139 |
-
}
|
| 140 |
-
|
| 141 |
-
function generateResumeLaTeX(): string {
|
| 142 |
-
return `\\documentclass[11pt,a4paper]{article}
|
| 143 |
-
\\usepackage[margin=1in]{geometry}
|
| 144 |
-
\\usepackage{hyperref}
|
| 145 |
-
\\usepackage{enumitem}
|
| 146 |
-
|
| 147 |
-
\\pagestyle{empty}
|
| 148 |
-
|
| 149 |
-
\\begin{document}
|
| 150 |
-
|
| 151 |
-
\\begin{center}
|
| 152 |
-
{\\LARGE \\textbf{Your Name}}\\\\[2mm]
|
| 153 |
-
your.email@example.com | (123) 456-7890 | LinkedIn: linkedin.com/in/yourname
|
| 154 |
-
\\end{center}
|
| 155 |
-
|
| 156 |
-
\\section*{Professional Summary}
|
| 157 |
-
Experienced professional with expertise in [field]. Proven track record of [achievements].
|
| 158 |
-
|
| 159 |
-
\\section*{Education}
|
| 160 |
-
\\textbf{University Name} \\hfill 2018 - 2022\\\\
|
| 161 |
-
Bachelor of Science in Computer Science\\\\
|
| 162 |
-
GPA: 3.8/4.0
|
| 163 |
-
|
| 164 |
-
\\section*{Experience}
|
| 165 |
-
\\textbf{Software Engineer} \\hfill June 2022 - Present\\\\
|
| 166 |
-
\\textit{Company Name, Location}
|
| 167 |
-
\\begin{itemize}[leftmargin=*, itemsep=0pt]
|
| 168 |
-
\\item Developed and maintained web applications using modern frameworks
|
| 169 |
-
\\item Collaborated with cross-functional teams to deliver projects on time
|
| 170 |
-
\\item Improved system performance by 40\\% through optimization
|
| 171 |
-
\\end{itemize}
|
| 172 |
-
|
| 173 |
-
\\section*{Skills}
|
| 174 |
-
\\textbf{Programming:} Python, JavaScript, Java, C++\\\\
|
| 175 |
-
\\textbf{Technologies:} React, Node.js, Docker, AWS\\\\
|
| 176 |
-
\\textbf{Tools:} Git, VS Code, JIRA, Jenkins
|
| 177 |
-
|
| 178 |
-
\\section*{Projects}
|
| 179 |
-
\\textbf{Project Name} | \\textit{Technologies Used}\\\\
|
| 180 |
-
Brief description of the project and your role in it.
|
| 181 |
-
|
| 182 |
-
\\end{document}`
|
| 183 |
-
}
|
| 184 |
-
|
| 185 |
-
function generateCalculusLaTeX(): string {
|
| 186 |
-
return `\\documentclass[12pt]{article}
|
| 187 |
-
\\usepackage{amsmath,amssymb,amsthm}
|
| 188 |
-
\\usepackage{tikz}
|
| 189 |
-
\\usepackage{pgfplots}
|
| 190 |
-
\\pgfplotsset{compat=1.17}
|
| 191 |
-
|
| 192 |
-
\\theoremstyle{definition}
|
| 193 |
-
\\newtheorem{definition}{Definition}
|
| 194 |
-
\\newtheorem{theorem}{Theorem}
|
| 195 |
-
\\newtheorem{example}{Example}
|
| 196 |
-
|
| 197 |
-
\\title{Calculus Reference Sheet}
|
| 198 |
-
\\author{Mathematics Department}
|
| 199 |
-
\\date{\\today}
|
| 200 |
-
|
| 201 |
-
\\begin{document}
|
| 202 |
-
|
| 203 |
-
\\maketitle
|
| 204 |
-
|
| 205 |
-
\\section{Derivatives}
|
| 206 |
-
|
| 207 |
-
\\subsection{Basic Rules}
|
| 208 |
-
\\begin{align}
|
| 209 |
-
\\frac{d}{dx}[c] &= 0\\\\
|
| 210 |
-
\\frac{d}{dx}[x^n] &= nx^{n-1}\\\\
|
| 211 |
-
\\frac{d}{dx}[e^x] &= e^x\\\\
|
| 212 |
-
\\frac{d}{dx}[\\ln x] &= \\frac{1}{x}\\\\
|
| 213 |
-
\\frac{d}{dx}[\\sin x] &= \\cos x\\\\
|
| 214 |
-
\\frac{d}{dx}[\\cos x] &= -\\sin x
|
| 215 |
-
\\end{align}
|
| 216 |
-
|
| 217 |
-
\\subsection{Chain Rule}
|
| 218 |
-
If $y = f(g(x))$, then:
|
| 219 |
-
\\[
|
| 220 |
-
\\frac{dy}{dx} = f'(g(x)) \\cdot g'(x)
|
| 221 |
-
\\]
|
| 222 |
-
|
| 223 |
-
\\subsection{Product Rule}
|
| 224 |
-
\\[
|
| 225 |
-
\\frac{d}{dx}[f(x)g(x)] = f'(x)g(x) + f(x)g'(x)
|
| 226 |
-
\\]
|
| 227 |
-
|
| 228 |
-
\\subsection{Quotient Rule}
|
| 229 |
-
\\[
|
| 230 |
-
\\frac{d}{dx}\\left[\\frac{f(x)}{g(x)}\\right] = \\frac{f'(x)g(x) - f(x)g'(x)}{[g(x)]^2}
|
| 231 |
-
\\]
|
| 232 |
-
|
| 233 |
-
\\section{Integrals}
|
| 234 |
-
|
| 235 |
-
\\subsection{Basic Integrals}
|
| 236 |
-
\\begin{align}
|
| 237 |
-
\\int x^n\\,dx &= \\frac{x^{n+1}}{n+1} + C, \\quad n \\neq -1\\\\
|
| 238 |
-
\\int \\frac{1}{x}\\,dx &= \\ln|x| + C\\\\
|
| 239 |
-
\\int e^x\\,dx &= e^x + C\\\\
|
| 240 |
-
\\int \\sin x\\,dx &= -\\cos x + C\\\\
|
| 241 |
-
\\int \\cos x\\,dx &= \\sin x + C
|
| 242 |
-
\\end{align}
|
| 243 |
-
|
| 244 |
-
\\subsection{Integration by Parts}
|
| 245 |
-
\\[
|
| 246 |
-
\\int u\\,dv = uv - \\int v\\,du
|
| 247 |
-
\\]
|
| 248 |
-
|
| 249 |
-
\\subsection{Substitution Method}
|
| 250 |
-
If $u = g(x)$, then $du = g'(x)dx$:
|
| 251 |
-
\\[
|
| 252 |
-
\\int f(g(x))g'(x)\\,dx = \\int f(u)\\,du
|
| 253 |
-
\\]
|
| 254 |
-
|
| 255 |
-
\\section{Series}
|
| 256 |
-
|
| 257 |
-
\\subsection{Taylor Series}
|
| 258 |
-
\\[
|
| 259 |
-
f(x) = \\sum_{n=0}^{\\infty} \\frac{f^{(n)}(a)}{n!}(x-a)^n
|
| 260 |
-
\\]
|
| 261 |
-
|
| 262 |
-
\\subsection{Common Series}
|
| 263 |
-
\\begin{align}
|
| 264 |
-
e^x &= \\sum_{n=0}^{\\infty} \\frac{x^n}{n!}\\\\
|
| 265 |
-
\\sin x &= \\sum_{n=0}^{\\infty} \\frac{(-1)^n x^{2n+1}}{(2n+1)!}\\\\
|
| 266 |
-
\\cos x &= \\sum_{n=0}^{\\infty} \\frac{(-1)^n x^{2n}}{(2n)!}
|
| 267 |
-
\\end{align}
|
| 268 |
-
|
| 269 |
-
\\end{document}`
|
| 270 |
-
}
|
| 271 |
-
|
| 272 |
-
function generatePhysicsLaTeX(): string {
|
| 273 |
-
return `\\documentclass[12pt]{article}
|
| 274 |
-
\\usepackage{amsmath,amssymb}
|
| 275 |
-
\\usepackage{physics}
|
| 276 |
-
\\usepackage{siunitx}
|
| 277 |
-
\\usepackage{tikz}
|
| 278 |
-
|
| 279 |
-
\\title{Physics Formula Sheet}
|
| 280 |
-
\\author{Physics Department}
|
| 281 |
-
\\date{\\today}
|
| 282 |
-
|
| 283 |
-
\\begin{document}
|
| 284 |
-
|
| 285 |
-
\\maketitle
|
| 286 |
-
|
| 287 |
-
\\section{Classical Mechanics}
|
| 288 |
-
|
| 289 |
-
\\subsection{Newton's Laws}
|
| 290 |
-
\\begin{enumerate}
|
| 291 |
-
\\item First Law: $\\vec{F}_{net} = 0 \\implies \\vec{v} = \\text{constant}$
|
| 292 |
-
\\item Second Law: $\\vec{F} = m\\vec{a}$
|
| 293 |
-
\\item Third Law: $\\vec{F}_{12} = -\\vec{F}_{21}$
|
| 294 |
-
\\end{enumerate}
|
| 295 |
-
|
| 296 |
-
\\subsection{Kinematics}
|
| 297 |
-
\\begin{align}
|
| 298 |
-
v &= v_0 + at\\\\
|
| 299 |
-
x &= x_0 + v_0t + \\frac{1}{2}at^2\\\\
|
| 300 |
-
v^2 &= v_0^2 + 2a(x - x_0)
|
| 301 |
-
\\end{align}
|
| 302 |
-
|
| 303 |
-
\\subsection{Energy}
|
| 304 |
-
\\begin{itemize}
|
| 305 |
-
\\item Kinetic Energy: $KE = \\frac{1}{2}mv^2$
|
| 306 |
-
\\item Potential Energy: $PE = mgh$
|
| 307 |
-
\\item Work: $W = \\vec{F} \\cdot \\vec{d} = Fd\\cos\\theta$
|
| 308 |
-
\\item Power: $P = \\frac{dW}{dt} = \\vec{F} \\cdot \\vec{v}$
|
| 309 |
-
\\end{itemize}
|
| 310 |
-
|
| 311 |
-
\\section{Electromagnetism}
|
| 312 |
-
|
| 313 |
-
\\subsection{Maxwell's Equations}
|
| 314 |
-
\\begin{align}
|
| 315 |
-
\\nabla \\cdot \\vec{E} &= \\frac{\\rho}{\\epsilon_0}\\\\
|
| 316 |
-
\\nabla \\cdot \\vec{B} &= 0\\\\
|
| 317 |
-
\\nabla \\times \\vec{E} &= -\\frac{\\partial \\vec{B}}{\\partial t}\\\\
|
| 318 |
-
\\nabla \\times \\vec{B} &= \\mu_0\\vec{J} + \\mu_0\\epsilon_0\\frac{\\partial \\vec{E}}{\\partial t}
|
| 319 |
-
\\end{align}
|
| 320 |
-
|
| 321 |
-
\\subsection{Electric Fields and Forces}
|
| 322 |
-
\\begin{itemize}
|
| 323 |
-
\\item Coulomb's Law: $F = k\\frac{q_1q_2}{r^2}$
|
| 324 |
-
\\item Electric Field: $\\vec{E} = \\frac{\\vec{F}}{q}$
|
| 325 |
-
\\item Electric Potential: $V = \\frac{U}{q}$
|
| 326 |
-
\\end{itemize}
|
| 327 |
-
|
| 328 |
-
\\section{Quantum Mechanics}
|
| 329 |
-
|
| 330 |
-
\\subsection{Schrödinger Equation}
|
| 331 |
-
Time-dependent:
|
| 332 |
-
\\[
|
| 333 |
-
i\\hbar\\frac{\\partial\\Psi}{\\partial t} = \\hat{H}\\Psi
|
| 334 |
-
\\]
|
| 335 |
-
|
| 336 |
-
Time-independent:
|
| 337 |
-
\\[
|
| 338 |
-
\\hat{H}\\psi = E\\psi
|
| 339 |
-
\\]
|
| 340 |
-
|
| 341 |
-
\\subsection{Uncertainty Principle}
|
| 342 |
-
\\[
|
| 343 |
-
\\Delta x \\cdot \\Delta p \\geq \\frac{\\hbar}{2}
|
| 344 |
-
\\]
|
| 345 |
-
|
| 346 |
-
\\end{document}`
|
| 347 |
-
}
|
| 348 |
-
|
| 349 |
-
function generateChemistryLaTeX(): string {
|
| 350 |
-
return `\\documentclass[12pt]{article}
|
| 351 |
-
\\usepackage{amsmath,amssymb}
|
| 352 |
-
\\usepackage{chemformula}
|
| 353 |
-
\\usepackage{chemfig}
|
| 354 |
-
\\usepackage{siunitx}
|
| 355 |
-
|
| 356 |
-
\\title{Chemistry Reference Guide}
|
| 357 |
-
\\author{Chemistry Department}
|
| 358 |
-
\\date{\\today}
|
| 359 |
-
|
| 360 |
-
\\begin{document}
|
| 361 |
-
|
| 362 |
-
\\maketitle
|
| 363 |
-
|
| 364 |
-
\\section{Chemical Equations}
|
| 365 |
-
|
| 366 |
-
\\subsection{Balancing Equations}
|
| 367 |
-
Example reaction:
|
| 368 |
-
\\[
|
| 369 |
-
\\ch{2 H2 + O2 -> 2 H2O}
|
| 370 |
-
\\]
|
| 371 |
-
|
| 372 |
-
\\subsection{Combustion}
|
| 373 |
-
\\[
|
| 374 |
-
\\ch{CH4 + 2 O2 -> CO2 + 2 H2O}
|
| 375 |
-
\\]
|
| 376 |
-
|
| 377 |
-
\\section{Stoichiometry}
|
| 378 |
-
|
| 379 |
-
\\subsection{Molar Relationships}
|
| 380 |
-
\\begin{itemize}
|
| 381 |
-
\\item Molar mass: Mass of one mole of substance
|
| 382 |
-
\\item Avogadro's number: $N_A = 6.022 \\times 10^{23}$ mol$^{-1}$
|
| 383 |
-
\\item Moles: $n = \\frac{m}{M}$ where $m$ is mass and $M$ is molar mass
|
| 384 |
-
\\end{itemize}
|
| 385 |
-
|
| 386 |
-
\\section{Thermodynamics}
|
| 387 |
-
|
| 388 |
-
\\subsection{First Law}
|
| 389 |
-
\\[
|
| 390 |
-
\\Delta U = q + w
|
| 391 |
-
\\]
|
| 392 |
-
|
| 393 |
-
\\subsection{Enthalpy}
|
| 394 |
-
\\[
|
| 395 |
-
\\Delta H = \\Delta U + P\\Delta V
|
| 396 |
-
\\]
|
| 397 |
-
|
| 398 |
-
\\subsection{Gibbs Free Energy}
|
| 399 |
-
\\[
|
| 400 |
-
\\Delta G = \\Delta H - T\\Delta S
|
| 401 |
-
\\]
|
| 402 |
-
\\begin{itemize}
|
| 403 |
-
\\item $\\Delta G < 0$: Spontaneous reaction
|
| 404 |
-
\\item $\\Delta G = 0$: Equilibrium
|
| 405 |
-
\\item $\\Delta G > 0$: Non-spontaneous
|
| 406 |
-
\\end{itemize}
|
| 407 |
-
|
| 408 |
-
\\section{Kinetics}
|
| 409 |
-
|
| 410 |
-
\\subsection{Rate Laws}
|
| 411 |
-
First order: $\\text{Rate} = k[A]$
|
| 412 |
-
|
| 413 |
-
Second order: $\\text{Rate} = k[A]^2$ or $\\text{Rate} = k[A][B]$
|
| 414 |
-
|
| 415 |
-
\\subsection{Arrhenius Equation}
|
| 416 |
-
\\[
|
| 417 |
-
k = Ae^{-E_a/RT}
|
| 418 |
-
\\]
|
| 419 |
-
|
| 420 |
-
\\section{Equilibrium}
|
| 421 |
-
|
| 422 |
-
\\subsection{Equilibrium Constant}
|
| 423 |
-
For reaction: \\ch{aA + bB <=> cC + dD}
|
| 424 |
-
\\[
|
| 425 |
-
K_c = \\frac{[C]^c[D]^d}{[A]^a[B]^b}
|
| 426 |
-
\\]
|
| 427 |
-
|
| 428 |
-
\\subsection{Le Chatelier's Principle}
|
| 429 |
-
System at equilibrium will shift to counteract any imposed change.
|
| 430 |
-
|
| 431 |
-
\\end{document}`
|
| 432 |
-
}
|
| 433 |
-
|
| 434 |
-
function generateThesisLaTeX(topic: string): string {
|
| 435 |
-
return `\\documentclass[12pt,a4paper]{report}
|
| 436 |
-
\\usepackage{amsmath,amssymb,amsthm}
|
| 437 |
-
\\usepackage{graphicx}
|
| 438 |
-
\\usepackage{hyperref}
|
| 439 |
-
\\usepackage{cite}
|
| 440 |
-
\\usepackage{setspace}
|
| 441 |
-
\\usepackage[margin=1in]{geometry}
|
| 442 |
-
|
| 443 |
-
\\doublespacing
|
| 444 |
-
|
| 445 |
-
\\title{${topic}}
|
| 446 |
-
\\author{Your Name}
|
| 447 |
-
\\date{\\today}
|
| 448 |
-
|
| 449 |
-
\\begin{document}
|
| 450 |
-
|
| 451 |
-
\\maketitle
|
| 452 |
-
|
| 453 |
-
\\begin{abstract}
|
| 454 |
-
This thesis investigates ${topic}. We present novel findings that contribute to the understanding of this field. Our research demonstrates significant improvements over existing methods.
|
| 455 |
-
|
| 456 |
-
\\textbf{Keywords:} ${topic}, research, analysis, methodology
|
| 457 |
-
\\end{abstract}
|
| 458 |
-
|
| 459 |
-
\\tableofcontents
|
| 460 |
-
\\listoffigures
|
| 461 |
-
\\listoftables
|
| 462 |
-
|
| 463 |
-
\\chapter{Introduction}
|
| 464 |
-
|
| 465 |
-
\\section{Background}
|
| 466 |
-
The study of ${topic} has gained significant attention in recent years. This research aims to address key challenges in the field.
|
| 467 |
-
|
| 468 |
-
\\section{Research Questions}
|
| 469 |
-
\\begin{enumerate}
|
| 470 |
-
\\item What are the fundamental principles underlying ${topic}?
|
| 471 |
-
\\item How can we improve current approaches?
|
| 472 |
-
\\item What are the practical applications?
|
| 473 |
-
\\end{enumerate}
|
| 474 |
-
|
| 475 |
-
\\section{Thesis Structure}
|
| 476 |
-
This thesis is organized as follows:
|
| 477 |
-
\\begin{itemize}
|
| 478 |
-
\\item Chapter 2: Literature Review
|
| 479 |
-
\\item Chapter 3: Methodology
|
| 480 |
-
\\item Chapter 4: Results and Analysis
|
| 481 |
-
\\item Chapter 5: Discussion
|
| 482 |
-
\\item Chapter 6: Conclusion
|
| 483 |
-
\\end{itemize}
|
| 484 |
-
|
| 485 |
-
\\chapter{Literature Review}
|
| 486 |
-
|
| 487 |
-
\\section{Historical Context}
|
| 488 |
-
Previous work in ${topic} has established several important foundations...
|
| 489 |
-
|
| 490 |
-
\\section{Current State of Research}
|
| 491 |
-
Recent developments have shown that...
|
| 492 |
-
|
| 493 |
-
\\chapter{Methodology}
|
| 494 |
-
|
| 495 |
-
\\section{Research Design}
|
| 496 |
-
Our approach involves...
|
| 497 |
-
|
| 498 |
-
\\section{Data Collection}
|
| 499 |
-
Data was collected using...
|
| 500 |
-
|
| 501 |
-
\\chapter{Results and Analysis}
|
| 502 |
-
|
| 503 |
-
\\section{Findings}
|
| 504 |
-
Our analysis reveals...
|
| 505 |
-
|
| 506 |
-
\\section{Statistical Analysis}
|
| 507 |
-
The results show statistical significance with $p < 0.05$.
|
| 508 |
-
|
| 509 |
-
\\chapter{Discussion}
|
| 510 |
-
|
| 511 |
-
\\section{Interpretation of Results}
|
| 512 |
-
The findings suggest...
|
| 513 |
-
|
| 514 |
-
\\section{Limitations}
|
| 515 |
-
This study has several limitations...
|
| 516 |
-
|
| 517 |
-
\\chapter{Conclusion}
|
| 518 |
-
|
| 519 |
-
\\section{Summary}
|
| 520 |
-
This thesis has presented...
|
| 521 |
-
|
| 522 |
-
\\section{Future Work}
|
| 523 |
-
Future research should explore...
|
| 524 |
-
|
| 525 |
-
\\bibliographystyle{plain}
|
| 526 |
-
\\bibliography{references}
|
| 527 |
-
|
| 528 |
-
\\appendix
|
| 529 |
-
\\chapter{Additional Data}
|
| 530 |
-
|
| 531 |
-
\\end{document}`
|
| 532 |
-
}
|
| 533 |
-
|
| 534 |
-
function generatePresentationLaTeX(topic: string): string {
|
| 535 |
-
return `\\documentclass{beamer}
|
| 536 |
-
\\usetheme{Madrid}
|
| 537 |
-
\\usecolortheme{seahorse}
|
| 538 |
-
\\usepackage{amsmath,amssymb}
|
| 539 |
-
\\usepackage{graphicx}
|
| 540 |
-
\\usepackage{tikz}
|
| 541 |
-
|
| 542 |
-
\\title{${topic}}
|
| 543 |
-
\\author{Presenter Name}
|
| 544 |
-
\\institute{Your Institution}
|
| 545 |
-
\\date{\\today}
|
| 546 |
-
|
| 547 |
-
\\begin{document}
|
| 548 |
-
|
| 549 |
-
\\frame{\\titlepage}
|
| 550 |
-
|
| 551 |
-
\\begin{frame}
|
| 552 |
-
\\frametitle{Outline}
|
| 553 |
-
\\tableofcontents
|
| 554 |
-
\\end{frame}
|
| 555 |
-
|
| 556 |
-
\\section{Introduction}
|
| 557 |
-
|
| 558 |
-
\\begin{frame}
|
| 559 |
-
\\frametitle{Introduction}
|
| 560 |
-
\\begin{itemize}
|
| 561 |
-
\\item Overview of ${topic}
|
| 562 |
-
\\item Why it matters
|
| 563 |
-
\\item Our approach
|
| 564 |
-
\\end{itemize}
|
| 565 |
-
\\end{frame}
|
| 566 |
-
|
| 567 |
-
\\section{Background}
|
| 568 |
-
|
| 569 |
-
\\begin{frame}
|
| 570 |
-
\\frametitle{Background}
|
| 571 |
-
\\begin{columns}
|
| 572 |
-
\\column{0.5\\textwidth}
|
| 573 |
-
Key concepts:
|
| 574 |
-
\\begin{itemize}
|
| 575 |
-
\\item Concept 1
|
| 576 |
-
\\item Concept 2
|
| 577 |
-
\\item Concept 3
|
| 578 |
-
\\end{itemize}
|
| 579 |
-
|
| 580 |
-
\\column{0.5\\textwidth}
|
| 581 |
-
\\begin{block}{Definition}
|
| 582 |
-
${topic} is defined as...
|
| 583 |
-
\\end{block}
|
| 584 |
-
\\end{columns}
|
| 585 |
-
\\end{frame}
|
| 586 |
-
|
| 587 |
-
\\section{Methodology}
|
| 588 |
-
|
| 589 |
-
\\begin{frame}
|
| 590 |
-
\\frametitle{Our Approach}
|
| 591 |
-
\\begin{enumerate}
|
| 592 |
-
\\item<1-> Step 1: Initial analysis
|
| 593 |
-
\\item<2-> Step 2: Data collection
|
| 594 |
-
\\item<3-> Step 3: Processing
|
| 595 |
-
\\item<4-> Step 4: Evaluation
|
| 596 |
-
\\end{enumerate}
|
| 597 |
-
\\end{frame}
|
| 598 |
-
|
| 599 |
-
\\section{Results}
|
| 600 |
-
|
| 601 |
-
\\begin{frame}
|
| 602 |
-
\\frametitle{Key Findings}
|
| 603 |
-
\\begin{theorem}
|
| 604 |
-
Our main result shows that...
|
| 605 |
-
\\end{theorem}
|
| 606 |
-
|
| 607 |
-
\\begin{proof}
|
| 608 |
-
The proof follows from...
|
| 609 |
-
\\end{proof}
|
| 610 |
-
\\end{frame}
|
| 611 |
-
|
| 612 |
-
\\begin{frame}
|
| 613 |
-
\\frametitle{Performance Metrics}
|
| 614 |
-
\\begin{center}
|
| 615 |
-
\\begin{tikzpicture}
|
| 616 |
-
\\begin{axis}[
|
| 617 |
-
ybar,
|
| 618 |
-
ylabel={Performance},
|
| 619 |
-
xlabel={Method},
|
| 620 |
-
symbolic x coords={Baseline, Our Method, State-of-Art},
|
| 621 |
-
xtick=data
|
| 622 |
-
]
|
| 623 |
-
\\addplot coordinates {
|
| 624 |
-
(Baseline, 65)
|
| 625 |
-
(Our Method, 85)
|
| 626 |
-
(State-of-Art, 78)
|
| 627 |
-
};
|
| 628 |
-
\\end{axis}
|
| 629 |
-
\\end{tikzpicture}
|
| 630 |
-
\\end{center}
|
| 631 |
-
\\end{frame}
|
| 632 |
-
|
| 633 |
-
\\section{Conclusion}
|
| 634 |
-
|
| 635 |
-
\\begin{frame}
|
| 636 |
-
\\frametitle{Conclusion}
|
| 637 |
-
\\begin{itemize}
|
| 638 |
-
\\item Successfully demonstrated ${topic}
|
| 639 |
-
\\item Achieved 20\\% improvement
|
| 640 |
-
\\item Future work includes...
|
| 641 |
-
\\end{itemize}
|
| 642 |
-
|
| 643 |
-
\\vspace{1cm}
|
| 644 |
-
\\begin{center}
|
| 645 |
-
\\Large{Thank you! Questions?}
|
| 646 |
-
\\end{center}
|
| 647 |
-
\\end{frame}
|
| 648 |
-
|
| 649 |
-
\\end{document}`
|
| 650 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/components/Desktop.tsx
CHANGED
|
@@ -34,7 +34,10 @@ import {
|
|
| 34 |
Key,
|
| 35 |
Brain,
|
| 36 |
Code,
|
| 37 |
-
FileText
|
|
|
|
|
|
|
|
|
|
| 38 |
} from '@phosphor-icons/react'
|
| 39 |
|
| 40 |
export function Desktop() {
|
|
@@ -437,8 +440,12 @@ export function Desktop() {
|
|
| 437 |
id: 'flutter-runner',
|
| 438 |
label: 'Flutter Runner',
|
| 439 |
icon: (
|
| 440 |
-
<div className="bg-gradient-to-
|
| 441 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 442 |
</div>
|
| 443 |
),
|
| 444 |
onRestore: () => setFlutterRunnerMinimized(false)
|
|
@@ -450,8 +457,12 @@ export function Desktop() {
|
|
| 450 |
id: 'research',
|
| 451 |
label: 'Research',
|
| 452 |
icon: (
|
| 453 |
-
<div className="bg-gradient-to-
|
| 454 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
</div>
|
| 456 |
),
|
| 457 |
onRestore: () => setResearchBrowserMinimized(false)
|
|
@@ -463,8 +474,12 @@ export function Desktop() {
|
|
| 463 |
id: 'flutter-editor',
|
| 464 |
label: 'Flutter IDE',
|
| 465 |
icon: (
|
| 466 |
-
<div className="bg-gradient-to-
|
| 467 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
</div>
|
| 469 |
),
|
| 470 |
onRestore: () => setFlutterCodeEditorMinimized(false)
|
|
@@ -476,8 +491,12 @@ export function Desktop() {
|
|
| 476 |
id: 'latex-editor',
|
| 477 |
label: 'LaTeX Studio',
|
| 478 |
icon: (
|
| 479 |
-
<div className="bg-
|
| 480 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
</div>
|
| 482 |
),
|
| 483 |
onRestore: () => setLaTeXEditorMinimized(false)
|
|
@@ -600,7 +619,7 @@ export function Desktop() {
|
|
| 600 |
<DraggableDesktopIcon
|
| 601 |
id="research"
|
| 602 |
label="Research"
|
| 603 |
-
iconType="
|
| 604 |
initialPosition={{ x: 0, y: 0 }}
|
| 605 |
onClick={() => { }}
|
| 606 |
onDoubleClick={openResearchBrowser}
|
|
@@ -610,7 +629,7 @@ export function Desktop() {
|
|
| 610 |
<DraggableDesktopIcon
|
| 611 |
id="flutter-editor"
|
| 612 |
label="Flutter IDE"
|
| 613 |
-
iconType="
|
| 614 |
initialPosition={{ x: 0, y: 0 }}
|
| 615 |
onClick={() => { }}
|
| 616 |
onDoubleClick={openFlutterCodeEditor}
|
|
@@ -620,7 +639,7 @@ export function Desktop() {
|
|
| 620 |
<DraggableDesktopIcon
|
| 621 |
id="latex-editor"
|
| 622 |
label="LaTeX Studio"
|
| 623 |
-
iconType="
|
| 624 |
initialPosition={{ x: 0, y: 0 }}
|
| 625 |
onClick={() => { }}
|
| 626 |
onDoubleClick={openLaTeXEditor}
|
|
|
|
| 34 |
Key,
|
| 35 |
Brain,
|
| 36 |
Code,
|
| 37 |
+
FileText,
|
| 38 |
+
DeviceMobile,
|
| 39 |
+
Lightning,
|
| 40 |
+
Function
|
| 41 |
} from '@phosphor-icons/react'
|
| 42 |
|
| 43 |
export function Desktop() {
|
|
|
|
| 440 |
id: 'flutter-runner',
|
| 441 |
label: 'Flutter Runner',
|
| 442 |
icon: (
|
| 443 |
+
<div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
|
| 444 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
|
| 445 |
+
<DeviceMobile size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
|
| 446 |
+
<div className="absolute top-1 right-1">
|
| 447 |
+
<Lightning size={8} weight="fill" className="text-yellow-300 drop-shadow-md" />
|
| 448 |
+
</div>
|
| 449 |
</div>
|
| 450 |
),
|
| 451 |
onRestore: () => setFlutterRunnerMinimized(false)
|
|
|
|
| 457 |
id: 'research',
|
| 458 |
label: 'Research',
|
| 459 |
icon: (
|
| 460 |
+
<div className="bg-gradient-to-b from-purple-500 to-indigo-600 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
|
| 461 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
|
| 462 |
+
<Globe size={20} weight="duotone" className="text-white relative z-10 drop-shadow-md" />
|
| 463 |
+
<div className="absolute bottom-1 right-1 bg-white/20 p-0.5 rounded-full backdrop-blur-md shadow-sm">
|
| 464 |
+
<Sparkle size={6} weight="fill" className="text-yellow-300" />
|
| 465 |
+
</div>
|
| 466 |
</div>
|
| 467 |
),
|
| 468 |
onRestore: () => setResearchBrowserMinimized(false)
|
|
|
|
| 474 |
id: 'flutter-editor',
|
| 475 |
label: 'Flutter IDE',
|
| 476 |
icon: (
|
| 477 |
+
<div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
|
| 478 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
|
| 479 |
+
<DeviceMobile size={20} weight="fill" className="text-white relative z-10 drop-shadow-md" />
|
| 480 |
+
<div className="absolute top-1 right-1">
|
| 481 |
+
<Lightning size={8} weight="fill" className="text-yellow-300 drop-shadow-md" />
|
| 482 |
+
</div>
|
| 483 |
</div>
|
| 484 |
),
|
| 485 |
onRestore: () => setFlutterCodeEditorMinimized(false)
|
|
|
|
| 491 |
id: 'latex-editor',
|
| 492 |
label: 'LaTeX Studio',
|
| 493 |
icon: (
|
| 494 |
+
<div className="bg-gradient-to-b from-slate-700 to-slate-900 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
|
| 495 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/10 to-transparent opacity-30" />
|
| 496 |
+
<div className="relative z-10 flex flex-col items-center justify-center">
|
| 497 |
+
<Function size={18} weight="bold" className="text-green-400 drop-shadow-md" />
|
| 498 |
+
<span className="text-[5px] font-black text-gray-300 tracking-widest mt-0.5">TEX</span>
|
| 499 |
+
</div>
|
| 500 |
</div>
|
| 501 |
),
|
| 502 |
onRestore: () => setLaTeXEditorMinimized(false)
|
|
|
|
| 619 |
<DraggableDesktopIcon
|
| 620 |
id="research"
|
| 621 |
label="Research"
|
| 622 |
+
iconType="research"
|
| 623 |
initialPosition={{ x: 0, y: 0 }}
|
| 624 |
onClick={() => { }}
|
| 625 |
onDoubleClick={openResearchBrowser}
|
|
|
|
| 629 |
<DraggableDesktopIcon
|
| 630 |
id="flutter-editor"
|
| 631 |
label="Flutter IDE"
|
| 632 |
+
iconType="flutter"
|
| 633 |
initialPosition={{ x: 0, y: 0 }}
|
| 634 |
onClick={() => { }}
|
| 635 |
onDoubleClick={openFlutterCodeEditor}
|
|
|
|
| 639 |
<DraggableDesktopIcon
|
| 640 |
id="latex-editor"
|
| 641 |
label="LaTeX Studio"
|
| 642 |
+
iconType="latex"
|
| 643 |
initialPosition={{ x: 0, y: 0 }}
|
| 644 |
onClick={() => { }}
|
| 645 |
onDoubleClick={openLaTeXEditor}
|
app/components/Dock.tsx
CHANGED
|
@@ -94,8 +94,9 @@ export function Dock({
|
|
| 94 |
},
|
| 95 |
{
|
| 96 |
icon: (
|
| 97 |
-
<div className="bg-white w-full h-full rounded-xl flex items-center justify-center border border-
|
| 98 |
-
<
|
|
|
|
| 99 |
</div>
|
| 100 |
),
|
| 101 |
label: 'Gemini',
|
|
|
|
| 94 |
},
|
| 95 |
{
|
| 96 |
icon: (
|
| 97 |
+
<div className="bg-gradient-to-b from-white to-blue-50 w-full h-full rounded-xl flex items-center justify-center shadow-lg border-[0.5px] border-white/50 relative overflow-hidden">
|
| 98 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/40 to-transparent" />
|
| 99 |
+
<Sparkle size={24} weight="fill" className="text-blue-500 md:scale-110 drop-shadow-sm" />
|
| 100 |
</div>
|
| 101 |
),
|
| 102 |
label: 'Gemini',
|
app/components/DraggableDesktopIcon.tsx
CHANGED
|
@@ -68,14 +68,39 @@ export function DraggableDesktopIcon({
|
|
| 68 |
)
|
| 69 |
case 'research':
|
| 70 |
return (
|
| 71 |
-
<div className="bg-gradient-to-
|
| 72 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
</div>
|
| 74 |
)
|
| 75 |
case 'gemini':
|
| 76 |
return (
|
| 77 |
-
<div className="bg-white w-full h-full rounded-
|
| 78 |
-
<
|
|
|
|
| 79 |
</div>
|
| 80 |
)
|
| 81 |
case 'harddrive':
|
|
@@ -103,12 +128,6 @@ export function DraggableDesktopIcon({
|
|
| 103 |
<Key size={32} weight="bold" className="text-white" />
|
| 104 |
</div>
|
| 105 |
)
|
| 106 |
-
case 'flutter':
|
| 107 |
-
return (
|
| 108 |
-
<div className="bg-gradient-to-br from-cyan-400 to-blue-500 w-full h-full rounded-xl flex items-center justify-center shadow-inner border border-cyan-300/30">
|
| 109 |
-
<Code size={32} weight="bold" className="text-white" />
|
| 110 |
-
</div>
|
| 111 |
-
)
|
| 112 |
default:
|
| 113 |
return (
|
| 114 |
<div className="bg-gray-400 w-full h-full rounded-xl flex items-center justify-center">
|
|
|
|
| 68 |
)
|
| 69 |
case 'research':
|
| 70 |
return (
|
| 71 |
+
<div className="bg-gradient-to-b from-purple-500 to-indigo-600 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
|
| 72 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
|
| 73 |
+
<Globe size={36} weight="duotone" className="text-white relative z-10 drop-shadow-md" />
|
| 74 |
+
<div className="absolute bottom-1.5 right-1.5 bg-white/20 p-1 rounded-full backdrop-blur-md shadow-sm">
|
| 75 |
+
<Sparkle size={10} weight="fill" className="text-yellow-300" />
|
| 76 |
+
</div>
|
| 77 |
+
</div>
|
| 78 |
+
)
|
| 79 |
+
case 'flutter':
|
| 80 |
+
return (
|
| 81 |
+
<div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
|
| 82 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
|
| 83 |
+
<DeviceMobile size={36} weight="fill" className="text-white relative z-10 drop-shadow-md" />
|
| 84 |
+
<div className="absolute top-1.5 right-1.5">
|
| 85 |
+
<Lightning size={12} weight="fill" className="text-yellow-300 drop-shadow-md" />
|
| 86 |
+
</div>
|
| 87 |
+
</div>
|
| 88 |
+
)
|
| 89 |
+
case 'latex':
|
| 90 |
+
return (
|
| 91 |
+
<div className="bg-gradient-to-b from-slate-700 to-slate-900 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
|
| 92 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/10 to-transparent opacity-30" />
|
| 93 |
+
<div className="relative z-10 flex flex-col items-center justify-center">
|
| 94 |
+
<Function size={32} weight="bold" className="text-green-400 drop-shadow-md" />
|
| 95 |
+
<span className="text-[8px] font-black text-gray-300 tracking-widest mt-0.5">TEX</span>
|
| 96 |
+
</div>
|
| 97 |
</div>
|
| 98 |
)
|
| 99 |
case 'gemini':
|
| 100 |
return (
|
| 101 |
+
<div className="bg-gradient-to-b from-white to-blue-50 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/50 relative overflow-hidden group-hover:shadow-2xl transition-all duration-300">
|
| 102 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/40 to-transparent" />
|
| 103 |
+
<Sparkle size={36} weight="fill" className="text-blue-500 relative z-10 drop-shadow-sm" />
|
| 104 |
</div>
|
| 105 |
)
|
| 106 |
case 'harddrive':
|
|
|
|
| 128 |
<Key size={32} weight="bold" className="text-white" />
|
| 129 |
</div>
|
| 130 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
default:
|
| 132 |
return (
|
| 133 |
<div className="bg-gray-400 w-full h-full rounded-xl flex items-center justify-center">
|
app/components/FileManager.tsx
CHANGED
|
@@ -31,7 +31,8 @@ import {
|
|
| 31 |
Function,
|
| 32 |
Calendar as CalendarIcon,
|
| 33 |
Clock as ClockIcon,
|
| 34 |
-
Sparkle
|
|
|
|
| 35 |
} from '@phosphor-icons/react'
|
| 36 |
import { motion } from 'framer-motion'
|
| 37 |
import { FilePreview } from './FilePreview'
|
|
@@ -326,13 +327,50 @@ export function FileManager({ currentPath, onNavigate, onClose, onOpenFlutterApp
|
|
| 326 |
}
|
| 327 |
|
| 328 |
const applications = [
|
| 329 |
-
{ id: 'files', name: 'Finder', icon: <FolderIcon size={
|
| 330 |
-
{ id: 'calendar', name: 'Calendar', icon: <CalendarIcon size={48} weight="regular" className="text-red-500"
|
| 331 |
-
{ id: 'clock', name: 'Clock', icon: <ClockIcon size={48} weight="regular" className="text-black"
|
| 332 |
-
{
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
]
|
| 337 |
|
| 338 |
return (
|
|
|
|
| 31 |
Function,
|
| 32 |
Calendar as CalendarIcon,
|
| 33 |
Clock as ClockIcon,
|
| 34 |
+
Sparkle,
|
| 35 |
+
Lightning
|
| 36 |
} from '@phosphor-icons/react'
|
| 37 |
import { motion } from 'framer-motion'
|
| 38 |
import { FilePreview } from './FilePreview'
|
|
|
|
| 327 |
}
|
| 328 |
|
| 329 |
const applications = [
|
| 330 |
+
{ id: 'files', name: 'Finder', icon: <div className="bg-gradient-to-br from-blue-400 to-cyan-200 w-full h-full rounded-xl flex items-center justify-center border border-white/30"><FolderIcon size={32} weight="regular" className="text-blue-900" /></div> },
|
| 331 |
+
{ id: 'calendar', name: 'Calendar', icon: <div className="w-full h-full"><CalendarIcon size={48} weight="regular" className="text-red-500" /></div> },
|
| 332 |
+
{ id: 'clock', name: 'Clock', icon: <div className="w-full h-full"><ClockIcon size={48} weight="regular" className="text-black" /></div> },
|
| 333 |
+
{
|
| 334 |
+
id: 'gemini', name: 'Gemini', icon: (
|
| 335 |
+
<div className="bg-gradient-to-b from-white to-blue-50 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/50 relative overflow-hidden">
|
| 336 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/40 to-transparent" />
|
| 337 |
+
<Sparkle size={32} weight="fill" className="text-blue-500 drop-shadow-sm" />
|
| 338 |
+
</div>
|
| 339 |
+
)
|
| 340 |
+
},
|
| 341 |
+
{
|
| 342 |
+
id: 'research', name: 'Research', icon: (
|
| 343 |
+
<div className="bg-gradient-to-b from-purple-500 to-indigo-600 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
|
| 344 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
|
| 345 |
+
<Globe size={32} weight="duotone" className="text-white relative z-10 drop-shadow-md" />
|
| 346 |
+
<div className="absolute bottom-1.5 right-1.5 bg-white/20 p-1 rounded-full backdrop-blur-md shadow-sm">
|
| 347 |
+
<Sparkle size={10} weight="fill" className="text-yellow-300" />
|
| 348 |
+
</div>
|
| 349 |
+
</div>
|
| 350 |
+
)
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
id: 'flutter-editor', name: 'Flutter IDE', icon: (
|
| 354 |
+
<div className="bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
|
| 355 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50" />
|
| 356 |
+
<DeviceMobile size={32} weight="fill" className="text-white relative z-10 drop-shadow-md" />
|
| 357 |
+
<div className="absolute top-1.5 right-1.5">
|
| 358 |
+
<Lightning size={12} weight="fill" className="text-yellow-300 drop-shadow-md" />
|
| 359 |
+
</div>
|
| 360 |
+
</div>
|
| 361 |
+
)
|
| 362 |
+
},
|
| 363 |
+
{
|
| 364 |
+
id: 'latex-editor', name: 'LaTeX Studio', icon: (
|
| 365 |
+
<div className="bg-gradient-to-b from-slate-700 to-slate-900 w-full h-full rounded-[22%] flex items-center justify-center shadow-lg border-[0.5px] border-white/20 relative overflow-hidden">
|
| 366 |
+
<div className="absolute inset-0 bg-gradient-to-b from-white/10 to-transparent opacity-30" />
|
| 367 |
+
<div className="relative z-10 flex flex-col items-center justify-center">
|
| 368 |
+
<Function size={32} weight="bold" className="text-green-400 drop-shadow-md" />
|
| 369 |
+
<span className="text-[8px] font-black text-gray-300 tracking-widest mt-0.5">TEX</span>
|
| 370 |
+
</div>
|
| 371 |
+
</div>
|
| 372 |
+
)
|
| 373 |
+
},
|
| 374 |
]
|
| 375 |
|
| 376 |
return (
|
app/components/FlutterCodeEditor.tsx
CHANGED
|
@@ -129,23 +129,10 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
| 129 |
|
| 130 |
setIsGenerating(true)
|
| 131 |
try {
|
| 132 |
-
//
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
body: JSON.stringify({ prompt: claudePrompt })
|
| 137 |
-
})
|
| 138 |
-
|
| 139 |
-
if (response.ok) {
|
| 140 |
-
const data = await response.json()
|
| 141 |
-
setGeneratedCode(data.code || '')
|
| 142 |
-
// Optionally auto-apply generated code
|
| 143 |
-
// setCode(data.code || code)
|
| 144 |
-
}
|
| 145 |
-
} catch (error) {
|
| 146 |
-
console.error('Failed to generate code:', error)
|
| 147 |
-
// For demo purposes, generate mock code
|
| 148 |
-
const mockGenerated = `// Generated code for: ${claudePrompt}
|
| 149 |
import 'package:flutter/material.dart';
|
| 150 |
|
| 151 |
void main() => runApp(MyApp());
|
|
@@ -155,10 +142,31 @@ class MyApp extends StatelessWidget {
|
|
| 155 |
Widget build(BuildContext context) {
|
| 156 |
return MaterialApp(
|
| 157 |
title: 'Generated App',
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
home: Scaffold(
|
| 159 |
appBar: AppBar(title: Text('${claudePrompt}')),
|
| 160 |
body: Center(
|
| 161 |
-
child:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
),
|
| 163 |
),
|
| 164 |
);
|
|
|
|
| 129 |
|
| 130 |
setIsGenerating(true)
|
| 131 |
try {
|
| 132 |
+
// Note: Using MCP for direct Claude communication
|
| 133 |
+
// Generate a simple template based on prompt
|
| 134 |
+
const mockGenerated = `// Template for: ${claudePrompt}
|
| 135 |
+
// Use MCP to communicate directly with Claude Desktop for custom generation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
import 'package:flutter/material.dart';
|
| 137 |
|
| 138 |
void main() => runApp(MyApp());
|
|
|
|
| 142 |
Widget build(BuildContext context) {
|
| 143 |
return MaterialApp(
|
| 144 |
title: 'Generated App',
|
| 145 |
+
theme: ThemeData(
|
| 146 |
+
primarySwatch: Colors.blue,
|
| 147 |
+
useMaterial3: true,
|
| 148 |
+
),
|
| 149 |
home: Scaffold(
|
| 150 |
appBar: AppBar(title: Text('${claudePrompt}')),
|
| 151 |
body: Center(
|
| 152 |
+
child: Column(
|
| 153 |
+
mainAxisAlignment: MainAxisAlignment.center,
|
| 154 |
+
children: [
|
| 155 |
+
Icon(Icons.flutter_dash, size: 80, color: Colors.blue),
|
| 156 |
+
SizedBox(height: 20),
|
| 157 |
+
Text(
|
| 158 |
+
'${claudePrompt}',
|
| 159 |
+
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
| 160 |
+
textAlign: TextAlign.center,
|
| 161 |
+
),
|
| 162 |
+
SizedBox(height: 10),
|
| 163 |
+
Text(
|
| 164 |
+
'Use MCP in Claude Desktop for custom code generation',
|
| 165 |
+
style: TextStyle(fontSize: 14, color: Colors.grey),
|
| 166 |
+
textAlign: TextAlign.center,
|
| 167 |
+
),
|
| 168 |
+
],
|
| 169 |
+
),
|
| 170 |
),
|
| 171 |
),
|
| 172 |
);
|
app/components/FlutterRunner.tsx
CHANGED
|
@@ -219,7 +219,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
| 219 |
</div>
|
| 220 |
)}
|
| 221 |
|
| 222 |
-
{/* Main Content */}
|
| 223 |
<div className="flex-1 flex flex-col min-w-0">
|
| 224 |
{/* Toolbar */}
|
| 225 |
<div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
|
|
@@ -245,13 +245,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
| 245 |
</div>
|
| 246 |
|
| 247 |
<div className="flex items-center gap-2">
|
| 248 |
-
<button
|
| 249 |
-
onClick={handleRun}
|
| 250 |
-
className="flex items-center gap-2 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded text-xs font-medium transition-colors"
|
| 251 |
-
>
|
| 252 |
-
<Play size={14} weight="fill" />
|
| 253 |
-
Run
|
| 254 |
-
</button>
|
| 255 |
<button
|
| 256 |
onClick={handleDownload}
|
| 257 |
className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
|
|
@@ -262,63 +255,15 @@ class _MyHomePageState extends State<MyHomePage> {
|
|
| 262 |
</div>
|
| 263 |
</div>
|
| 264 |
|
| 265 |
-
{/*
|
| 266 |
-
<div className="flex-1
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
onChange={(value) => setCode(value || '')}
|
| 275 |
-
options={{
|
| 276 |
-
minimap: { enabled: false },
|
| 277 |
-
fontSize: 14,
|
| 278 |
-
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
| 279 |
-
lineNumbers: 'on',
|
| 280 |
-
scrollBeyondLastLine: false,
|
| 281 |
-
automaticLayout: true,
|
| 282 |
-
padding: { top: 16, bottom: 16 },
|
| 283 |
-
renderLineHighlight: 'all',
|
| 284 |
-
smoothScrolling: true,
|
| 285 |
-
cursorBlinking: 'smooth',
|
| 286 |
-
cursorSmoothCaretAnimation: 'on'
|
| 287 |
-
}}
|
| 288 |
-
/>
|
| 289 |
-
</div>
|
| 290 |
-
|
| 291 |
-
{/* Preview */}
|
| 292 |
-
<div className="w-[400px] bg-[#1e1e1e] flex flex-col border-l border-[#333]">
|
| 293 |
-
<div className="h-8 bg-[#252526] border-b border-[#333] flex items-center justify-between px-3">
|
| 294 |
-
<span className="text-xs font-bold text-gray-500 uppercase">Preview</span>
|
| 295 |
-
<div className="flex gap-1">
|
| 296 |
-
<div className="w-2 h-2 rounded-full bg-red-500/50" />
|
| 297 |
-
<div className="w-2 h-2 rounded-full bg-yellow-500/50" />
|
| 298 |
-
<div className="w-2 h-2 rounded-full bg-green-500/50" />
|
| 299 |
-
</div>
|
| 300 |
-
</div>
|
| 301 |
-
<div className="flex-1 bg-white relative overflow-hidden">
|
| 302 |
-
<iframe
|
| 303 |
-
key={key}
|
| 304 |
-
src={`https://dartpad.dev/embed-flutter.html?theme=light&run=true&split=0&code=${encodeURIComponent(code)}`}
|
| 305 |
-
className="w-full h-full border-0"
|
| 306 |
-
sandbox="allow-scripts allow-same-origin allow-popups"
|
| 307 |
-
title="Flutter Preview"
|
| 308 |
-
/>
|
| 309 |
-
{!isRunning && (
|
| 310 |
-
<div className="absolute inset-0 bg-black/5 flex items-center justify-center backdrop-blur-[1px]">
|
| 311 |
-
<button
|
| 312 |
-
onClick={handleRun}
|
| 313 |
-
className="px-4 py-2 bg-blue-500 text-white rounded-lg shadow-lg hover:bg-blue-600 transition-colors flex items-center gap-2 text-sm font-medium"
|
| 314 |
-
>
|
| 315 |
-
<Play size={16} weight="fill" />
|
| 316 |
-
Run App
|
| 317 |
-
</button>
|
| 318 |
-
</div>
|
| 319 |
-
)}
|
| 320 |
-
</div>
|
| 321 |
-
</div>
|
| 322 |
</div>
|
| 323 |
</div>
|
| 324 |
</div>
|
|
|
|
| 219 |
</div>
|
| 220 |
)}
|
| 221 |
|
| 222 |
+
{/* Main Content - DartPad Embed */}
|
| 223 |
<div className="flex-1 flex flex-col min-w-0">
|
| 224 |
{/* Toolbar */}
|
| 225 |
<div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
|
|
|
|
| 245 |
</div>
|
| 246 |
|
| 247 |
<div className="flex items-center gap-2">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
<button
|
| 249 |
onClick={handleDownload}
|
| 250 |
className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
|
|
|
|
| 255 |
</div>
|
| 256 |
</div>
|
| 257 |
|
| 258 |
+
{/* DartPad Embed */}
|
| 259 |
+
<div className="flex-1 bg-[#1e1e1e] relative overflow-hidden">
|
| 260 |
+
<iframe
|
| 261 |
+
key={key}
|
| 262 |
+
src={`https://dartpad.dev/embed-flutter.html?theme=dark&run=true&split=50&code=${encodeURIComponent(code)}`}
|
| 263 |
+
className="w-full h-full border-0"
|
| 264 |
+
sandbox="allow-scripts allow-same-origin allow-popups"
|
| 265 |
+
title="Flutter Preview"
|
| 266 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
</div>
|
| 268 |
</div>
|
| 269 |
</div>
|
app/components/LaTeXEditor.tsx
CHANGED
|
@@ -1,36 +1,44 @@
|
|
| 1 |
'use client'
|
| 2 |
|
| 3 |
import React, { useState, useEffect, useRef } from 'react'
|
| 4 |
-
import Window from './Window'
|
| 5 |
-
import { MathOperations, FloppyDisk, Download, Sparkle, Copy, Eye, FileText, Trash, FolderOpen } from '@phosphor-icons/react'
|
| 6 |
import Editor from '@monaco-editor/react'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
import katex from 'katex'
|
| 8 |
import 'katex/dist/katex.min.css'
|
| 9 |
|
| 10 |
-
interface LaTeXDocument {
|
| 11 |
-
id: string
|
| 12 |
-
name: string
|
| 13 |
-
content: string
|
| 14 |
-
createdAt: Date
|
| 15 |
-
modifiedAt: Date
|
| 16 |
-
sessionId?: string
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
interface LaTeXEditorProps {
|
| 20 |
onClose: () => void
|
| 21 |
-
sessionId: string
|
| 22 |
onMinimize?: () => void
|
| 23 |
onMaximize?: () => void
|
|
|
|
| 24 |
}
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
| 31 |
|
|
|
|
|
|
|
|
|
|
| 32 |
\\title{My LaTeX Document}
|
| 33 |
-
\\author{
|
| 34 |
\\date{\\today}
|
| 35 |
|
| 36 |
\\begin{document}
|
|
@@ -38,527 +46,258 @@ export function LaTeXEditor({ onClose, sessionId, onMinimize, onMaximize }: LaTe
|
|
| 38 |
\\maketitle
|
| 39 |
|
| 40 |
\\section{Introduction}
|
| 41 |
-
|
| 42 |
|
| 43 |
\\section{Mathematics}
|
| 44 |
-
Here
|
| 45 |
-
\\[
|
| 46 |
-
x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}
|
| 47 |
-
\\]
|
| 48 |
-
|
| 49 |
-
And here's an inline equation: $E = mc^2$
|
| 50 |
-
|
| 51 |
-
\\section{Conclusion}
|
| 52 |
-
LaTeX makes mathematical typesetting beautiful!
|
| 53 |
|
| 54 |
\\end{document}`)
|
| 55 |
|
| 56 |
-
const [
|
| 57 |
-
const [
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
// Load documents from localStorage (session-specific)
|
| 67 |
-
useEffect(() => {
|
| 68 |
-
const storageKey = `latex_documents_${sessionId}`
|
| 69 |
-
const docs = localStorage.getItem(storageKey)
|
| 70 |
-
if (docs) {
|
| 71 |
-
const parsed = JSON.parse(docs)
|
| 72 |
-
setDocuments(parsed.map((d: any) => ({
|
| 73 |
-
...d,
|
| 74 |
-
createdAt: new Date(d.createdAt),
|
| 75 |
-
modifiedAt: new Date(d.modifiedAt)
|
| 76 |
-
})))
|
| 77 |
}
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
-
//
|
| 81 |
useEffect(() => {
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
html = html.replace(/\\begin\{document\}/g, '')
|
| 98 |
-
html = html.replace(/\\end\{document\}/g, '')
|
| 99 |
-
|
| 100 |
-
// Convert sections
|
| 101 |
-
html = html.replace(/\\section\{(.*?)\}/g, '<h2>$1</h2>')
|
| 102 |
-
html = html.replace(/\\subsection\{(.*?)\}/g, '<h3>$1</h3>')
|
| 103 |
-
html = html.replace(/\\subsubsection\{(.*?)\}/g, '<h4>$1</h4>')
|
| 104 |
-
|
| 105 |
-
// Convert lists
|
| 106 |
-
html = html.replace(/\\begin\{itemize\}/g, '<ul>')
|
| 107 |
-
html = html.replace(/\\end\{itemize\}/g, '</ul>')
|
| 108 |
-
html = html.replace(/\\begin\{enumerate\}/g, '<ol>')
|
| 109 |
-
html = html.replace(/\\end\{enumerate\}/g, '</ol>')
|
| 110 |
-
html = html.replace(/\\item\s+(.*?)(?=\\item|<\/ul>|<\/ol>|$)/g, '<li>$1</li>')
|
| 111 |
-
|
| 112 |
-
// Convert display math
|
| 113 |
-
html = html.replace(/\\\[([\s\S]*?)\\\]/g, (match, math) => {
|
| 114 |
-
try {
|
| 115 |
-
return `<div class="math-display">${katex.renderToString(math, { displayMode: true, throwOnError: false })}</div>`
|
| 116 |
-
} catch {
|
| 117 |
-
return `<div class="math-error">Error rendering: ${math}</div>`
|
| 118 |
-
}
|
| 119 |
-
})
|
| 120 |
-
|
| 121 |
-
// Convert inline math
|
| 122 |
-
html = html.replace(/\$(.*?)\$/g, (match, math) => {
|
| 123 |
-
try {
|
| 124 |
-
return katex.renderToString(math, { displayMode: false, throwOnError: false })
|
| 125 |
-
} catch {
|
| 126 |
-
return `<span class="math-error">Error: ${math}</span>`
|
| 127 |
-
}
|
| 128 |
-
})
|
| 129 |
-
|
| 130 |
-
// Convert paragraphs
|
| 131 |
-
html = html.split('\n\n').map(p => p.trim() ? `<p>${p}</p>` : '').join('\n')
|
| 132 |
-
|
| 133 |
-
setRenderedContent(html)
|
| 134 |
-
} catch (error) {
|
| 135 |
-
console.error('LaTeX rendering error:', error)
|
| 136 |
-
setRenderedContent('<p class="error">Error rendering LaTeX</p>')
|
| 137 |
-
}
|
| 138 |
-
}
|
| 139 |
-
|
| 140 |
-
const handleGenerateLaTeX = async () => {
|
| 141 |
-
if (!claudePrompt.trim()) return
|
| 142 |
-
|
| 143 |
-
setIsGenerating(true)
|
| 144 |
-
try {
|
| 145 |
-
const response = await fetch('/api/claude/generate-latex', {
|
| 146 |
-
method: 'POST',
|
| 147 |
-
headers: { 'Content-Type': 'application/json' },
|
| 148 |
-
body: JSON.stringify({
|
| 149 |
-
prompt: claudePrompt,
|
| 150 |
-
sessionId
|
| 151 |
})
|
| 152 |
-
})
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
setContent(data.latex || content)
|
| 157 |
-
setClaudePrompt('')
|
| 158 |
}
|
| 159 |
-
} catch (error) {
|
| 160 |
-
console.error('Failed to generate LaTeX:', error)
|
| 161 |
-
// Generate mock LaTeX for demo
|
| 162 |
-
const mockLaTeX = generateMockLaTeX(claudePrompt)
|
| 163 |
-
setContent(mockLaTeX)
|
| 164 |
-
setClaudePrompt('')
|
| 165 |
-
} finally {
|
| 166 |
-
setIsGenerating(false)
|
| 167 |
}
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
-
const generateMockLaTeX = (prompt: string): string => {
|
| 171 |
-
return `\\documentclass{article}
|
| 172 |
-
\\usepackage{amsmath}
|
| 173 |
-
\\usepackage{amssymb}
|
| 174 |
-
|
| 175 |
-
\\title{${prompt}}
|
| 176 |
-
\\author{Generated by Claude}
|
| 177 |
-
\\date{\\today}
|
| 178 |
-
|
| 179 |
-
\\begin{document}
|
| 180 |
-
|
| 181 |
-
\\maketitle
|
| 182 |
-
|
| 183 |
-
\\section{Generated Content}
|
| 184 |
-
This LaTeX document was generated based on: "${prompt}"
|
| 185 |
-
|
| 186 |
-
\\section{Example Mathematics}
|
| 187 |
-
Here are some mathematical expressions related to your request:
|
| 188 |
-
|
| 189 |
-
\\subsection{Equations}
|
| 190 |
-
\\[
|
| 191 |
-
f(x) = \\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}
|
| 192 |
-
\\]
|
| 193 |
-
|
| 194 |
-
\\[
|
| 195 |
-
\\nabla \\times \\vec{E} = -\\frac{\\partial \\vec{B}}{\\partial t}
|
| 196 |
-
\\]
|
| 197 |
-
|
| 198 |
-
\\section{Conclusion}
|
| 199 |
-
This document demonstrates LaTeX capabilities for: ${prompt}
|
| 200 |
-
|
| 201 |
-
\\end{document}`
|
| 202 |
-
}
|
| 203 |
-
|
| 204 |
-
const handleSaveDocument = async () => {
|
| 205 |
-
setSaving(true)
|
| 206 |
-
try {
|
| 207 |
-
const docName = prompt('Enter document name:', currentDocument?.name || 'document.tex')
|
| 208 |
-
if (!docName) return
|
| 209 |
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
content: content,
|
| 214 |
-
createdAt: currentDocument?.createdAt || new Date(),
|
| 215 |
-
modifiedAt: new Date(),
|
| 216 |
-
sessionId
|
| 217 |
-
}
|
| 218 |
-
|
| 219 |
-
const updatedDocs = currentDocument
|
| 220 |
-
? documents.map(d => d.id === currentDocument.id ? newDoc : d)
|
| 221 |
-
: [...documents, newDoc]
|
| 222 |
-
|
| 223 |
-
setDocuments(updatedDocs)
|
| 224 |
-
setCurrentDocument(newDoc)
|
| 225 |
-
|
| 226 |
-
// Auto-save to session
|
| 227 |
-
useEffect(() => {
|
| 228 |
-
const saveToSession = async () => {
|
| 229 |
-
if (!sessionId) return
|
| 230 |
-
setIsSaving(true)
|
| 231 |
-
try {
|
| 232 |
-
await fetch('/api/session/code', {
|
| 233 |
-
method: 'POST',
|
| 234 |
-
headers: {
|
| 235 |
-
'Content-Type': 'application/json',
|
| 236 |
-
'x-session-id': sessionId
|
| 237 |
-
},
|
| 238 |
-
body: JSON.stringify({
|
| 239 |
-
type: 'latex',
|
| 240 |
-
code,
|
| 241 |
-
filename: 'main.tex'
|
| 242 |
-
})
|
| 243 |
-
})
|
| 244 |
-
} catch (error) {
|
| 245 |
-
console.error('Failed to save to session:', error)
|
| 246 |
-
} finally {
|
| 247 |
-
setIsSaving(false)
|
| 248 |
-
}
|
| 249 |
-
}
|
| 250 |
-
|
| 251 |
-
const debounce = setTimeout(saveToSession, 2000)
|
| 252 |
-
return () => clearTimeout(debounce)
|
| 253 |
-
}, [code, sessionId])
|
| 254 |
-
|
| 255 |
-
// Render LaTeX preview
|
| 256 |
-
useEffect(() => {
|
| 257 |
-
if (previewRef.current) {
|
| 258 |
-
try {
|
| 259 |
-
// Simple regex-based rendering for demo purposes
|
| 260 |
-
// In a real app, you'd use a full LaTeX engine or server-side rendering
|
| 261 |
-
const text = code
|
| 262 |
-
.replace(/\\section\{([^}]+)\}/g, '<h2>$1</h2>')
|
| 263 |
-
.replace(/\\subsection\{([^}]+)\}/g, '<h3>$1</h3>')
|
| 264 |
-
.replace(/\\textbf\{([^}]+)\}/g, '<b>$1</b>')
|
| 265 |
-
.replace(/\\textit\{([^}]+)\}/g, '<i>$1</i>')
|
| 266 |
-
.replace(/\\maketitle/, '<div class="title"><h1>My LaTeX Document</h1><p>Reuben OS</p><p>' + new Date().toLocaleDateString() + '</p></div>')
|
| 267 |
-
.replace(/\\begin\{document\}/, '')
|
| 268 |
-
.replace(/\\end\{document\}/, '')
|
| 269 |
-
.replace(/\\documentclass\{[^}]+\}/, '')
|
| 270 |
-
.replace(/\\usepackage\{[^}]+\}/, '')
|
| 271 |
-
.replace(/\\title\{[^}]+\}/, '')
|
| 272 |
-
.replace(/\\author\{[^}]+\}/, '')
|
| 273 |
-
.replace(/\\date\{[^}]+\}/, '')
|
| 274 |
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
}
|
| 298 |
-
}
|
| 299 |
-
}
|
| 300 |
-
|
| 301 |
-
const handleDownload = () => {
|
| 302 |
-
const blob = new Blob([code], { type: 'text/plain' })
|
| 303 |
-
const url = URL.createObjectURL(blob)
|
| 304 |
-
const a = document.createElement('a')
|
| 305 |
-
a.href = url
|
| 306 |
-
a.download = 'main.tex'
|
| 307 |
-
a.click()
|
| 308 |
-
URL.revokeObjectURL(url)
|
| 309 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
</div>
|
| 332 |
-
<div className="
|
| 333 |
-
|
| 334 |
-
<div
|
| 335 |
-
<
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
{
|
| 340 |
-
<div
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
onClick={() => setActiveFileId(child.id)}
|
| 349 |
-
className={`flex items-center gap-2 py-1 px-2 ml-4 text-sm rounded cursor-pointer ${activeFileId === child.id ? 'bg-[#37373d] text-white' : 'text-gray-400 hover:bg-[#2a2d2e]'
|
| 350 |
-
}`}
|
| 351 |
-
>
|
| 352 |
-
<FileText size={14} />
|
| 353 |
-
{child.name}
|
| 354 |
-
</div>
|
| 355 |
-
))}
|
| 356 |
</div>
|
| 357 |
))}
|
| 358 |
</div>
|
| 359 |
-
|
| 360 |
</div>
|
| 361 |
</div>
|
| 362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
<
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
<div className="flex items-center gap-2">
|
| 390 |
-
<
|
| 391 |
-
className="flex items-center gap-2 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded text-xs font-medium transition-colors"
|
| 392 |
-
>
|
| 393 |
-
<Play size={14} weight="fill" />
|
| 394 |
-
Compile
|
| 395 |
-
</button>
|
| 396 |
-
<button
|
| 397 |
-
onClick={handleDownload}
|
| 398 |
-
className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
|
| 399 |
-
title="Download PDF"
|
| 400 |
-
>
|
| 401 |
-
<Download size={16} />
|
| 402 |
-
</button>
|
| 403 |
</div>
|
| 404 |
</div>
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
value={code}
|
| 415 |
-
onChange={(value) => setCode(value || '')}
|
| 416 |
-
options={{
|
| 417 |
-
minimap: { enabled: false },
|
| 418 |
-
fontSize: 14,
|
| 419 |
-
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
| 420 |
-
lineNumbers: 'on',
|
| 421 |
-
scrollBeyondLastLine: false,
|
| 422 |
-
automaticLayout: true,
|
| 423 |
-
padding: { top: 16, bottom: 16 },
|
| 424 |
-
renderLineHighlight: 'all',
|
| 425 |
-
smoothScrolling: true,
|
| 426 |
-
cursorBlinking: 'smooth',
|
| 427 |
-
cursorSmoothCaretAnimation: 'on'
|
| 428 |
-
}}
|
| 429 |
-
/>
|
| 430 |
-
</div>
|
| 431 |
-
|
| 432 |
-
{/* Preview */}
|
| 433 |
-
<div className="w-[500px] bg-[#525659] flex flex-col border-l border-[#333]">
|
| 434 |
-
<div className="h-8 bg-[#323639] border-b border-[#2b2b2b] flex items-center justify-between px-3 shadow-md z-10">
|
| 435 |
-
<span className="text-xs font-bold text-gray-400 uppercase">PDF Preview</span>
|
| 436 |
-
<div className="flex items-center gap-2">
|
| 437 |
-
<span className="text-xs text-gray-400">100%</span>
|
| 438 |
-
</div>
|
| 439 |
-
</div>
|
| 440 |
-
<div className="flex-1 overflow-y-auto p-8 flex justify-center">
|
| 441 |
-
<div
|
| 442 |
-
className="bg-white w-full max-w-[800px] min-h-[1000px] shadow-2xl p-12 text-black font-serif"
|
| 443 |
-
ref={previewRef}
|
| 444 |
-
style={{
|
| 445 |
-
boxShadow: '0 0 20px rgba(0,0,0,0.5)'
|
| 446 |
-
}}
|
| 447 |
-
dangerouslySetInnerHTML={{ __html: renderedContent }}
|
| 448 |
-
/>
|
| 449 |
-
</div>
|
| 450 |
-
</div>
|
| 451 |
-
)}
|
| 452 |
-
|
| 453 |
-
{/* Documents Sidebar */}
|
| 454 |
-
<div className="w-64 bg-gray-50 border-l border-gray-200 flex flex-col">
|
| 455 |
-
<div className="p-4 border-b border-gray-200">
|
| 456 |
-
<span className="text-xs font-bold text-gray-400 uppercase tracking-wider">Saved Documents</span>
|
| 457 |
-
</div>
|
| 458 |
-
|
| 459 |
-
<div className="flex-1 overflow-y-auto p-3 space-y-2">
|
| 460 |
-
{documents.length === 0 ? (
|
| 461 |
-
<div className="text-gray-400 text-sm text-center py-8 italic">
|
| 462 |
-
No saved documents
|
| 463 |
-
</div>
|
| 464 |
-
) : (
|
| 465 |
-
documents.map(doc => (
|
| 466 |
-
<div
|
| 467 |
-
key={doc.id}
|
| 468 |
-
className={`group rounded-lg p-3 cursor-pointer border transition-all ${currentDocument?.id === doc.id
|
| 469 |
-
? 'bg-white border-purple-200 shadow-sm ring-1 ring-purple-500/20'
|
| 470 |
-
: 'bg-transparent border-transparent hover:bg-white hover:border-gray-200'
|
| 471 |
-
}`}
|
| 472 |
-
onClick={() => handleLoadDocument(doc)}
|
| 473 |
-
>
|
| 474 |
-
<div className="flex items-center justify-between mb-1">
|
| 475 |
-
<div className="flex items-center gap-2 overflow-hidden">
|
| 476 |
-
<FileText size={16} className={currentDocument?.id === doc.id ? "text-purple-500" : "text-gray-400"} weight={currentDocument?.id === doc.id ? "fill" : "regular"} />
|
| 477 |
-
<span className={`text-sm font-medium truncate ${currentDocument?.id === doc.id ? "text-gray-900" : "text-gray-600"}`}>
|
| 478 |
-
{doc.name}
|
| 479 |
-
</span>
|
| 480 |
-
</div>
|
| 481 |
-
<button
|
| 482 |
-
onClick={(e) => {
|
| 483 |
-
e.stopPropagation()
|
| 484 |
-
handleDeleteDocument(doc.id)
|
| 485 |
-
}}
|
| 486 |
-
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-red-50 rounded text-gray-400 hover:text-red-500 transition-all"
|
| 487 |
-
>
|
| 488 |
-
<Trash size={14} />
|
| 489 |
-
</button>
|
| 490 |
-
</div>
|
| 491 |
-
<div className="text-[10px] text-gray-400 pl-6">
|
| 492 |
-
{doc.modifiedAt.toLocaleDateString()}
|
| 493 |
-
</div>
|
| 494 |
-
</div>
|
| 495 |
-
))
|
| 496 |
-
)}
|
| 497 |
-
</div>
|
| 498 |
</div>
|
| 499 |
</div>
|
| 500 |
-
|
| 501 |
-
{/* Style for rendered content */}
|
| 502 |
-
<style jsx global>{`
|
| 503 |
-
.math-display {
|
| 504 |
-
margin: 1.5em 0;
|
| 505 |
-
text-align: center;
|
| 506 |
-
overflow-x: auto;
|
| 507 |
-
padding: 1em 0;
|
| 508 |
-
}
|
| 509 |
-
.math-error {
|
| 510 |
-
color: #ef4444;
|
| 511 |
-
font-style: italic;
|
| 512 |
-
background: #fef2f2;
|
| 513 |
-
padding: 0.2em 0.5em;
|
| 514 |
-
border-radius: 4px;
|
| 515 |
-
}
|
| 516 |
-
.author {
|
| 517 |
-
text-align: center;
|
| 518 |
-
font-style: italic;
|
| 519 |
-
margin: 1em 0 2em;
|
| 520 |
-
color: #4b5563;
|
| 521 |
-
}
|
| 522 |
-
h1 {
|
| 523 |
-
font-size: 2.5em;
|
| 524 |
-
margin: 1em 0 0.5em;
|
| 525 |
-
font-weight: 700;
|
| 526 |
-
line-height: 1.2;
|
| 527 |
-
color: #111827;
|
| 528 |
-
}
|
| 529 |
-
h2 {
|
| 530 |
-
font-size: 1.75em;
|
| 531 |
-
margin: 1.5em 0 0.8em;
|
| 532 |
-
font-weight: 600;
|
| 533 |
-
color: #1f2937;
|
| 534 |
-
border-bottom: 1px solid #e5e7eb;
|
| 535 |
-
padding-bottom: 0.3em;
|
| 536 |
-
}
|
| 537 |
-
h3 {
|
| 538 |
-
font-size: 1.4em;
|
| 539 |
-
margin: 1.2em 0 0.6em;
|
| 540 |
-
font-weight: 600;
|
| 541 |
-
color: #374151;
|
| 542 |
-
}
|
| 543 |
-
h4 {
|
| 544 |
-
font-size: 1.2em;
|
| 545 |
-
margin: 1em 0 0.5em;
|
| 546 |
-
font-weight: 600;
|
| 547 |
-
color: #4b5563;
|
| 548 |
-
}
|
| 549 |
-
p {
|
| 550 |
-
margin-bottom: 1em;
|
| 551 |
-
text-align: justify;
|
| 552 |
-
}
|
| 553 |
-
ul, ol {
|
| 554 |
-
margin: 1em 0;
|
| 555 |
-
padding-left: 2em;
|
| 556 |
-
}
|
| 557 |
-
li {
|
| 558 |
-
margin-bottom: 0.5em;
|
| 559 |
-
}
|
| 560 |
-
`}</style>
|
| 561 |
</div>
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
'use client'
|
| 2 |
|
| 3 |
import React, { useState, useEffect, useRef } from 'react'
|
|
|
|
|
|
|
| 4 |
import Editor from '@monaco-editor/react'
|
| 5 |
+
import {
|
| 6 |
+
Play,
|
| 7 |
+
Download,
|
| 8 |
+
X,
|
| 9 |
+
Minus,
|
| 10 |
+
Square,
|
| 11 |
+
SidebarSimple,
|
| 12 |
+
FileText,
|
| 13 |
+
ArrowsClockwise,
|
| 14 |
+
CaretDown,
|
| 15 |
+
FilePdf
|
| 16 |
+
} from '@phosphor-icons/react'
|
| 17 |
+
import Window from './Window'
|
| 18 |
import katex from 'katex'
|
| 19 |
import 'katex/dist/katex.min.css'
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
interface LaTeXEditorProps {
|
| 22 |
onClose: () => void
|
|
|
|
| 23 |
onMinimize?: () => void
|
| 24 |
onMaximize?: () => void
|
| 25 |
+
sessionId?: string
|
| 26 |
}
|
| 27 |
|
| 28 |
+
interface FileNode {
|
| 29 |
+
id: string
|
| 30 |
+
name: string
|
| 31 |
+
type: 'file' | 'folder'
|
| 32 |
+
content?: string
|
| 33 |
+
children?: FileNode[]
|
| 34 |
+
isOpen?: boolean
|
| 35 |
+
}
|
| 36 |
|
| 37 |
+
export function LaTeXEditor({ onClose, onMinimize, onMaximize, sessionId }: LaTeXEditorProps) {
|
| 38 |
+
const [code, setCode] = useState(`\\documentclass{article}
|
| 39 |
+
\\usepackage{amsmath}
|
| 40 |
\\title{My LaTeX Document}
|
| 41 |
+
\\author{Reuben OS}
|
| 42 |
\\date{\\today}
|
| 43 |
|
| 44 |
\\begin{document}
|
|
|
|
| 46 |
\\maketitle
|
| 47 |
|
| 48 |
\\section{Introduction}
|
| 49 |
+
Welcome to LaTeX Studio on Reuben OS.
|
| 50 |
|
| 51 |
\\section{Mathematics}
|
| 52 |
+
Here is an equation:
|
| 53 |
+
\\[ E = mc^2 \\]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
\\end{document}`)
|
| 56 |
|
| 57 |
+
const [showSidebar, setShowSidebar] = useState(true)
|
| 58 |
+
const [files, setFiles] = useState<FileNode[]>([
|
| 59 |
+
{
|
| 60 |
+
id: 'root',
|
| 61 |
+
name: 'Project',
|
| 62 |
+
type: 'folder',
|
| 63 |
+
isOpen: true,
|
| 64 |
+
children: [
|
| 65 |
+
{ id: 'main', name: 'main.tex', type: 'file', content: code }
|
| 66 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
}
|
| 68 |
+
])
|
| 69 |
+
const [activeFileId, setActiveFileId] = useState('main')
|
| 70 |
+
const [isSaving, setIsSaving] = useState(false)
|
| 71 |
+
const previewRef = useRef<HTMLDivElement>(null)
|
| 72 |
|
| 73 |
+
// Auto-save to session
|
| 74 |
useEffect(() => {
|
| 75 |
+
const saveToSession = async () => {
|
| 76 |
+
if (!sessionId) return
|
| 77 |
+
setIsSaving(true)
|
| 78 |
+
try {
|
| 79 |
+
await fetch('/api/session/code', {
|
| 80 |
+
method: 'POST',
|
| 81 |
+
headers: {
|
| 82 |
+
'Content-Type': 'application/json',
|
| 83 |
+
'x-session-id': sessionId
|
| 84 |
+
},
|
| 85 |
+
body: JSON.stringify({
|
| 86 |
+
type: 'latex',
|
| 87 |
+
code,
|
| 88 |
+
filename: 'main.tex'
|
| 89 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
})
|
| 91 |
+
} catch (error) {
|
| 92 |
+
console.error('Failed to save to session:', error)
|
| 93 |
+
} finally {
|
| 94 |
+
setIsSaving(false)
|
|
|
|
|
|
|
| 95 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
+
const debounce = setTimeout(saveToSession, 2000)
|
| 99 |
+
return () => clearTimeout(debounce)
|
| 100 |
+
}, [code, sessionId])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
+
// Render LaTeX preview
|
| 103 |
+
useEffect(() => {
|
| 104 |
+
if (previewRef.current) {
|
| 105 |
+
try {
|
| 106 |
+
// Simple regex-based rendering for demo purposes
|
| 107 |
+
const text = code
|
| 108 |
+
.replace(/\\section\{([^}]+)\}/g, '<h2>$1</h2>')
|
| 109 |
+
.replace(/\\subsection\{([^}]+)\}/g, '<h3>$1</h3>')
|
| 110 |
+
.replace(/\\textbf\{([^}]+)\}/g, '<b>$1</b>')
|
| 111 |
+
.replace(/\\textit\{([^}]+)\}/g, '<i>$1</i>')
|
| 112 |
+
.replace(/\\maketitle/, '<div class="title"><h1>My LaTeX Document</h1><p>Reuben OS</p><p>' + new Date().toLocaleDateString() + '</p></div>')
|
| 113 |
+
.replace(/\\begin\{document\}/, '')
|
| 114 |
+
.replace(/\\end\{document\}/, '')
|
| 115 |
+
.replace(/\\documentclass\{[^}]+\}/, '')
|
| 116 |
+
.replace(/\\usepackage\{[^}]+\}/, '')
|
| 117 |
+
.replace(/\\title\{[^}]+\}/, '')
|
| 118 |
+
.replace(/\\author\{[^}]+\}/, '')
|
| 119 |
+
.replace(/\\date\{[^}]+\}/, '')
|
| 120 |
+
|
| 121 |
+
// Split by math delimiters to render KaTeX
|
| 122 |
+
const parts = text.split(/(\\\[[\s\S]*?\\\]|\$[^$]+\$)/)
|
| 123 |
+
previewRef.current.innerHTML = ''
|
| 124 |
+
|
| 125 |
+
parts.forEach(part => {
|
| 126 |
+
if (part.startsWith('\\[') || part.startsWith('$')) {
|
| 127 |
+
const math = part.replace(/^\\\[|\\\]$|^\$|\$$/g, '')
|
| 128 |
+
const span = document.createElement('span')
|
| 129 |
+
try {
|
| 130 |
+
katex.render(math, span, { throwOnError: false, displayMode: part.startsWith('\\[') })
|
| 131 |
+
previewRef.current?.appendChild(span)
|
| 132 |
+
} catch (e) {
|
| 133 |
+
previewRef.current?.appendChild(document.createTextNode(part))
|
| 134 |
+
}
|
| 135 |
+
} else {
|
| 136 |
+
const div = document.createElement('div')
|
| 137 |
+
div.innerHTML = part
|
| 138 |
+
previewRef.current?.appendChild(div)
|
| 139 |
}
|
| 140 |
+
})
|
| 141 |
+
} catch (e) {
|
| 142 |
+
console.error('Render error:', e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
}
|
| 144 |
+
}
|
| 145 |
+
}, [code])
|
| 146 |
+
|
| 147 |
+
const handleDownload = () => {
|
| 148 |
+
const blob = new Blob([code], { type: 'text/plain' })
|
| 149 |
+
const url = URL.createObjectURL(blob)
|
| 150 |
+
const a = document.createElement('a')
|
| 151 |
+
a.href = url
|
| 152 |
+
a.download = 'main.tex'
|
| 153 |
+
a.click()
|
| 154 |
+
URL.revokeObjectURL(url)
|
| 155 |
+
}
|
| 156 |
|
| 157 |
+
return (
|
| 158 |
+
<Window
|
| 159 |
+
id="latex-editor"
|
| 160 |
+
title="LaTeX Studio"
|
| 161 |
+
isOpen={true}
|
| 162 |
+
onClose={onClose}
|
| 163 |
+
onMinimize={onMinimize}
|
| 164 |
+
onMaximize={onMaximize}
|
| 165 |
+
width={1200}
|
| 166 |
+
height={800}
|
| 167 |
+
x={60}
|
| 168 |
+
y={60}
|
| 169 |
+
className="latex-studio-window"
|
| 170 |
+
>
|
| 171 |
+
<div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
|
| 172 |
+
{/* Sidebar */}
|
| 173 |
+
{showSidebar && (
|
| 174 |
+
<div className="w-64 bg-[#252526] border-r border-[#333] flex flex-col">
|
| 175 |
+
<div className="h-9 px-4 flex items-center text-xs font-bold text-gray-500 uppercase tracking-wider">
|
| 176 |
+
Explorer
|
| 177 |
+
</div>
|
| 178 |
+
<div className="flex-1 overflow-y-auto py-2">
|
| 179 |
+
<div className="px-2">
|
| 180 |
+
<div className="flex items-center gap-1 py-1 px-2 text-sm text-gray-300 hover:bg-[#2a2d2e] rounded cursor-pointer">
|
| 181 |
+
<CaretDown size={12} weight="bold" />
|
| 182 |
+
<span className="font-bold">PROJECT</span>
|
| 183 |
</div>
|
| 184 |
+
<div className="pl-4">
|
| 185 |
+
{files.map(file => (
|
| 186 |
+
<div key={file.id}>
|
| 187 |
+
<div className="flex items-center gap-2 py-1 px-2 text-sm hover:bg-[#2a2d2e] rounded cursor-pointer text-blue-400">
|
| 188 |
+
<CaretDown size={12} weight="bold" />
|
| 189 |
+
{file.name}
|
| 190 |
+
</div>
|
| 191 |
+
{file.children?.map(child => (
|
| 192 |
+
<div
|
| 193 |
+
key={child.id}
|
| 194 |
+
onClick={() => setActiveFileId(child.id)}
|
| 195 |
+
className={`flex items-center gap-2 py-1 px-2 ml-4 text-sm rounded cursor-pointer ${activeFileId === child.id ? 'bg-[#37373d] text-white' : 'text-gray-400 hover:bg-[#2a2d2e]'
|
| 196 |
+
}`}
|
| 197 |
+
>
|
| 198 |
+
<FileText size={14} />
|
| 199 |
+
{child.name}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
</div>
|
| 201 |
))}
|
| 202 |
</div>
|
| 203 |
+
))}
|
| 204 |
</div>
|
| 205 |
</div>
|
| 206 |
+
</div>
|
| 207 |
+
</div>
|
| 208 |
+
)}
|
| 209 |
+
|
| 210 |
+
{/* Main Content */}
|
| 211 |
+
<div className="flex-1 flex flex-col min-w-0">
|
| 212 |
+
{/* Toolbar */}
|
| 213 |
+
<div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
|
| 214 |
+
<div className="flex items-center gap-2">
|
| 215 |
+
<button
|
| 216 |
+
onClick={() => setShowSidebar(!showSidebar)}
|
| 217 |
+
className={`p-1.5 rounded hover:bg-[#3e3e42] ${showSidebar ? 'text-white' : 'text-gray-500'}`}
|
| 218 |
+
title="Toggle Sidebar"
|
| 219 |
+
>
|
| 220 |
+
<SidebarSimple size={16} />
|
| 221 |
+
</button>
|
| 222 |
+
<div className="h-4 w-[1px] bg-gray-600 mx-2" />
|
| 223 |
+
<div className="flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded text-xs text-gray-400 border border-[#333]">
|
| 224 |
+
<FileText size={14} className="text-green-400" />
|
| 225 |
+
main.tex
|
| 226 |
+
</div>
|
| 227 |
+
{isSaving && (
|
| 228 |
+
<span className="text-xs text-gray-500 flex items-center gap-1">
|
| 229 |
+
<ArrowsClockwise size={12} className="animate-spin" />
|
| 230 |
+
Syncing...
|
| 231 |
+
</span>
|
| 232 |
+
)}
|
| 233 |
+
</div>
|
| 234 |
|
| 235 |
+
<div className="flex items-center gap-2">
|
| 236 |
+
<button
|
| 237 |
+
className="flex items-center gap-2 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded text-xs font-medium transition-colors"
|
| 238 |
+
>
|
| 239 |
+
<Play size={14} weight="fill" />
|
| 240 |
+
Compile
|
| 241 |
+
</button>
|
| 242 |
+
<button
|
| 243 |
+
onClick={handleDownload}
|
| 244 |
+
className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
|
| 245 |
+
title="Download PDF"
|
| 246 |
+
>
|
| 247 |
+
<Download size={16} />
|
| 248 |
+
</button>
|
| 249 |
+
</div>
|
| 250 |
+
</div>
|
| 251 |
+
|
| 252 |
+
{/* Split View: Editor & Preview */}
|
| 253 |
+
<div className="flex-1 flex overflow-hidden">
|
| 254 |
+
{/* Editor */}
|
| 255 |
+
<div className="flex-1 border-r border-[#333]">
|
| 256 |
+
<Editor
|
| 257 |
+
height="100%"
|
| 258 |
+
defaultLanguage="latex"
|
| 259 |
+
theme="vs-dark"
|
| 260 |
+
value={code}
|
| 261 |
+
onChange={(value) => setCode(value || '')}
|
| 262 |
+
options={{
|
| 263 |
+
minimap: { enabled: false },
|
| 264 |
+
fontSize: 14,
|
| 265 |
+
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
| 266 |
+
lineNumbers: 'on',
|
| 267 |
+
scrollBeyondLastLine: false,
|
| 268 |
+
automaticLayout: true,
|
| 269 |
+
padding: { top: 16, bottom: 16 },
|
| 270 |
+
renderLineHighlight: 'all',
|
| 271 |
+
smoothScrolling: true,
|
| 272 |
+
cursorBlinking: 'smooth',
|
| 273 |
+
cursorSmoothCaretAnimation: 'on'
|
| 274 |
+
}}
|
| 275 |
+
/>
|
| 276 |
+
</div>
|
| 277 |
|
| 278 |
+
{/* Preview */}
|
| 279 |
+
<div className="w-[500px] bg-[#525659] flex flex-col border-l border-[#333]">
|
| 280 |
+
<div className="h-8 bg-[#323639] border-b border-[#2b2b2b] flex items-center justify-between px-3 shadow-md z-10">
|
| 281 |
+
<span className="text-xs font-bold text-gray-400 uppercase">PDF Preview</span>
|
| 282 |
<div className="flex items-center gap-2">
|
| 283 |
+
<span className="text-xs text-gray-400">100%</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
</div>
|
| 285 |
</div>
|
| 286 |
+
<div className="flex-1 overflow-y-auto p-8 flex justify-center">
|
| 287 |
+
<div
|
| 288 |
+
className="bg-white w-full max-w-[800px] min-h-[1000px] shadow-2xl p-12 text-black font-serif"
|
| 289 |
+
ref={previewRef}
|
| 290 |
+
style={{
|
| 291 |
+
boxShadow: '0 0 20px rgba(0,0,0,0.5)'
|
| 292 |
+
}}
|
| 293 |
+
>
|
| 294 |
+
{/* Content rendered via useEffect */}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
</div>
|
| 296 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
</div>
|
| 298 |
+
</div>
|
| 299 |
+
</div>
|
| 300 |
+
</div>
|
| 301 |
+
</Window>
|
| 302 |
+
)
|
| 303 |
+
}
|
app/components/SpotlightSearch.tsx
CHANGED
|
@@ -1,7 +1,17 @@
|
|
| 1 |
'use client'
|
| 2 |
|
| 3 |
import React, { useState, useRef, useEffect } from 'react'
|
| 4 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
import { motion, AnimatePresence } from 'framer-motion'
|
| 6 |
|
| 7 |
interface SpotlightSearchProps {
|
|
@@ -23,13 +33,13 @@ export function SpotlightSearch({ isOpen, onClose, onOpenApp }: SpotlightSearchP
|
|
| 23 |
const inputRef = useRef<HTMLInputElement>(null)
|
| 24 |
|
| 25 |
const apps: SearchResult[] = [
|
| 26 |
-
{ id: 'files', name: 'Files', type: 'app' },
|
| 27 |
-
{ id: 'calendar', name: 'Calendar', type: 'app' },
|
| 28 |
-
{ id: 'clock', name: 'Clock', type: 'app' },
|
| 29 |
-
{ id: '
|
| 30 |
-
{ id: '
|
| 31 |
-
{ id: '
|
| 32 |
-
{ id: '
|
| 33 |
]
|
| 34 |
|
| 35 |
const files: SearchResult[] = [
|
|
@@ -132,10 +142,14 @@ export function SpotlightSearch({ isOpen, onClose, onOpenApp }: SpotlightSearchP
|
|
| 132 |
<div
|
| 133 |
key={result.id}
|
| 134 |
onClick={() => handleSelect(result)}
|
| 135 |
-
className={`flex items-center px-3 py-2.5 hover:bg-blue-500 hover:text-white rounded-lg cursor-pointer transition-colors ${
|
| 136 |
-
|
| 137 |
-
}`}
|
| 138 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
<div className="flex-1 flex flex-col">
|
| 140 |
<span className="font-medium">{result.name}</span>
|
| 141 |
<span className="text-xs opacity-70 capitalize">{result.type}</span>
|
|
|
|
| 1 |
'use client'
|
| 2 |
|
| 3 |
import React, { useState, useRef, useEffect } from 'react'
|
| 4 |
+
import {
|
| 5 |
+
MagnifyingGlass,
|
| 6 |
+
X,
|
| 7 |
+
Folder,
|
| 8 |
+
Calendar,
|
| 9 |
+
Clock,
|
| 10 |
+
Sparkle,
|
| 11 |
+
Globe,
|
| 12 |
+
DeviceMobile,
|
| 13 |
+
Function as FunctionIcon
|
| 14 |
+
} from '@phosphor-icons/react'
|
| 15 |
import { motion, AnimatePresence } from 'framer-motion'
|
| 16 |
|
| 17 |
interface SpotlightSearchProps {
|
|
|
|
| 33 |
const inputRef = useRef<HTMLInputElement>(null)
|
| 34 |
|
| 35 |
const apps: SearchResult[] = [
|
| 36 |
+
{ id: 'files', name: 'Files', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-br from-blue-400 to-cyan-200 rounded-md flex items-center justify-center border border-white/30"><Folder size={16} weight="fill" className="text-blue-900" /></div> },
|
| 37 |
+
{ id: 'calendar', name: 'Calendar', type: 'app', icon: <div className="w-6 h-6 bg-white rounded-md flex items-center justify-center border border-gray-200"><Calendar size={16} weight="regular" className="text-red-500" /></div> },
|
| 38 |
+
{ id: 'clock', name: 'Clock', type: 'app', icon: <div className="w-6 h-6 bg-white rounded-full flex items-center justify-center border border-gray-200"><Clock size={16} weight="regular" className="text-black" /></div> },
|
| 39 |
+
{ id: 'gemini', name: 'Gemini Chat', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-white to-blue-50 rounded-md flex items-center justify-center border border-white/50"><Sparkle size={16} weight="fill" className="text-blue-500" /></div> },
|
| 40 |
+
{ id: 'research', name: 'Research', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-purple-500 to-indigo-600 rounded-md flex items-center justify-center border border-white/20"><Globe size={16} weight="duotone" className="text-white" /></div> },
|
| 41 |
+
{ id: 'flutter-editor', name: 'Flutter IDE', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-[#54C5F8] to-[#29B6F6] rounded-md flex items-center justify-center border border-white/20"><DeviceMobile size={16} weight="fill" className="text-white" /></div> },
|
| 42 |
+
{ id: 'latex-editor', name: 'LaTeX Studio', type: 'app', icon: <div className="w-6 h-6 bg-gradient-to-b from-slate-700 to-slate-900 rounded-md flex items-center justify-center border border-white/20"><FunctionIcon size={16} weight="bold" className="text-green-400" /></div> },
|
| 43 |
]
|
| 44 |
|
| 45 |
const files: SearchResult[] = [
|
|
|
|
| 142 |
<div
|
| 143 |
key={result.id}
|
| 144 |
onClick={() => handleSelect(result)}
|
| 145 |
+
className={`flex items-center px-3 py-2.5 hover:bg-blue-500 hover:text-white rounded-lg cursor-pointer transition-colors gap-3 ${index === 0 ? 'bg-blue-500/10' : ''
|
| 146 |
+
}`}
|
|
|
|
| 147 |
>
|
| 148 |
+
{result.icon && (
|
| 149 |
+
<div className="w-8 h-8 flex items-center justify-center">
|
| 150 |
+
{result.icon}
|
| 151 |
+
</div>
|
| 152 |
+
)}
|
| 153 |
<div className="flex-1 flex flex-col">
|
| 154 |
<span className="font-medium">{result.name}</span>
|
| 155 |
<span className="text-xs opacity-70 capitalize">{result.type}</span>
|