Reubencf commited on
Commit
9523283
·
1 Parent(s): f7e5865

made some changes with respect to quiz and flutter MCP

Browse files
app/api/data/route.ts CHANGED
@@ -53,9 +53,12 @@ export async function GET(request: NextRequest) {
53
  const stats = await stat(itemPath)
54
  const relativePath = path.relative(userDir, itemPath)
55
 
56
- // Read content for JSON files (like quiz.json, quiz_answers.json)
57
  let content = undefined
58
- if (!stats.isDirectory() && path.extname(item).toLowerCase() === '.json') {
 
 
 
59
  try {
60
  const fileContent = await fs.promises.readFile(itemPath, 'utf-8')
61
  content = fileContent
@@ -89,18 +92,21 @@ export async function POST(request: NextRequest) {
89
  // Handle JSON body (for save_file action)
90
  if (contentType?.includes('application/json')) {
91
  const body = await request.json()
92
- const { passkey, action, fileName, content, folder = '' } = body
 
 
93
 
94
- if (!passkey) {
95
  return NextResponse.json({ error: 'Passkey is required' }, { status: 400 })
96
  }
97
 
98
  if (action === 'save_file') {
99
- if (!fileName || !content) {
 
100
  return NextResponse.json({ error: 'fileName and content are required' }, { status: 400 })
101
  }
102
 
103
- const sanitizedKey = passkey.replace(/[^a-zA-Z0-9_-]/g, '')
104
  const userDir = path.join(DATA_DIR, sanitizedKey)
105
  const targetDir = path.join(userDir, folder)
106
 
@@ -108,6 +114,10 @@ export async function POST(request: NextRequest) {
108
  await mkdir(targetDir, { recursive: true })
109
 
110
  const filePath = path.join(targetDir, fileName)
 
 
 
 
111
  await writeFile(filePath, content, 'utf-8')
112
 
113
  return NextResponse.json({ success: true })
 
53
  const stats = await stat(itemPath)
54
  const relativePath = path.relative(userDir, itemPath)
55
 
56
+ // Read content for text-based files (JSON, LaTeX, Dart, etc.)
57
  let content = undefined
58
+ const ext = path.extname(item).toLowerCase()
59
+ const textExtensions = ['.json', '.tex', '.dart', '.txt', '.md', '.html', '.css', '.js', '.ts', '.jsx', '.tsx']
60
+
61
+ if (!stats.isDirectory() && textExtensions.includes(ext)) {
62
  try {
63
  const fileContent = await fs.promises.readFile(itemPath, 'utf-8')
64
  content = fileContent
 
92
  // Handle JSON body (for save_file action)
93
  if (contentType?.includes('application/json')) {
94
  const body = await request.json()
95
+ // Support both 'passkey' and 'key' for backwards compatibility
96
+ const { passkey, key, action, fileName, content, folder = '' } = body
97
+ const actualKey = passkey || key
98
 
99
+ if (!actualKey) {
100
  return NextResponse.json({ error: 'Passkey is required' }, { status: 400 })
101
  }
102
 
103
  if (action === 'save_file') {
104
+ if (!fileName || content === undefined || content === null) {
105
+ console.log('Save file validation failed:', { fileName, contentLength: content?.length || 0 })
106
  return NextResponse.json({ error: 'fileName and content are required' }, { status: 400 })
107
  }
108
 
109
+ const sanitizedKey = actualKey.replace(/[^a-zA-Z0-9_-]/g, '')
110
  const userDir = path.join(DATA_DIR, sanitizedKey)
111
  const targetDir = path.join(userDir, folder)
112
 
 
114
  await mkdir(targetDir, { recursive: true })
115
 
116
  const filePath = path.join(targetDir, fileName)
117
+
118
+ // Debug logging
119
+ console.log(`Saving file: ${filePath}, content length: ${content.length}`)
120
+
121
  await writeFile(filePath, content, 'utf-8')
122
 
123
  return NextResponse.json({ success: true })
app/components/FlutterRunner.tsx CHANGED
@@ -6,8 +6,8 @@ import {
6
  Download,
7
  SidebarSimple,
8
  FileCode,
9
- CaretDown,
10
- Play
11
  } from '@phosphor-icons/react'
12
  import Window from './Window'
13
 
@@ -98,7 +98,7 @@ class _MyHomePageState extends State<MyHomePage> {
98
 
99
  export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }: FlutterRunnerProps) {
100
  const [code, setCode] = useState(initialCode || DEFAULT_FLUTTER_CODE)
101
- const [key, setKey] = useState(0)
102
  const [showFiles, setShowFiles] = useState(true)
103
  const [files, setFiles] = useState<FileNode[]>([])
104
  const [activeFileId, setActiveFileId] = useState('main')
@@ -235,10 +235,6 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
235
  return () => clearTimeout(saveTimer)
236
  }, [code, passkey, activeFileName, isUnlocked])
237
 
238
- const handleRun = () => {
239
- setKey(prev => prev + 1)
240
- }
241
-
242
  const handleDownload = () => {
243
  const blob = new Blob([code], { type: 'text/plain' })
244
  const url = URL.createObjectURL(blob)
@@ -288,7 +284,8 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
288
  </div>
289
  ) : (
290
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
291
- {/* Code Editor - Left Side */}
 
292
  <div className="w-[600px] flex flex-col border-r border-[#333]">
293
  {/* Toolbar */}
294
  <div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
@@ -313,14 +310,6 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
313
  </div>
314
 
315
  <div className="flex items-center gap-2">
316
- <button
317
- onClick={handleRun}
318
- className="flex items-center gap-1 px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded text-xs font-medium transition-colors"
319
- title="Run in DartPad"
320
- >
321
- <Play size={14} weight="fill" />
322
- Run
323
- </button>
324
  <button
325
  onClick={handleDownload}
326
  className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
@@ -355,15 +344,31 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
355
  />
356
  </div>
357
  </div>
 
358
 
359
- {/* DartPad Preview - Center */}
360
  <div className="flex-1 flex flex-col">
361
- <div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-center px-4">
362
- <span className="text-xs text-gray-400 uppercase font-bold">Live Preview</span>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  </div>
364
  <div className="flex-1 bg-[#1e1e1e] relative overflow-hidden">
365
  <iframe
366
- key={key}
367
  src={`https://dartpad.dev/embed-flutter.html?theme=dark&run=true&split=50&code=${encodeURIComponent(code)}`}
368
  className="w-full h-full border-0"
369
  sandbox="allow-scripts allow-same-origin allow-popups"
@@ -372,8 +377,8 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
372
  </div>
373
  </div>
374
 
375
- {/* File List - Right Side */}
376
- {showFiles && (
377
  <div className="w-64 bg-[#252526] border-l border-[#333] flex flex-col">
378
  <div className="h-9 px-4 flex items-center text-xs font-bold text-gray-500 uppercase tracking-wider">
379
  Project Files
 
6
  Download,
7
  SidebarSimple,
8
  FileCode,
9
+ CaretDown
10
+
11
  } from '@phosphor-icons/react'
12
  import Window from './Window'
13
 
 
98
 
99
  export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }: FlutterRunnerProps) {
100
  const [code, setCode] = useState(initialCode || DEFAULT_FLUTTER_CODE)
101
+ const [showEditor, setShowEditor] = useState(true)
102
  const [showFiles, setShowFiles] = useState(true)
103
  const [files, setFiles] = useState<FileNode[]>([])
104
  const [activeFileId, setActiveFileId] = useState('main')
 
235
  return () => clearTimeout(saveTimer)
236
  }, [code, passkey, activeFileName, isUnlocked])
237
 
 
 
 
 
238
  const handleDownload = () => {
239
  const blob = new Blob([code], { type: 'text/plain' })
240
  const url = URL.createObjectURL(blob)
 
284
  </div>
285
  ) : (
286
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
287
+ {/* Code Editor - Conditionally Rendered */}
288
+ {showEditor && (
289
  <div className="w-[600px] flex flex-col border-r border-[#333]">
290
  {/* Toolbar */}
291
  <div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
 
310
  </div>
311
 
312
  <div className="flex items-center gap-2">
 
 
 
 
 
 
 
 
313
  <button
314
  onClick={handleDownload}
315
  className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
 
344
  />
345
  </div>
346
  </div>
347
+ )}
348
 
349
+ {/* DartPad Preview - Takes full width when editor is hidden */}
350
  <div className="flex-1 flex flex-col">
351
+ <div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
352
+ <button
353
+ onClick={() => setShowEditor(!showEditor)}
354
+ className={`flex items-center gap-2 px-2 py-1 rounded transition-all ${showEditor
355
+ ? 'bg-[#3e3e42] text-white hover:bg-[#4e4e52]'
356
+ : 'bg-blue-600 text-white hover:bg-blue-700'}`}
357
+ title={showEditor ? "Hide Code Editor" : "Show Code Editor"}
358
+ >
359
+ <SidebarSimple size={16} weight={showEditor ? "fill" : "regular"} />
360
+ <span className="text-xs font-medium">
361
+ {showEditor ? "Hide Editor" : "Show Editor"}
362
+ </span>
363
+ </button>
364
+ <span className="text-xs text-gray-400 uppercase font-bold">
365
+ Live Preview {!showEditor && "- Full Screen"}
366
+ </span>
367
+ <div className="w-24" /> {/* Spacer for centering */}
368
  </div>
369
  <div className="flex-1 bg-[#1e1e1e] relative overflow-hidden">
370
  <iframe
371
+
372
  src={`https://dartpad.dev/embed-flutter.html?theme=dark&run=true&split=50&code=${encodeURIComponent(code)}`}
373
  className="w-full h-full border-0"
374
  sandbox="allow-scripts allow-same-origin allow-popups"
 
377
  </div>
378
  </div>
379
 
380
+ {/* File List - Right Side - Only show when editor is visible */}
381
+ {showEditor && showFiles && (
382
  <div className="w-64 bg-[#252526] border-l border-[#333] flex flex-col">
383
  <div className="h-9 px-4 flex items-center text-xs font-bold text-gray-500 uppercase tracking-wider">
384
  Project Files
public/data/rreuben/main.dart ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import 'package:flutter/material.dart';
2
+
3
+ void main() {
4
+ runApp(const MyApp());
5
+ }
6
+
7
+ class MyApp extends StatelessWidget {
8
+ const MyApp({super.key});
9
+
10
+ @override
11
+ Widget build(BuildContext context) {
12
+ return MaterialApp(
13
+ title: 'Flutter Demo',
14
+ theme: ThemeData(
15
+ primarySwatch: Colors.blue,
16
+ useMaterial3: true,
17
+ ),
18
+ home: const MyHomePage(title: 'Flutter Demo Home Page'),
19
+ );
20
+ }
21
+ }
22
+
23
+ class MyHomePage extends StatefulWidget {
24
+ const MyHomePage({super.key, required this.title});
25
+
26
+ final String title;
27
+
28
+ @override
29
+ State<MyHomePage> createState() => _MyHomePageState();
30
+ }
31
+
32
+ class _MyHomePageState extends State<MyHomePage> {
33
+ int _counter = 0;
34
+
35
+ void _incrementCounter() {
36
+ setState(() {
37
+ _counter++;
38
+ });
39
+ }
40
+
41
+ @override
42
+ Widget build(BuildContext context) {
43
+ return Scaffold(
44
+ appBar: AppBar(
45
+ title: Text(widget.title),
46
+ ),
47
+ body: Center(
48
+ child: Column(
49
+ mainAxisAlignment: MainAxisAlignment.center,
50
+ children: <Widget>[
51
+ const Text(
52
+ 'You have pushed the button this many times:',
53
+ ),
54
+ Text(
55
+ '$_counter',
56
+ style: Theme.of(context).textTheme.headlineMedium,
57
+ ),
58
+ ],
59
+ ),
60
+ ),
61
+ floatingActionButton: FloatingActionButton(
62
+ onPressed: _incrementCounter,
63
+ tooltip: 'Increment',
64
+ child: const Icon(Icons.add),
65
+ ),
66
+ );
67
+ }
68
+ }