Reubencf commited on
Commit
2a7ce2c
·
1 Parent(s): b60520b

fixing layout issue and rendering issues

Browse files
app/components/FlutterRunner.tsx CHANGED
@@ -244,6 +244,7 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
244
  a.download = activeFileName || 'main.dart'
245
  a.click()
246
  URL.revokeObjectURL(url)
 
247
 
248
  const handleCopyCode = () => {
249
  navigator.clipboard.writeText(code).then(() => {
@@ -252,7 +253,6 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
252
  console.error("Failed to copy:", err)
253
  })
254
  }
255
- }
256
 
257
  return (
258
  <Window
@@ -319,7 +319,6 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
319
  </div>
320
 
321
  <div className="flex items-center gap-2">
322
- <button
323
  <button
324
  onClick={handleCopyCode}
325
  className="flex items-center gap-1 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded text-xs font-medium transition-colors"
@@ -328,6 +327,7 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
328
  <Copy size={14} weight="bold" />
329
  Copy Code
330
  </button>
 
331
  onClick={handleDownload}
332
  className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
333
  title="Download Code"
@@ -406,16 +406,14 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, initialCode }:
406
  <div className="flex-1 overflow-y-auto py-2">
407
  <div className="px-2">
408
  <div className="flex items-center gap-1 py-1 px-2 text-sm text-gray-300 hover:bg-[#2a2d2e] rounded cursor-pointer">
409
- <CaretDown,
410
- Copy size={12} weight="bold" />
411
  <span className="font-bold">Flutter App</span>
412
  </div>
413
  <div className="pl-4">
414
  {files.map(file => (
415
  <div key={file.id}>
416
  <div className="flex items-center gap-2 py-1 px-2 text-sm hover:bg-[#2a2d2e] rounded cursor-pointer text-blue-400">
417
- <CaretDown,
418
- Copy size={12} weight="bold" />
419
  {file.name}
420
  </div>
421
  {file.children?.map(child => (
 
244
  a.download = activeFileName || 'main.dart'
245
  a.click()
246
  URL.revokeObjectURL(url)
247
+ }
248
 
249
  const handleCopyCode = () => {
250
  navigator.clipboard.writeText(code).then(() => {
 
253
  console.error("Failed to copy:", err)
254
  })
255
  }
 
256
 
257
  return (
258
  <Window
 
319
  </div>
320
 
321
  <div className="flex items-center gap-2">
 
322
  <button
323
  onClick={handleCopyCode}
324
  className="flex items-center gap-1 px-3 py-1.5 bg-green-600 hover:bg-green-700 text-white rounded text-xs font-medium transition-colors"
 
327
  <Copy size={14} weight="bold" />
328
  Copy Code
329
  </button>
330
+ <button
331
  onClick={handleDownload}
332
  className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
333
  title="Download Code"
 
406
  <div className="flex-1 overflow-y-auto py-2">
407
  <div className="px-2">
408
  <div className="flex items-center gap-1 py-1 px-2 text-sm text-gray-300 hover:bg-[#2a2d2e] rounded cursor-pointer">
409
+ <CaretDown size={12} weight="bold" />
 
410
  <span className="font-bold">Flutter App</span>
411
  </div>
412
  <div className="pl-4">
413
  {files.map(file => (
414
  <div key={file.id}>
415
  <div className="flex items-center gap-2 py-1 px-2 text-sm hover:bg-[#2a2d2e] rounded cursor-pointer text-blue-400">
416
+ <CaretDown size={12} weight="bold" />
 
417
  {file.name}
418
  </div>
419
  {file.children?.map(child => (
app/components/GeminiChat.tsx CHANGED
@@ -229,7 +229,7 @@ export function GeminiChat({ onClose, onMinimize, onMaximize, onFocus, zIndex }:
229
  className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
230
  >
231
  <div
232
- className={`max-w-[80%] ${message.role === 'user'
233
  ? 'bg-blue-600 text-white rounded-2xl rounded-tr-none'
234
  : 'bg-gray-100 text-gray-800 rounded-2xl rounded-tl-none'
235
  } px-4 py-2 text-sm`}
@@ -281,8 +281,12 @@ export function GeminiChat({ onClose, onMinimize, onMaximize, onFocus, zIndex }:
281
  value={input}
282
  onChange={(e) => setInput(e.target.value)}
283
  onKeyDown={handleKeyPress}
 
 
 
 
284
  placeholder="Ask Gemini..."
285
- className="flex-1 bg-gray-100 rounded-full px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200 transition-all"
286
  disabled={isLoading}
287
  />
288
  <button
 
229
  className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
230
  >
231
  <div
232
+ className={`max-w-[80%] select-text ${message.role === 'user'
233
  ? 'bg-blue-600 text-white rounded-2xl rounded-tr-none'
234
  : 'bg-gray-100 text-gray-800 rounded-2xl rounded-tl-none'
235
  } px-4 py-2 text-sm`}
 
281
  value={input}
282
  onChange={(e) => setInput(e.target.value)}
283
  onKeyDown={handleKeyPress}
284
+ onPaste={(e) => {
285
+ // Allow paste - default behavior
286
+ e.stopPropagation()
287
+ }}
288
  placeholder="Ask Gemini..."
289
+ className="flex-1 bg-gray-100 rounded-full px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200 transition-all select-text"
290
  disabled={isLoading}
291
  />
292
  <button
app/components/LaTeXEditor.tsx CHANGED
@@ -203,39 +203,115 @@ export function LaTeXEditor({ onClose, onMinimize, onMaximize, initialContent }:
203
  useEffect(() => {
204
  if (previewRef.current) {
205
  try {
206
- // Simple regex-based rendering for demo purposes
207
- // In a real app, we'd use a proper LaTeX to HTML converter or PDF.js
208
- const text = code
209
- .replace(/\\section\{([^}]+)\}/g, '<h2>$1</h2>')
210
- .replace(/\\subsection\{([^}]+)\}/g, '<h3>$1</h3>')
211
- .replace(/\\textbf\{([^}]+)\}/g, '<b>$1</b>')
212
- .replace(/\\textit\{([^}]+)\}/g, '<i>$1</i>')
213
- .replace(/\\maketitle/, '<div class="title"><h1>My LaTeX Document</h1><p>Reuben OS</p><p>' + new Date().toLocaleDateString() + '</p></div>')
214
- .replace(/\\begin\{document\}/, '')
215
- .replace(/\\end\{document\}/, '')
216
- .replace(/\\documentclass\{[^}]+\}/, '')
217
- .replace(/\\usepackage\{[^}]+\}/, '')
218
- .replace(/\\title\{[^}]+\}/, '')
219
- .replace(/\\author\{[^}]+\}/, '')
220
- .replace(/\\date\{[^}]+\}/, '')
221
-
222
- // Split by math delimiters to render KaTeX
223
- const parts = text.split(/(\\\[[\s\S]*?\\\]|\$[^$]+\$)/)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  previewRef.current.innerHTML = ''
225
 
226
  parts.forEach(part => {
227
- if (part.startsWith('\\[') || part.startsWith('$')) {
228
- const math = part.replace(/^\\\[|\\\]$|^\$|\$$/g, '')
229
- const span = document.createElement('span')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  try {
231
- katex.render(math, span, { throwOnError: false, displayMode: part.startsWith('\\[') })
 
 
 
 
232
  previewRef.current?.appendChild(span)
233
  } catch (e) {
234
- previewRef.current?.appendChild(document.createTextNode(part))
 
 
 
 
235
  }
236
- } else {
 
237
  const div = document.createElement('div')
238
  div.innerHTML = part
 
239
  previewRef.current?.appendChild(div)
240
  }
241
  })
 
203
  useEffect(() => {
204
  if (previewRef.current) {
205
  try {
206
+ // Extract title, author, and date from LaTeX preamble
207
+ let title = 'Untitled Document'
208
+ let author = 'Anonymous'
209
+ let date = new Date().toLocaleDateString()
210
+
211
+ const titleMatch = code.match(/\\title\{([^}]+)\}/)
212
+ const authorMatch = code.match(/\\author\{([^}]+)\}/)
213
+ const dateMatch = code.match(/\\date\{([^}]+)\}/)
214
+
215
+ if (titleMatch) title = titleMatch[1]
216
+ if (authorMatch) author = authorMatch[1]
217
+ if (dateMatch) {
218
+ date = dateMatch[1].replace(/\\today/g, new Date().toLocaleDateString())
219
+ }
220
+
221
+ // Process the document content
222
+ let processedText = code
223
+ // Remove preamble
224
+ .replace(/\\documentclass\{[^}]+\}/g, '')
225
+ .replace(/\\usepackage\{[^}]+\}/g, '')
226
+ .replace(/\\title\{[^}]+\}/g, '')
227
+ .replace(/\\author\{[^}]+\}/g, '')
228
+ .replace(/\\date\{[^}]+\}/g, '')
229
+ .replace(/\\begin\{document\}/g, '')
230
+ .replace(/\\end\{document\}/g, '')
231
+
232
+ // Handle title
233
+ .replace(/\\maketitle/g, `
234
+ <div style="text-align: center; margin-bottom: 2em; padding-bottom: 1em; border-bottom: 1px solid #ccc;">
235
+ <h1 style="font-size: 2em; margin: 0.5em 0;">${title}</h1>
236
+ <p style="margin: 0.3em 0; font-size: 1.1em;">${author}</p>
237
+ <p style="margin: 0.3em 0; color: #666;">${date}</p>
238
+ </div>
239
+ `)
240
+
241
+ // Sections and structure
242
+ .replace(/\\section\{([^}]+)\}/g, '<h2 style="font-size: 1.5em; margin-top: 1.5em; margin-bottom: 0.5em; font-weight: bold;">$1</h2>')
243
+ .replace(/\\subsection\{([^}]+)\}/g, '<h3 style="font-size: 1.2em; margin-top: 1em; margin-bottom: 0.5em; font-weight: bold;">$1</h3>')
244
+ .replace(/\\subsubsection\{([^}]+)\}/g, '<h4 style="font-size: 1em; margin-top: 0.8em; margin-bottom: 0.3em; font-weight: bold;">$1</h4>')
245
+ .replace(/\\paragraph\{([^}]+)\}/g, '<p style="font-weight: bold; margin-top: 0.5em;">$1</p>')
246
+
247
+ // Text formatting
248
+ .replace(/\\textbf\{([^}]+)\}/g, '<strong>$1</strong>')
249
+ .replace(/\\textit\{([^}]+)\}/g, '<em>$1</em>')
250
+ .replace(/\\underline\{([^}]+)\}/g, '<u>$1</u>')
251
+ .replace(/\\emph\{([^}]+)\}/g, '<em>$1</em>')
252
+ .replace(/\\texttt\{([^}]+)\}/g, '<code style="font-family: monospace; background: #f5f5f5; padding: 2px 4px; border-radius: 3px;">$1</code>')
253
+
254
+ // Lists
255
+ .replace(/\\begin\{itemize\}/g, '<ul style="margin: 0.5em 0; padding-left: 1.5em;">')
256
+ .replace(/\\end\{itemize\}/g, '</ul>')
257
+ .replace(/\\begin\{enumerate\}/g, '<ol style="margin: 0.5em 0; padding-left: 1.5em;">')
258
+ .replace(/\\end\{enumerate\}/g, '</ol>')
259
+ .replace(/\\item\s+/g, '<li style="margin: 0.3em 0;">')
260
+
261
+ // Paragraphs and line breaks
262
+ .replace(/\\\\/g, '<br />')
263
+ .replace(/\\newline/g, '<br />')
264
+ .replace(/\\par\b/g, '</p><p>')
265
+
266
+ // Clean up extra whitespace
267
+ .replace(/\n\n+/g, '</p><p style="margin: 0.5em 0;">')
268
+
269
+ // Split by math delimiters to render KaTeX (improved regex for display and inline math)
270
+ const parts = processedText.split(/(\\\[[\s\S]*?\\\]|\$\$[\s\S]*?\$\$|\$[^$]+\$|\\\([\s\S]*?\\\))/)
271
  previewRef.current.innerHTML = ''
272
 
273
  parts.forEach(part => {
274
+ if (part.startsWith('\\[') || part.startsWith('$$') || part.startsWith('$') || part.startsWith('\\(')) {
275
+ // Determine if it's display math or inline math
276
+ const isDisplayMath = part.startsWith('\\[') || part.startsWith('$$')
277
+
278
+ // Extract the math content
279
+ let math = part
280
+ if (part.startsWith('\\[') && part.endsWith('\\]')) {
281
+ math = part.slice(2, -2)
282
+ } else if (part.startsWith('$$') && part.endsWith('$$')) {
283
+ math = part.slice(2, -2)
284
+ } else if (part.startsWith('$') && part.endsWith('$')) {
285
+ math = part.slice(1, -1)
286
+ } else if (part.startsWith('\\(') && part.endsWith('\\)')) {
287
+ math = part.slice(2, -2)
288
+ }
289
+
290
+ const span = document.createElement(isDisplayMath ? 'div' : 'span')
291
+ if (isDisplayMath) {
292
+ span.style.textAlign = 'center'
293
+ span.style.margin = '1em 0'
294
+ }
295
+
296
  try {
297
+ katex.render(math, span, {
298
+ throwOnError: false,
299
+ displayMode: isDisplayMath,
300
+ strict: false
301
+ })
302
  previewRef.current?.appendChild(span)
303
  } catch (e) {
304
+ // If KaTeX fails, show the raw math
305
+ const errorSpan = document.createElement('span')
306
+ errorSpan.style.color = '#cc0000'
307
+ errorSpan.textContent = part
308
+ previewRef.current?.appendChild(errorSpan)
309
  }
310
+ } else if (part.trim()) {
311
+ // Only add non-empty parts
312
  const div = document.createElement('div')
313
  div.innerHTML = part
314
+ div.style.margin = '0.5em 0'
315
  previewRef.current?.appendChild(div)
316
  }
317
  })
app/components/Messages.tsx CHANGED
@@ -215,7 +215,7 @@ export function Messages({ onClose, onMinimize, onMaximize, onFocus, zIndex }: M
215
  )}
216
  <div
217
  className={`
218
- max-w-[70%] px-3 py-1.5 text-[13px] leading-relaxed break-words relative group
219
  ${isMe
220
  ? 'bg-[#0A84FF] text-white rounded-2xl rounded-br-sm message-bubble-sent'
221
  : 'bg-[#3a3a3a] text-gray-100 rounded-2xl rounded-bl-sm message-bubble-received'}
@@ -268,10 +268,20 @@ export function Messages({ onClose, onMinimize, onMaximize, onFocus, zIndex }: M
268
  type="text"
269
  value={inputText}
270
  onChange={(e) => setInputText(e.target.value)}
 
 
 
 
 
 
 
 
 
 
271
  placeholder="iMessage"
272
  maxLength={200}
273
  disabled={isLoading}
274
- className="w-full bg-[#2a2a2a] border border-white/10 rounded-full py-1.5 pl-4 pr-10 text-sm text-white placeholder-gray-500 focus:outline-none focus:border-gray-500 transition-colors"
275
  />
276
  <button
277
  type="submit"
 
215
  )}
216
  <div
217
  className={`
218
+ max-w-[70%] px-3 py-1.5 text-[13px] leading-relaxed break-words relative group select-text
219
  ${isMe
220
  ? 'bg-[#0A84FF] text-white rounded-2xl rounded-br-sm message-bubble-sent'
221
  : 'bg-[#3a3a3a] text-gray-100 rounded-2xl rounded-bl-sm message-bubble-received'}
 
268
  type="text"
269
  value={inputText}
270
  onChange={(e) => setInputText(e.target.value)}
271
+ onKeyDown={(e) => {
272
+ if (e.key === 'Enter' && !e.shiftKey) {
273
+ e.preventDefault()
274
+ handleSend(e)
275
+ }
276
+ }}
277
+ onPaste={(e) => {
278
+ // Allow paste - default behavior
279
+ e.stopPropagation()
280
+ }}
281
  placeholder="iMessage"
282
  maxLength={200}
283
  disabled={isLoading}
284
+ className="w-full bg-[#2a2a2a] border border-white/10 rounded-full py-1.5 pl-4 pr-10 text-sm text-white placeholder-gray-500 focus:outline-none focus:border-gray-500 transition-colors select-text"
285
  />
286
  <button
287
  type="submit"
app/globals.css CHANGED
@@ -269,6 +269,25 @@
269
  content: '+';
270
  }
271
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  /* Custom Scrollbar */
273
  ::-webkit-scrollbar {
274
  width: 10px;
 
269
  content: '+';
270
  }
271
 
272
+ /* Text Selection Styling */
273
+ ::selection {
274
+ background: rgba(59, 130, 246, 0.3);
275
+ color: inherit;
276
+ }
277
+
278
+ ::-moz-selection {
279
+ background: rgba(59, 130, 246, 0.3);
280
+ color: inherit;
281
+ }
282
+
283
+ /* Enable text selection for specific elements */
284
+ .select-text {
285
+ user-select: text !important;
286
+ -webkit-user-select: text !important;
287
+ -moz-user-select: text !important;
288
+ cursor: text;
289
+ }
290
+
291
  /* Custom Scrollbar */
292
  ::-webkit-scrollbar {
293
  width: 10px;