Reubencf commited on
Commit
5de3541
·
1 Parent(s): c52a01c

feat: Redesign Flutter IDE - Editor + DartPad side-by-side with auto-save

Browse files
app/components/FileManager.tsx CHANGED
@@ -510,6 +510,10 @@ export function FileManager({ currentPath, onNavigate, onClose, onOpenFlutterApp
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) {
 
510
  const data = await response.json()
511
  const fileData = data.files?.find((f: any) => f.name === file.name)
512
  fileContent = fileData?.content || ''
513
+
514
+ // Store for auto-save in Flutter IDE
515
+ sessionStorage.setItem('currentPasskey', passkey)
516
+ sessionStorage.setItem('currentFileName', file.name)
517
  }
518
  // Open Flutter IDE with the file content
519
  if (onOpenApp) {
app/components/FlutterRunner.tsx CHANGED
@@ -3,18 +3,11 @@
3
  import React, { useState, useEffect } from 'react'
4
  import Editor from '@monaco-editor/react'
5
  import {
6
- Play,
7
  Download,
8
- X,
9
- Minus,
10
- Square,
11
- DeviceMobile,
12
  SidebarSimple,
13
  FileCode,
14
- ArrowsClockwise,
15
- Check,
16
- CaretRight,
17
- CaretDown
18
  } from '@phosphor-icons/react'
19
  import Window from './Window'
20
 
@@ -104,9 +97,8 @@ class _MyHomePageState extends State<MyHomePage> {
104
  }
105
  }`)
106
 
107
- const [isRunning, setIsRunning] = useState(false)
108
  const [key, setKey] = useState(0)
109
- const [showSidebar, setShowSidebar] = useState(true)
110
  const [files, setFiles] = useState<FileNode[]>([
111
  {
112
  id: 'root',
@@ -119,9 +111,39 @@ class _MyHomePageState extends State<MyHomePage> {
119
  }
120
  ])
121
  const [activeFileId, setActiveFileId] = useState('main')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  const handleRun = () => {
124
- setIsRunning(true)
125
  setKey(prev => prev + 1)
126
  }
127
 
@@ -143,60 +165,22 @@ class _MyHomePageState extends State<MyHomePage> {
143
  onClose={onClose}
144
  onMinimize={onMinimize}
145
  onMaximize={onMaximize}
146
- width={1200}
147
  height={800}
148
  x={40}
149
  y={40}
150
  className="flutter-ide-window"
151
  >
152
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
153
- {/* Sidebar */}
154
- {showSidebar && (
155
- <div className="w-64 bg-[#252526] border-r border-[#333] flex flex-col">
156
- <div className="h-9 px-4 flex items-center text-xs font-bold text-gray-500 uppercase tracking-wider">
157
- Explorer
158
- </div>
159
- <div className="flex-1 overflow-y-auto py-2">
160
- <div className="px-2">
161
- <div className="flex items-center gap-1 py-1 px-2 text-sm text-gray-300 hover:bg-[#2a2d2e] rounded cursor-pointer">
162
- <CaretDown size={12} weight="bold" />
163
- <span className="font-bold">PROJECT</span>
164
- </div>
165
- <div className="pl-4">
166
- {files.map(file => (
167
- <div key={file.id}>
168
- <div className="flex items-center gap-2 py-1 px-2 text-sm hover:bg-[#2a2d2e] rounded cursor-pointer text-blue-400">
169
- <CaretDown size={12} weight="bold" />
170
- {file.name}
171
- </div>
172
- {file.children?.map(child => (
173
- <div
174
- key={child.id}
175
- onClick={() => setActiveFileId(child.id)}
176
- 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]'
177
- }`}
178
- >
179
- <FileCode size={14} />
180
- {child.name}
181
- </div>
182
- ))}
183
- </div>
184
- ))}
185
- </div>
186
- </div>
187
- </div>
188
- </div>
189
- )}
190
-
191
- {/* Main Content - DartPad Embed */}
192
- <div className="flex-1 flex flex-col min-w-0">
193
  {/* Toolbar */}
194
  <div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
195
  <div className="flex items-center gap-2">
196
  <button
197
- onClick={() => setShowSidebar(!showSidebar)}
198
- className={`p-1.5 rounded hover:bg-[#3e3e42] ${showSidebar ? 'text-white' : 'text-gray-500'}`}
199
- title="Toggle Sidebar"
200
  >
201
  <SidebarSimple size={16} />
202
  </button>
@@ -205,9 +189,22 @@ class _MyHomePageState extends State<MyHomePage> {
205
  <FileCode size={14} className="text-blue-400" />
206
  main.dart
207
  </div>
 
 
 
 
 
208
  </div>
209
 
210
  <div className="flex items-center gap-2">
 
 
 
 
 
 
 
 
211
  <button
212
  onClick={handleDownload}
213
  className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
@@ -218,7 +215,36 @@ class _MyHomePageState extends State<MyHomePage> {
218
  </div>
219
  </div>
220
 
221
- {/* DartPad Embed */}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  <div className="flex-1 bg-[#1e1e1e] relative overflow-hidden">
223
  <iframe
224
  key={key}
@@ -229,6 +255,44 @@ class _MyHomePageState extends State<MyHomePage> {
229
  />
230
  </div>
231
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  </div>
233
  </Window>
234
  )
 
3
  import React, { useState, useEffect } from 'react'
4
  import Editor from '@monaco-editor/react'
5
  import {
 
6
  Download,
 
 
 
 
7
  SidebarSimple,
8
  FileCode,
9
+ CaretDown,
10
+ Play
 
 
11
  } from '@phosphor-icons/react'
12
  import Window from './Window'
13
 
 
97
  }
98
  }`)
99
 
 
100
  const [key, setKey] = useState(0)
101
+ const [showFiles, setShowFiles] = useState(true)
102
  const [files, setFiles] = useState<FileNode[]>([
103
  {
104
  id: 'root',
 
111
  }
112
  ])
113
  const [activeFileId, setActiveFileId] = useState('main')
114
+ const [lastSaved, setLastSaved] = useState<Date | null>(null)
115
+
116
+ // Auto-save to file every 2 seconds when code changes
117
+ useEffect(() => {
118
+ const saveTimer = setTimeout(async () => {
119
+ // Check if we loaded this from a file
120
+ const passkey = sessionStorage.getItem('currentPasskey')
121
+ const fileName = sessionStorage.getItem('currentFileName')
122
+
123
+ if (passkey && fileName && initialCode) {
124
+ try {
125
+ // Save to secure data
126
+ await fetch('/api/data', {
127
+ method: 'POST',
128
+ headers: { 'Content-Type': 'application/json' },
129
+ body: JSON.stringify({
130
+ key: passkey,
131
+ action: 'save',
132
+ fileName: fileName,
133
+ content: code
134
+ })
135
+ })
136
+ setLastSaved(new Date())
137
+ } catch (error) {
138
+ console.error('Auto-save failed:', error)
139
+ }
140
+ }
141
+ }, 2000)
142
+
143
+ return () => clearTimeout(saveTimer)
144
+ }, [code, initialCode])
145
 
146
  const handleRun = () => {
 
147
  setKey(prev => prev + 1)
148
  }
149
 
 
165
  onClose={onClose}
166
  onMinimize={onMinimize}
167
  onMaximize={onMaximize}
168
+ width={1400}
169
  height={800}
170
  x={40}
171
  y={40}
172
  className="flutter-ide-window"
173
  >
174
  <div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
175
+ {/* Code Editor - Left Side */}
176
+ <div className="w-[600px] flex flex-col border-r border-[#333]">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  {/* Toolbar */}
178
  <div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
179
  <div className="flex items-center gap-2">
180
  <button
181
+ onClick={() => setShowFiles(!showFiles)}
182
+ className={`p-1.5 rounded hover:bg-[#3e3e42] ${showFiles ? 'text-white' : 'text-gray-500'}`}
183
+ title="Toggle Files"
184
  >
185
  <SidebarSimple size={16} />
186
  </button>
 
189
  <FileCode size={14} className="text-blue-400" />
190
  main.dart
191
  </div>
192
+ {lastSaved && (
193
+ <span className="text-xs text-green-400">
194
+ ✓ Saved {lastSaved.toLocaleTimeString()}
195
+ </span>
196
+ )}
197
  </div>
198
 
199
  <div className="flex items-center gap-2">
200
+ <button
201
+ onClick={handleRun}
202
+ 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"
203
+ title="Run in DartPad"
204
+ >
205
+ <Play size={14} weight="fill" />
206
+ Run
207
+ </button>
208
  <button
209
  onClick={handleDownload}
210
  className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
 
215
  </div>
216
  </div>
217
 
218
+ {/* Monaco Editor */}
219
+ <div className="flex-1">
220
+ <Editor
221
+ height="100%"
222
+ defaultLanguage="dart"
223
+ theme="vs-dark"
224
+ value={code}
225
+ onChange={(value) => setCode(value || '')}
226
+ options={{
227
+ minimap: { enabled: false },
228
+ fontSize: 14,
229
+ fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
230
+ lineNumbers: 'on',
231
+ scrollBeyondLastLine: false,
232
+ automaticLayout: true,
233
+ padding: { top: 16, bottom: 16 },
234
+ renderLineHighlight: 'all',
235
+ smoothScrolling: true,
236
+ cursorBlinking: 'smooth',
237
+ cursorSmoothCaretAnimation: 'on'
238
+ }}
239
+ />
240
+ </div>
241
+ </div>
242
+
243
+ {/* DartPad Preview - Center */}
244
+ <div className="flex-1 flex flex-col">
245
+ <div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-center px-4">
246
+ <span className="text-xs text-gray-400 uppercase font-bold">Live Preview</span>
247
+ </div>
248
  <div className="flex-1 bg-[#1e1e1e] relative overflow-hidden">
249
  <iframe
250
  key={key}
 
255
  />
256
  </div>
257
  </div>
258
+
259
+ {/* File List - Right Side */}
260
+ {showFiles && (
261
+ <div className="w-64 bg-[#252526] border-l border-[#333] flex flex-col">
262
+ <div className="h-9 px-4 flex items-center text-xs font-bold text-gray-500 uppercase tracking-wider">
263
+ Project Files
264
+ </div>
265
+ <div className="flex-1 overflow-y-auto py-2">
266
+ <div className="px-2">
267
+ <div className="flex items-center gap-1 py-1 px-2 text-sm text-gray-300 hover:bg-[#2a2d2e] rounded cursor-pointer">
268
+ <CaretDown size={12} weight="bold" />
269
+ <span className="font-bold">Flutter App</span>
270
+ </div>
271
+ <div className="pl-4">
272
+ {files.map(file => (
273
+ <div key={file.id}>
274
+ <div className="flex items-center gap-2 py-1 px-2 text-sm hover:bg-[#2a2d2e] rounded cursor-pointer text-blue-400">
275
+ <CaretDown size={12} weight="bold" />
276
+ {file.name}
277
+ </div>
278
+ {file.children?.map(child => (
279
+ <div
280
+ key={child.id}
281
+ onClick={() => setActiveFileId(child.id)}
282
+ 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]'
283
+ }`}
284
+ >
285
+ <FileCode size={14} />
286
+ {child.name}
287
+ </div>
288
+ ))}
289
+ </div>
290
+ ))}
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ )}
296
  </div>
297
  </Window>
298
  )