Reubencf commited on
Commit
c52a01c
Β·
1 Parent(s): bccb4db

fix: File preview for secure data and open .dart files in Flutter IDE with content

Browse files
app/components/Desktop.tsx CHANGED
@@ -194,9 +194,17 @@ export function Desktop() {
194
 
195
 
196
  const openFlutterCodeEditor = () => {
197
- setFlutterCodeEditorOpen(true)
198
- setFlutterCodeEditorMinimized(false)
199
- setWindowZIndices(prev => ({ ...prev, flutterCodeEditor: getNextZIndex() }))
 
 
 
 
 
 
 
 
200
  }
201
 
202
  const closeFlutterCodeEditor = () => {
 
194
 
195
 
196
  const openFlutterCodeEditor = () => {
197
+ // Check if there's file content stored from File Manager
198
+ const storedContent = sessionStorage.getItem('flutterFileContent')
199
+ if (storedContent) {
200
+ setActiveFlutterApp({ dartCode: storedContent })
201
+ sessionStorage.removeItem('flutterFileContent') // Clean up
202
+ } else {
203
+ setActiveFlutterApp(null) // Use default template
204
+ }
205
+ setFlutterRunnerOpen(true)
206
+ setFlutterRunnerMinimized(false)
207
+ setWindowZIndices(prev => ({ ...prev, flutterRunner: getNextZIndex() }))
208
  }
209
 
210
  const closeFlutterCodeEditor = () => {
app/components/FileManager.tsx CHANGED
@@ -496,13 +496,31 @@ export function FileManager({ currentPath, onNavigate, onClose, onOpenFlutterApp
496
  className="group relative"
497
  >
498
  <button
499
- onDoubleClick={() => {
500
  if (file.type === 'folder') {
501
  onNavigate(file.path)
502
  } else if (file.type === 'flutter_app' && onOpenFlutterApp) {
503
  onOpenFlutterApp(file)
504
  } else if (file.extension === 'dart' || file.extension === 'flutter') {
505
- if (onOpenApp) onOpenApp('flutter-editor')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
  } else if (file.extension === 'tex') {
507
  if (onOpenApp) onOpenApp('latex-editor')
508
  } else {
@@ -636,6 +654,8 @@ export function FileManager({ currentPath, onNavigate, onClose, onOpenFlutterApp
636
  file={previewFile}
637
  onClose={() => setPreviewFile(null)}
638
  onDownload={() => handleDownload(previewFile)}
 
 
639
  />
640
  )}
641
  </>
 
496
  className="group relative"
497
  >
498
  <button
499
+ onDoubleClick={async () => {
500
  if (file.type === 'folder') {
501
  onNavigate(file.path)
502
  } else if (file.type === 'flutter_app' && onOpenFlutterApp) {
503
  onOpenFlutterApp(file)
504
  } else if (file.extension === 'dart' || file.extension === 'flutter') {
505
+ // Load the file content and open Flutter IDE with it
506
+ try {
507
+ let fileContent = ''
508
+ if (sidebarSelection === 'secure' && passkey) {
509
+ const response = await fetch(`/api/data?key=${encodeURIComponent(passkey)}&folder=`)
510
+ const data = await response.json()
511
+ const fileData = data.files?.find((f: any) => f.name === file.name)
512
+ fileContent = fileData?.content || ''
513
+ }
514
+ // Open Flutter IDE with the file content
515
+ if (onOpenApp) {
516
+ // Store the content temporarily for the Flutter IDE to pick up
517
+ sessionStorage.setItem('flutterFileContent', fileContent)
518
+ onOpenApp('flutter-editor')
519
+ }
520
+ } catch (error) {
521
+ console.error('Error loading file:', error)
522
+ if (onOpenApp) onOpenApp('flutter-editor')
523
+ }
524
  } else if (file.extension === 'tex') {
525
  if (onOpenApp) onOpenApp('latex-editor')
526
  } else {
 
654
  file={previewFile}
655
  onClose={() => setPreviewFile(null)}
656
  onDownload={() => handleDownload(previewFile)}
657
+ passkey={passkey}
658
+ isPublic={sidebarSelection === 'public'}
659
  />
660
  )}
661
  </>
app/components/FilePreview.tsx CHANGED
@@ -15,9 +15,11 @@ interface FilePreviewProps {
15
  }
16
  onClose: () => void
17
  onDownload: () => void
 
 
18
  }
19
 
20
- export function FilePreview({ file, onClose, onDownload }: FilePreviewProps) {
21
  const [content, setContent] = useState<any>(null)
22
  const [loading, setLoading] = useState(true)
23
  const [error, setError] = useState<string | null>(null)
@@ -26,7 +28,11 @@ export function FilePreview({ file, onClose, onDownload }: FilePreviewProps) {
26
  // Excel specific state
27
  const [activeSheet, setActiveSheet] = useState(0)
28
 
29
- const previewUrl = `/api/download?path=${encodeURIComponent(file.path)}&preview=true`
 
 
 
 
30
  const ext = file.extension?.toLowerCase() || ''
31
 
32
  useEffect(() => {
@@ -69,6 +75,8 @@ export function FilePreview({ file, onClose, onDownload }: FilePreviewProps) {
69
  case 'sh':
70
  case 'yaml':
71
  case 'yml':
 
 
72
  await loadTextFile()
73
  break
74
  case 'csv':
@@ -124,9 +132,21 @@ export function FilePreview({ file, onClose, onDownload }: FilePreviewProps) {
124
 
125
  const loadTextFile = async () => {
126
  try {
127
- const response = await fetch(previewUrl)
128
- const text = await response.text()
129
- setContent({ type: 'text', data: text })
 
 
 
 
 
 
 
 
 
 
 
 
130
  } catch (err) {
131
  throw new Error('Failed to load text file')
132
  }
@@ -234,9 +254,8 @@ export function FilePreview({ file, onClose, onDownload }: FilePreviewProps) {
234
  <button
235
  key={index}
236
  onClick={() => setActiveSheet(index)}
237
- className={`px-3 py-1 rounded ${
238
- activeSheet === index ? 'bg-blue-500 text-white' : 'bg-gray-100'
239
- }`}
240
  >
241
  {sheet.name}
242
  </button>
 
15
  }
16
  onClose: () => void
17
  onDownload: () => void
18
+ passkey?: string
19
+ isPublic?: boolean
20
  }
21
 
22
+ export function FilePreview({ file, onClose, onDownload, passkey, isPublic = true }: FilePreviewProps) {
23
  const [content, setContent] = useState<any>(null)
24
  const [loading, setLoading] = useState(true)
25
  const [error, setError] = useState<string | null>(null)
 
28
  // Excel specific state
29
  const [activeSheet, setActiveSheet] = useState(0)
30
 
31
+ // For secure data, we read directly from /api/data
32
+ // For public, use the download API
33
+ const previewUrl = isPublic
34
+ ? `/api/download?path=${encodeURIComponent(file.path)}&preview=true`
35
+ : `/api/data?key=${encodeURIComponent(passkey || '')}&path=${encodeURIComponent(file.path)}`
36
  const ext = file.extension?.toLowerCase() || ''
37
 
38
  useEffect(() => {
 
75
  case 'sh':
76
  case 'yaml':
77
  case 'yml':
78
+ case 'dart':
79
+ case 'tex':
80
  await loadTextFile()
81
  break
82
  case 'csv':
 
132
 
133
  const loadTextFile = async () => {
134
  try {
135
+ // For secure data files, read from /api/data which returns file list with content
136
+ if (!isPublic && passkey) {
137
+ const response = await fetch(`/api/data?key=${encodeURIComponent(passkey)}&folder=`)
138
+ const data = await response.json()
139
+ const fileData = data.files?.find((f: any) => f.name === file.name)
140
+ if (fileData && fileData.content) {
141
+ setContent({ type: 'text', data: fileData.content })
142
+ } else {
143
+ throw new Error('File content not available')
144
+ }
145
+ } else {
146
+ const response = await fetch(previewUrl)
147
+ const text = await response.text()
148
+ setContent({ type: 'text', data: text })
149
+ }
150
  } catch (err) {
151
  throw new Error('Failed to load text file')
152
  }
 
254
  <button
255
  key={index}
256
  onClick={() => setActiveSheet(index)}
257
+ className={`px-3 py-1 rounded ${activeSheet === index ? 'bg-blue-500 text-white' : 'bg-gray-100'
258
+ }`}
 
259
  >
260
  {sheet.name}
261
  </button>
test-mcp-endpoint.js ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Test script for MCP handler API
2
+ // Run with: node test-mcp-endpoint.js
3
+
4
+ const BASE_URL = 'https://mcp-1st-birthday-reuben-os.hf.space';
5
+ const API_URL = `${BASE_URL}/api/mcp-handler`;
6
+
7
+ async function testMCPEndpoint() {
8
+ console.log('πŸ§ͺ Testing MCP Handler API on Hugging Face...\n');
9
+
10
+ // Test 1: GET without passkey (should fail with error)
11
+ console.log('Test 1: GET without passkey');
12
+ try {
13
+ const response = await fetch(API_URL);
14
+ const data = await response.json();
15
+ console.log('βœ… Response:', JSON.stringify(data, null, 2));
16
+ console.log('Expected error message: βœ“\n');
17
+ } catch (error) {
18
+ console.log('❌ Error:', error.message, '\n');
19
+ }
20
+
21
+ // Test 2: POST - Save a test file to public folder
22
+ console.log('Test 2: POST - Save public file');
23
+ try {
24
+ const response = await fetch(API_URL, {
25
+ method: 'POST',
26
+ headers: { 'Content-Type': 'application/json' },
27
+ body: JSON.stringify({
28
+ action: 'save_file',
29
+ fileName: 'test.txt',
30
+ content: 'Hello from MCP test!',
31
+ isPublic: true,
32
+ }),
33
+ });
34
+ const data = await response.json();
35
+ console.log('βœ… Response:', JSON.stringify(data, null, 2));
36
+ if (data.success) {
37
+ console.log('βœ“ File saved successfully to public folder!\n');
38
+ } else {
39
+ console.log('βœ— Save failed:', data.error, '\n');
40
+ }
41
+ } catch (error) {
42
+ console.log('❌ Error:', error.message, '\n');
43
+ }
44
+
45
+ // Test 3: GET - List public files
46
+ console.log('Test 3: GET - List public files');
47
+ try {
48
+ const url = new URL(API_URL);
49
+ url.searchParams.set('isPublic', 'true');
50
+
51
+ const response = await fetch(url);
52
+ const data = await response.json();
53
+ console.log('βœ… Response:', JSON.stringify(data, null, 2));
54
+ if (data.success) {
55
+ console.log(`βœ“ Found ${data.count} public file(s)\n`);
56
+ }
57
+ } catch (error) {
58
+ console.log('❌ Error:', error.message, '\n');
59
+ }
60
+
61
+ // Test 4: POST - Save with passkey
62
+ console.log('Test 4: POST - Save with passkey');
63
+ const testPasskey = 'test-passkey-123';
64
+ try {
65
+ const response = await fetch(API_URL, {
66
+ method: 'POST',
67
+ headers: { 'Content-Type': 'application/json' },
68
+ body: JSON.stringify({
69
+ passkey: testPasskey,
70
+ action: 'save_file',
71
+ fileName: 'secure_test.dart',
72
+ content: `// Flutter test file\nvoid main() {\n print('Hello from secure storage!');\n}`,
73
+ isPublic: false,
74
+ }),
75
+ });
76
+ const data = await response.json();
77
+ console.log('βœ… Response:', JSON.stringify(data, null, 2));
78
+ if (data.success) {
79
+ console.log('βœ“ File saved successfully to secure storage!\n');
80
+ }
81
+ } catch (error) {
82
+ console.log('❌ Error:', error.message, '\n');
83
+ }
84
+
85
+ // Test 5: GET - List files with passkey
86
+ console.log('Test 5: GET - List files with passkey');
87
+ try {
88
+ const url = new URL(API_URL);
89
+ url.searchParams.set('passkey', testPasskey);
90
+
91
+ const response = await fetch(url);
92
+ const data = await response.json();
93
+ console.log('βœ… Response:', JSON.stringify(data, null, 2));
94
+ if (data.success) {
95
+ console.log(`βœ“ Found ${data.count} file(s) for passkey: ${testPasskey}\n`);
96
+ }
97
+ } catch (error) {
98
+ console.log('❌ Error:', error.message, '\n');
99
+ }
100
+
101
+ // Test 6: Deploy a test quiz
102
+ console.log('Test 6: POST - Deploy quiz');
103
+ try {
104
+ const quizData = {
105
+ title: 'Test Quiz',
106
+ description: 'A test quiz from MCP',
107
+ questions: [
108
+ {
109
+ id: 'q1',
110
+ question: 'What is 2+2?',
111
+ type: 'multiple-choice',
112
+ options: ['3', '4', '5', '6'],
113
+ correctAnswer: 1,
114
+ points: 1,
115
+ },
116
+ ],
117
+ };
118
+
119
+ const response = await fetch(API_URL, {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ body: JSON.stringify({
123
+ passkey: testPasskey,
124
+ action: 'deploy_quiz',
125
+ fileName: 'quiz.json',
126
+ content: JSON.stringify(quizData, null, 2),
127
+ isPublic: false,
128
+ }),
129
+ });
130
+ const data = await response.json();
131
+ console.log('βœ… Response:', JSON.stringify(data, null, 2));
132
+ if (data.success) {
133
+ console.log('βœ“ Quiz deployed successfully!\n');
134
+ }
135
+ } catch (error) {
136
+ console.log('❌ Error:', error.message, '\n');
137
+ }
138
+
139
+ console.log('πŸŽ‰ All tests completed!\n');
140
+ console.log('Summary:');
141
+ console.log('- MCP Handler API is accessible');
142
+ console.log('- Public file uploads work');
143
+ console.log('- Passkey-protected uploads work');
144
+ console.log('- File listing works');
145
+ console.log('- Quiz deployment works');
146
+ console.log('\nβœ… Claude Desktop can now upload files to ReubenOS!');
147
+ }
148
+
149
+ // Run tests
150
+ testMCPEndpoint().catch(console.error);