Spaces:
Running
Running
changed latex
Browse files- app/api/data/route.ts +44 -0
- app/api/latex/compile/route.ts +35 -12
- app/components/Desktop.tsx +0 -90
- app/components/FlutterRunner.tsx +1 -71
- app/components/LaTeXEditor.tsx +0 -515
- package-lock.json +302 -17
- package.json +1 -1
- public/data/reuben/main.tex +18 -0
app/api/data/route.ts
CHANGED
|
@@ -120,6 +120,50 @@ export async function POST(request: NextRequest) {
|
|
| 120 |
|
| 121 |
await writeFile(filePath, content, 'utf-8')
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
return NextResponse.json({ success: true })
|
| 124 |
}
|
| 125 |
|
|
|
|
| 120 |
|
| 121 |
await writeFile(filePath, content, 'utf-8')
|
| 122 |
|
| 123 |
+
// Auto-convert .tex files to PDF
|
| 124 |
+
if (fileName.endsWith('.tex')) {
|
| 125 |
+
console.log('Detected .tex file, auto-converting to PDF...')
|
| 126 |
+
try {
|
| 127 |
+
// Compile LaTeX to PDF using LaTeX.Online
|
| 128 |
+
const compileResponse = await fetch('https://latexonline.cc/compile', {
|
| 129 |
+
method: 'POST',
|
| 130 |
+
headers: {
|
| 131 |
+
'Content-Type': 'text/plain',
|
| 132 |
+
},
|
| 133 |
+
body: content
|
| 134 |
+
})
|
| 135 |
+
|
| 136 |
+
if (compileResponse.ok) {
|
| 137 |
+
const pdfBuffer = await compileResponse.arrayBuffer()
|
| 138 |
+
const pdfFileName = fileName.replace('.tex', '.pdf')
|
| 139 |
+
const pdfFilePath = path.join(targetDir, pdfFileName)
|
| 140 |
+
|
| 141 |
+
await writeFile(pdfFilePath, Buffer.from(pdfBuffer))
|
| 142 |
+
console.log(`PDF generated successfully: ${pdfFilePath}`)
|
| 143 |
+
|
| 144 |
+
return NextResponse.json({
|
| 145 |
+
success: true,
|
| 146 |
+
pdfGenerated: true,
|
| 147 |
+
pdfFileName: pdfFileName
|
| 148 |
+
})
|
| 149 |
+
} else {
|
| 150 |
+
console.error('PDF compilation failed:', await compileResponse.text())
|
| 151 |
+
return NextResponse.json({
|
| 152 |
+
success: true,
|
| 153 |
+
pdfGenerated: false,
|
| 154 |
+
error: 'LaTeX file saved but PDF generation failed'
|
| 155 |
+
})
|
| 156 |
+
}
|
| 157 |
+
} catch (pdfError) {
|
| 158 |
+
console.error('Error generating PDF:', pdfError)
|
| 159 |
+
return NextResponse.json({
|
| 160 |
+
success: true,
|
| 161 |
+
pdfGenerated: false,
|
| 162 |
+
error: 'LaTeX file saved but PDF generation failed'
|
| 163 |
+
})
|
| 164 |
+
}
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
return NextResponse.json({ success: true })
|
| 168 |
}
|
| 169 |
|
app/api/latex/compile/route.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
|
|
| 2 |
|
| 3 |
export async function POST(request: NextRequest) {
|
| 4 |
try {
|
| 5 |
-
const { latex,
|
| 6 |
|
| 7 |
if (!latex) {
|
| 8 |
return NextResponse.json(
|
|
@@ -11,25 +11,48 @@ export async function POST(request: NextRequest) {
|
|
| 11 |
)
|
| 12 |
}
|
| 13 |
|
| 14 |
-
|
| 15 |
-
// 1. Use a LaTeX compiler service (like LaTeX online compiler API)
|
| 16 |
-
// 2. Or install and use pdflatex locally with child_process
|
| 17 |
-
// 3. Or use a Docker container with LaTeX installed
|
| 18 |
|
| 19 |
-
//
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
return response
|
| 28 |
|
| 29 |
} catch (error) {
|
| 30 |
console.error('Error compiling LaTeX:', error)
|
| 31 |
return NextResponse.json(
|
| 32 |
-
{
|
|
|
|
|
|
|
|
|
|
| 33 |
{ status: 500 }
|
| 34 |
)
|
| 35 |
}
|
|
|
|
| 2 |
|
| 3 |
export async function POST(request: NextRequest) {
|
| 4 |
try {
|
| 5 |
+
const { latex, filename } = await request.json()
|
| 6 |
|
| 7 |
if (!latex) {
|
| 8 |
return NextResponse.json(
|
|
|
|
| 11 |
)
|
| 12 |
}
|
| 13 |
|
| 14 |
+
console.log('Compiling LaTeX to PDF using LaTeX.Online...')
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
// Use LaTeX.Online free API to compile LaTeX to PDF
|
| 17 |
+
const compileResponse = await fetch('https://latexonline.cc/compile', {
|
| 18 |
+
method: 'POST',
|
| 19 |
+
headers: {
|
| 20 |
+
'Content-Type': 'text/plain',
|
| 21 |
+
},
|
| 22 |
+
body: latex
|
| 23 |
+
})
|
| 24 |
|
| 25 |
+
if (!compileResponse.ok) {
|
| 26 |
+
const errorText = await compileResponse.text()
|
| 27 |
+
console.error('LaTeX compilation failed:', errorText)
|
| 28 |
+
return NextResponse.json(
|
| 29 |
+
{
|
| 30 |
+
error: 'LaTeX compilation failed',
|
| 31 |
+
details: errorText
|
| 32 |
+
},
|
| 33 |
+
{ status: 500 }
|
| 34 |
+
)
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
// Get the PDF buffer
|
| 38 |
+
const pdfBuffer = await compileResponse.arrayBuffer()
|
| 39 |
+
|
| 40 |
+
console.log('LaTeX compiled successfully, PDF size:', pdfBuffer.byteLength, 'bytes')
|
| 41 |
+
|
| 42 |
+
// Return the PDF
|
| 43 |
+
const response = new NextResponse(Buffer.from(pdfBuffer))
|
| 44 |
+
response.headers.set('Content-Type', 'application/pdf')
|
| 45 |
+
response.headers.set('Content-Disposition', `attachment; filename="${filename || 'document.pdf'}"`)
|
| 46 |
|
| 47 |
return response
|
| 48 |
|
| 49 |
} catch (error) {
|
| 50 |
console.error('Error compiling LaTeX:', error)
|
| 51 |
return NextResponse.json(
|
| 52 |
+
{
|
| 53 |
+
error: 'Failed to compile LaTeX',
|
| 54 |
+
details: error instanceof Error ? error.message : String(error)
|
| 55 |
+
},
|
| 56 |
{ status: 500 }
|
| 57 |
)
|
| 58 |
}
|
app/components/Desktop.tsx
CHANGED
|
@@ -20,9 +20,6 @@ import { ContextMenu } from './ContextMenu'
|
|
| 20 |
import { AboutModal } from './AboutModal'
|
| 21 |
|
| 22 |
import { FlutterRunner } from './FlutterRunner'
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
import { LaTeXEditor } from './LaTeXEditor'
|
| 26 |
import { QuizApp } from './QuizApp'
|
| 27 |
import { motion, AnimatePresence } from 'framer-motion'
|
| 28 |
import { SystemPowerOverlay } from './SystemPowerOverlay'
|
|
@@ -68,8 +65,6 @@ export function Desktop() {
|
|
| 68 |
const [activeFlutterApp, setActiveFlutterApp] = useState<any>(null)
|
| 69 |
|
| 70 |
const [flutterCodeEditorOpen, setFlutterCodeEditorOpen] = useState(false)
|
| 71 |
-
const [latexEditorOpen, setLaTeXEditorOpen] = useState(false)
|
| 72 |
-
const [activeLaTeXApp, setActiveLaTeXApp] = useState<any>(null)
|
| 73 |
const [quizAppOpen, setQuizAppOpen] = useState(false)
|
| 74 |
|
| 75 |
// Minimized states
|
|
@@ -84,7 +79,6 @@ export function Desktop() {
|
|
| 84 |
const [flutterRunnerMinimized, setFlutterRunnerMinimized] = useState(false)
|
| 85 |
|
| 86 |
const [flutterCodeEditorMinimized, setFlutterCodeEditorMinimized] = useState(false)
|
| 87 |
-
const [latexEditorMinimized, setLaTeXEditorMinimized] = useState(false)
|
| 88 |
const [quizAppMinimized, setQuizAppMinimized] = useState(false)
|
| 89 |
|
| 90 |
const [powerState, setPowerState] = useState<'active' | 'sleep' | 'restart' | 'shutdown'>('active')
|
|
@@ -111,7 +105,6 @@ export function Desktop() {
|
|
| 111 |
setFlutterRunnerOpen(false)
|
| 112 |
|
| 113 |
setFlutterCodeEditorOpen(false)
|
| 114 |
-
setLaTeXEditorOpen(false)
|
| 115 |
setQuizAppOpen(false)
|
| 116 |
|
| 117 |
// Reset all minimized states
|
|
@@ -123,7 +116,6 @@ export function Desktop() {
|
|
| 123 |
setFlutterRunnerMinimized(false)
|
| 124 |
|
| 125 |
setFlutterCodeEditorMinimized(false)
|
| 126 |
-
setLaTeXEditorMinimized(false)
|
| 127 |
setQuizAppMinimized(false)
|
| 128 |
|
| 129 |
// Reset window z-indices
|
|
@@ -228,25 +220,6 @@ export function Desktop() {
|
|
| 228 |
setFlutterCodeEditorMinimized(false)
|
| 229 |
}
|
| 230 |
|
| 231 |
-
const openLaTeXEditor = () => {
|
| 232 |
-
// Check if there's file content stored from File Manager
|
| 233 |
-
const storedContent = sessionStorage.getItem('latexFileContent')
|
| 234 |
-
if (storedContent) {
|
| 235 |
-
setActiveLaTeXApp({ content: storedContent })
|
| 236 |
-
sessionStorage.removeItem('latexFileContent') // Clean up
|
| 237 |
-
} else {
|
| 238 |
-
setActiveLaTeXApp(null)
|
| 239 |
-
}
|
| 240 |
-
setLaTeXEditorOpen(true)
|
| 241 |
-
setLaTeXEditorMinimized(false)
|
| 242 |
-
bringWindowToFront('latexEditor')
|
| 243 |
-
}
|
| 244 |
-
|
| 245 |
-
const closeLaTeXEditor = () => {
|
| 246 |
-
setLaTeXEditorOpen(false)
|
| 247 |
-
setLaTeXEditorMinimized(false)
|
| 248 |
-
}
|
| 249 |
-
|
| 250 |
const openQuizApp = () => {
|
| 251 |
setQuizAppOpen(true)
|
| 252 |
setQuizAppMinimized(false)
|
|
@@ -281,9 +254,6 @@ export function Desktop() {
|
|
| 281 |
case 'flutter-editor':
|
| 282 |
openFlutterCodeEditor()
|
| 283 |
break
|
| 284 |
-
case 'latex-editor':
|
| 285 |
-
openLaTeXEditor()
|
| 286 |
-
break
|
| 287 |
case 'quiz':
|
| 288 |
openQuizApp()
|
| 289 |
break
|
|
@@ -518,26 +488,6 @@ export function Desktop() {
|
|
| 518 |
})
|
| 519 |
}
|
| 520 |
|
| 521 |
-
if (latexEditorMinimized && latexEditorOpen) {
|
| 522 |
-
minimizedApps.push({
|
| 523 |
-
id: 'latex-editor',
|
| 524 |
-
label: 'LaTeX Studio',
|
| 525 |
-
icon: (
|
| 526 |
-
<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">
|
| 527 |
-
<div className="absolute inset-0 bg-gradient-to-b from-white/10 to-transparent opacity-30" />
|
| 528 |
-
<div className="relative z-10 flex flex-col items-center justify-center">
|
| 529 |
-
<Function size={18} weight="bold" className="text-green-400 drop-shadow-md" />
|
| 530 |
-
<span className="text-[5px] font-black text-gray-300 tracking-widest mt-0.5">TEX</span>
|
| 531 |
-
</div>
|
| 532 |
-
</div>
|
| 533 |
-
),
|
| 534 |
-
onRestore: () => {
|
| 535 |
-
setLaTeXEditorMinimized(false)
|
| 536 |
-
bringWindowToFront('latexEditor')
|
| 537 |
-
}
|
| 538 |
-
})
|
| 539 |
-
}
|
| 540 |
-
|
| 541 |
if (quizAppMinimized && quizAppOpen) {
|
| 542 |
minimizedApps.push({
|
| 543 |
id: 'quiz',
|
|
@@ -699,17 +649,6 @@ export function Desktop() {
|
|
| 699 |
/>
|
| 700 |
</div>
|
| 701 |
|
| 702 |
-
<div className="pointer-events-auto w-24 h-24">
|
| 703 |
-
<DraggableDesktopIcon
|
| 704 |
-
id="latex-editor"
|
| 705 |
-
label="LaTeX Studio"
|
| 706 |
-
iconType="latex"
|
| 707 |
-
initialPosition={{ x: 0, y: 0 }}
|
| 708 |
-
onClick={() => { }}
|
| 709 |
-
onDoubleClick={openLaTeXEditor}
|
| 710 |
-
/>
|
| 711 |
-
</div>
|
| 712 |
-
|
| 713 |
<div className="pointer-events-auto w-24 h-24">
|
| 714 |
<DraggableDesktopIcon
|
| 715 |
id="quiz"
|
|
@@ -909,35 +848,6 @@ export function Desktop() {
|
|
| 909 |
</motion.div>
|
| 910 |
)}
|
| 911 |
|
| 912 |
-
{latexEditorOpen && (
|
| 913 |
-
<motion.div
|
| 914 |
-
key="latex-editor"
|
| 915 |
-
initial={{ opacity: 0, scale: 0.95 }}
|
| 916 |
-
animate={{
|
| 917 |
-
opacity: latexEditorMinimized ? 0 : 1,
|
| 918 |
-
scale: latexEditorMinimized ? 0.9 : 1,
|
| 919 |
-
y: latexEditorMinimized ? 100 : 0,
|
| 920 |
-
}}
|
| 921 |
-
exit={{ opacity: 0, scale: 0.95 }}
|
| 922 |
-
transition={{ duration: 0.2 }}
|
| 923 |
-
style={{
|
| 924 |
-
pointerEvents: latexEditorMinimized ? 'none' : 'auto',
|
| 925 |
-
display: latexEditorMinimized ? 'none' : 'block'
|
| 926 |
-
}}
|
| 927 |
-
>
|
| 928 |
-
<LaTeXEditor
|
| 929 |
-
initialContent={activeLaTeXApp?.content}
|
| 930 |
-
onClose={() => {
|
| 931 |
-
setLaTeXEditorOpen(false)
|
| 932 |
-
setActiveLaTeXApp(null)
|
| 933 |
-
}}
|
| 934 |
-
onMinimize={() => setLaTeXEditorMinimized(true)}
|
| 935 |
-
onFocus={() => bringWindowToFront('latexEditor')}
|
| 936 |
-
zIndex={windowZIndices.latexEditor || 1000}
|
| 937 |
-
/>
|
| 938 |
-
</motion.div>
|
| 939 |
-
)}
|
| 940 |
-
|
| 941 |
{quizAppOpen && (
|
| 942 |
<motion.div
|
| 943 |
key="quiz-app"
|
|
|
|
| 20 |
import { AboutModal } from './AboutModal'
|
| 21 |
|
| 22 |
import { FlutterRunner } from './FlutterRunner'
|
|
|
|
|
|
|
|
|
|
| 23 |
import { QuizApp } from './QuizApp'
|
| 24 |
import { motion, AnimatePresence } from 'framer-motion'
|
| 25 |
import { SystemPowerOverlay } from './SystemPowerOverlay'
|
|
|
|
| 65 |
const [activeFlutterApp, setActiveFlutterApp] = useState<any>(null)
|
| 66 |
|
| 67 |
const [flutterCodeEditorOpen, setFlutterCodeEditorOpen] = useState(false)
|
|
|
|
|
|
|
| 68 |
const [quizAppOpen, setQuizAppOpen] = useState(false)
|
| 69 |
|
| 70 |
// Minimized states
|
|
|
|
| 79 |
const [flutterRunnerMinimized, setFlutterRunnerMinimized] = useState(false)
|
| 80 |
|
| 81 |
const [flutterCodeEditorMinimized, setFlutterCodeEditorMinimized] = useState(false)
|
|
|
|
| 82 |
const [quizAppMinimized, setQuizAppMinimized] = useState(false)
|
| 83 |
|
| 84 |
const [powerState, setPowerState] = useState<'active' | 'sleep' | 'restart' | 'shutdown'>('active')
|
|
|
|
| 105 |
setFlutterRunnerOpen(false)
|
| 106 |
|
| 107 |
setFlutterCodeEditorOpen(false)
|
|
|
|
| 108 |
setQuizAppOpen(false)
|
| 109 |
|
| 110 |
// Reset all minimized states
|
|
|
|
| 116 |
setFlutterRunnerMinimized(false)
|
| 117 |
|
| 118 |
setFlutterCodeEditorMinimized(false)
|
|
|
|
| 119 |
setQuizAppMinimized(false)
|
| 120 |
|
| 121 |
// Reset window z-indices
|
|
|
|
| 220 |
setFlutterCodeEditorMinimized(false)
|
| 221 |
}
|
| 222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
const openQuizApp = () => {
|
| 224 |
setQuizAppOpen(true)
|
| 225 |
setQuizAppMinimized(false)
|
|
|
|
| 254 |
case 'flutter-editor':
|
| 255 |
openFlutterCodeEditor()
|
| 256 |
break
|
|
|
|
|
|
|
|
|
|
| 257 |
case 'quiz':
|
| 258 |
openQuizApp()
|
| 259 |
break
|
|
|
|
| 488 |
})
|
| 489 |
}
|
| 490 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
if (quizAppMinimized && quizAppOpen) {
|
| 492 |
minimizedApps.push({
|
| 493 |
id: 'quiz',
|
|
|
|
| 649 |
/>
|
| 650 |
</div>
|
| 651 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 652 |
<div className="pointer-events-auto w-24 h-24">
|
| 653 |
<DraggableDesktopIcon
|
| 654 |
id="quiz"
|
|
|
|
| 848 |
</motion.div>
|
| 849 |
)}
|
| 850 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 851 |
{quizAppOpen && (
|
| 852 |
<motion.div
|
| 853 |
key="quiz-app"
|
app/components/FlutterRunner.tsx
CHANGED
|
@@ -28,74 +28,7 @@ interface FileNode {
|
|
| 28 |
isOpen?: boolean
|
| 29 |
}
|
| 30 |
|
| 31 |
-
const DEFAULT_FLUTTER_CODE =
|
| 32 |
-
|
| 33 |
-
void main() {
|
| 34 |
-
runApp(const MyApp());
|
| 35 |
-
}
|
| 36 |
-
|
| 37 |
-
class MyApp extends StatelessWidget {
|
| 38 |
-
const MyApp({super.key});
|
| 39 |
-
|
| 40 |
-
@override
|
| 41 |
-
Widget build(BuildContext context) {
|
| 42 |
-
return MaterialApp(
|
| 43 |
-
title: 'Flutter Demo',
|
| 44 |
-
theme: ThemeData(
|
| 45 |
-
primarySwatch: Colors.blue,
|
| 46 |
-
useMaterial3: true,
|
| 47 |
-
),
|
| 48 |
-
home: const MyHomePage(title: 'Flutter Demo Home Page'),
|
| 49 |
-
);
|
| 50 |
-
}
|
| 51 |
-
}
|
| 52 |
-
|
| 53 |
-
class MyHomePage extends StatefulWidget {
|
| 54 |
-
const MyHomePage({super.key, required this.title});
|
| 55 |
-
|
| 56 |
-
final String title;
|
| 57 |
-
|
| 58 |
-
@override
|
| 59 |
-
State<MyHomePage> createState() => _MyHomePageState();
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
-
class _MyHomePageState extends State<MyHomePage> {
|
| 63 |
-
int _counter = 0;
|
| 64 |
-
|
| 65 |
-
void _incrementCounter() {
|
| 66 |
-
setState(() {
|
| 67 |
-
_counter++;
|
| 68 |
-
});
|
| 69 |
-
}
|
| 70 |
-
|
| 71 |
-
@override
|
| 72 |
-
Widget build(BuildContext context) {
|
| 73 |
-
return Scaffold(
|
| 74 |
-
appBar: AppBar(
|
| 75 |
-
title: Text(widget.title),
|
| 76 |
-
),
|
| 77 |
-
body: Center(
|
| 78 |
-
child: Column(
|
| 79 |
-
mainAxisAlignment: MainAxisAlignment.center,
|
| 80 |
-
children: <Widget>[
|
| 81 |
-
const Text(
|
| 82 |
-
'You have pushed the button this many times:',
|
| 83 |
-
),
|
| 84 |
-
Text(
|
| 85 |
-
'$_counter',
|
| 86 |
-
style: Theme.of(context).textTheme.headlineMedium,
|
| 87 |
-
),
|
| 88 |
-
],
|
| 89 |
-
),
|
| 90 |
-
),
|
| 91 |
-
floatingActionButton: FloatingActionButton(
|
| 92 |
-
onPressed: _incrementCounter,
|
| 93 |
-
tooltip: 'Increment',
|
| 94 |
-
child: const Icon(Icons.add),
|
| 95 |
-
),
|
| 96 |
-
);
|
| 97 |
-
}
|
| 98 |
-
}`
|
| 99 |
|
| 100 |
export function FlutterRunner({ onClose, onMinimize, onMaximize, onFocus, zIndex, initialCode }: FlutterRunnerProps) {
|
| 101 |
const [code, setCode] = useState(initialCode || DEFAULT_FLUTTER_CODE)
|
|
@@ -371,9 +304,6 @@ export function FlutterRunner({ onClose, onMinimize, onMaximize, onFocus, zIndex
|
|
| 371 |
<div className="w-24" /> {/* Spacer for centering */}
|
| 372 |
</div>
|
| 373 |
<div className="flex-1 bg-[#1e1e1e] relative overflow-hidden">
|
| 374 |
-
<div className="absolute top-0 left-0 right-0 bg-gray-900/95 backdrop-blur-sm px-4 py-2 z-10 border-b border-gray-700">
|
| 375 |
-
<p className="text-red-500 text-sm font-bold">Copy the code from editor to DartPad</p>
|
| 376 |
-
</div>
|
| 377 |
<iframe
|
| 378 |
|
| 379 |
src="https://dartpad.dev/embed-flutter.html?theme=dark&split=50"
|
|
|
|
| 28 |
isOpen?: boolean
|
| 29 |
}
|
| 30 |
|
| 31 |
+
const DEFAULT_FLUTTER_CODE = ``
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
export function FlutterRunner({ onClose, onMinimize, onMaximize, onFocus, zIndex, initialCode }: FlutterRunnerProps) {
|
| 34 |
const [code, setCode] = useState(initialCode || DEFAULT_FLUTTER_CODE)
|
|
|
|
| 304 |
<div className="w-24" /> {/* Spacer for centering */}
|
| 305 |
</div>
|
| 306 |
<div className="flex-1 bg-[#1e1e1e] relative overflow-hidden">
|
|
|
|
|
|
|
|
|
|
| 307 |
<iframe
|
| 308 |
|
| 309 |
src="https://dartpad.dev/embed-flutter.html?theme=dark&split=50"
|
app/components/LaTeXEditor.tsx
DELETED
|
@@ -1,515 +0,0 @@
|
|
| 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 |
-
FloppyDisk,
|
| 17 |
-
Check
|
| 18 |
-
} from '@phosphor-icons/react'
|
| 19 |
-
import Window from './Window'
|
| 20 |
-
import katex from 'katex'
|
| 21 |
-
import 'katex/dist/katex.min.css'
|
| 22 |
-
|
| 23 |
-
interface LaTeXEditorProps {
|
| 24 |
-
onClose: () => void
|
| 25 |
-
onMinimize?: () => void
|
| 26 |
-
onMaximize?: () => void
|
| 27 |
-
onFocus?: () => void
|
| 28 |
-
zIndex?: number
|
| 29 |
-
initialContent?: string
|
| 30 |
-
}
|
| 31 |
-
|
| 32 |
-
interface FileNode {
|
| 33 |
-
id: string
|
| 34 |
-
name: string
|
| 35 |
-
type: 'file' | 'folder'
|
| 36 |
-
content?: string
|
| 37 |
-
children?: FileNode[]
|
| 38 |
-
isOpen?: boolean
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
const DEFAULT_LATEX = `\\documentclass{article}
|
| 42 |
-
\\usepackage{amsmath}
|
| 43 |
-
\\title{My LaTeX Document}
|
| 44 |
-
\\author{Reuben OS}
|
| 45 |
-
\\date{\\today}
|
| 46 |
-
|
| 47 |
-
\\begin{document}
|
| 48 |
-
|
| 49 |
-
\\maketitle
|
| 50 |
-
|
| 51 |
-
\\section{Introduction}
|
| 52 |
-
Welcome to LaTeX Studio on Reuben OS.
|
| 53 |
-
|
| 54 |
-
\\section{Mathematics}
|
| 55 |
-
Here is an equation:
|
| 56 |
-
\\[ E = mc^2 \\]
|
| 57 |
-
|
| 58 |
-
\\end{document}`
|
| 59 |
-
|
| 60 |
-
export function LaTeXEditor({ onClose, onMinimize, onMaximize, onFocus, zIndex, initialContent }: LaTeXEditorProps) {
|
| 61 |
-
const [code, setCode] = useState(initialContent || DEFAULT_LATEX)
|
| 62 |
-
const [showSidebar, setShowSidebar] = useState(true)
|
| 63 |
-
const [files, setFiles] = useState<FileNode[]>([])
|
| 64 |
-
const [activeFileId, setActiveFileId] = useState('main')
|
| 65 |
-
const [activeFileName, setActiveFileName] = useState('main.tex')
|
| 66 |
-
const previewRef = useRef<HTMLDivElement>(null)
|
| 67 |
-
const [lastSaved, setLastSaved] = useState<Date | null>(null)
|
| 68 |
-
const [isSaving, setIsSaving] = useState(false)
|
| 69 |
-
const [passkey, setPasskey] = useState('')
|
| 70 |
-
const [isUnlocked, setIsUnlocked] = useState(false)
|
| 71 |
-
const [tempPasskey, setTempPasskey] = useState('')
|
| 72 |
-
const [loading, setLoading] = useState(false)
|
| 73 |
-
|
| 74 |
-
// Load files from secure storage
|
| 75 |
-
const loadFiles = async (key: string) => {
|
| 76 |
-
setLoading(true)
|
| 77 |
-
try {
|
| 78 |
-
const response = await fetch(`/api/data?key=${encodeURIComponent(key)}&folder=`)
|
| 79 |
-
const data = await response.json()
|
| 80 |
-
|
| 81 |
-
if (data.error) {
|
| 82 |
-
throw new Error(data.error)
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
// Filter for .tex files
|
| 86 |
-
const texFiles = data.files?.filter((f: any) => f.name.endsWith('.tex')) || []
|
| 87 |
-
|
| 88 |
-
if (texFiles.length > 0) {
|
| 89 |
-
const fileNodes: FileNode[] = texFiles.map((file: any, index: number) => ({
|
| 90 |
-
id: `file_${index}`,
|
| 91 |
-
name: file.name,
|
| 92 |
-
type: 'file' as const,
|
| 93 |
-
content: file.content
|
| 94 |
-
}))
|
| 95 |
-
|
| 96 |
-
setFiles([{
|
| 97 |
-
id: 'root',
|
| 98 |
-
name: 'LaTeX Files',
|
| 99 |
-
type: 'folder',
|
| 100 |
-
isOpen: true,
|
| 101 |
-
children: fileNodes
|
| 102 |
-
}])
|
| 103 |
-
|
| 104 |
-
// Load first file
|
| 105 |
-
if (fileNodes.length > 0) {
|
| 106 |
-
setActiveFileId(fileNodes[0].id)
|
| 107 |
-
setActiveFileName(fileNodes[0].name)
|
| 108 |
-
setCode(fileNodes[0].content || DEFAULT_LATEX)
|
| 109 |
-
}
|
| 110 |
-
} else {
|
| 111 |
-
// No files found, create default
|
| 112 |
-
setFiles([{
|
| 113 |
-
id: 'root',
|
| 114 |
-
name: 'LaTeX Files',
|
| 115 |
-
type: 'folder',
|
| 116 |
-
isOpen: true,
|
| 117 |
-
children: [
|
| 118 |
-
{ id: 'main', name: 'main.tex', type: 'file', content: DEFAULT_LATEX }
|
| 119 |
-
]
|
| 120 |
-
}])
|
| 121 |
-
setCode(DEFAULT_LATEX)
|
| 122 |
-
setActiveFileName('main.tex')
|
| 123 |
-
}
|
| 124 |
-
} catch (err) {
|
| 125 |
-
console.error('Error loading files:', err)
|
| 126 |
-
// Create default on error
|
| 127 |
-
setFiles([{
|
| 128 |
-
id: 'root',
|
| 129 |
-
name: 'LaTeX Files',
|
| 130 |
-
type: 'folder',
|
| 131 |
-
isOpen: true,
|
| 132 |
-
children: [
|
| 133 |
-
{ id: 'main', name: 'main.tex', type: 'file', content: DEFAULT_LATEX }
|
| 134 |
-
]
|
| 135 |
-
}])
|
| 136 |
-
setCode(DEFAULT_LATEX)
|
| 137 |
-
setActiveFileName('main.tex')
|
| 138 |
-
} finally {
|
| 139 |
-
setLoading(false)
|
| 140 |
-
}
|
| 141 |
-
}
|
| 142 |
-
|
| 143 |
-
const handleUnlock = async () => {
|
| 144 |
-
if (tempPasskey.trim().length >= 4) {
|
| 145 |
-
setPasskey(tempPasskey.trim())
|
| 146 |
-
setIsUnlocked(true)
|
| 147 |
-
await loadFiles(tempPasskey.trim())
|
| 148 |
-
} else {
|
| 149 |
-
alert('Passkey must be at least 4 characters')
|
| 150 |
-
}
|
| 151 |
-
}
|
| 152 |
-
|
| 153 |
-
const handleFileClick = (file: FileNode) => {
|
| 154 |
-
if (file.type === 'file') {
|
| 155 |
-
setActiveFileId(file.id)
|
| 156 |
-
setActiveFileName(file.name)
|
| 157 |
-
setCode(file.content || '')
|
| 158 |
-
}
|
| 159 |
-
}
|
| 160 |
-
|
| 161 |
-
// Update code if initialContent changes (e.g. opening a new file)
|
| 162 |
-
useEffect(() => {
|
| 163 |
-
if (initialContent) {
|
| 164 |
-
setCode(initialContent)
|
| 165 |
-
// Check if we have a passkey from session
|
| 166 |
-
const sessionPasskey = sessionStorage.getItem('currentPasskey')
|
| 167 |
-
if (sessionPasskey) {
|
| 168 |
-
setPasskey(sessionPasskey)
|
| 169 |
-
setIsUnlocked(true)
|
| 170 |
-
loadFiles(sessionPasskey)
|
| 171 |
-
}
|
| 172 |
-
}
|
| 173 |
-
}, [initialContent])
|
| 174 |
-
|
| 175 |
-
// Auto-save functionality
|
| 176 |
-
useEffect(() => {
|
| 177 |
-
if (!passkey || !isUnlocked || !activeFileName) return
|
| 178 |
-
|
| 179 |
-
const saveTimer = setTimeout(async () => {
|
| 180 |
-
setIsSaving(true)
|
| 181 |
-
try {
|
| 182 |
-
await fetch('/api/data', {
|
| 183 |
-
method: 'POST',
|
| 184 |
-
headers: { 'Content-Type': 'application/json' },
|
| 185 |
-
body: JSON.stringify({
|
| 186 |
-
key: passkey,
|
| 187 |
-
passkey: passkey,
|
| 188 |
-
action: 'save_file',
|
| 189 |
-
fileName: activeFileName,
|
| 190 |
-
content: code
|
| 191 |
-
})
|
| 192 |
-
})
|
| 193 |
-
setLastSaved(new Date())
|
| 194 |
-
} catch (err) {
|
| 195 |
-
console.error('Auto-save failed:', err)
|
| 196 |
-
} finally {
|
| 197 |
-
setIsSaving(false)
|
| 198 |
-
}
|
| 199 |
-
}, 2000) // Debounce 2s
|
| 200 |
-
|
| 201 |
-
return () => clearTimeout(saveTimer)
|
| 202 |
-
}, [code, passkey, activeFileName, isUnlocked])
|
| 203 |
-
|
| 204 |
-
// Render LaTeX preview
|
| 205 |
-
useEffect(() => {
|
| 206 |
-
if (previewRef.current) {
|
| 207 |
-
try {
|
| 208 |
-
// Extract title, author, and date from LaTeX preamble
|
| 209 |
-
let title = 'Untitled Document'
|
| 210 |
-
let author = 'Anonymous'
|
| 211 |
-
let date = new Date().toLocaleDateString()
|
| 212 |
-
|
| 213 |
-
const titleMatch = code.match(/\\title\{([^}]+)\}/)
|
| 214 |
-
const authorMatch = code.match(/\\author\{([^}]+)\}/)
|
| 215 |
-
const dateMatch = code.match(/\\date\{([^}]+)\}/)
|
| 216 |
-
|
| 217 |
-
if (titleMatch) title = titleMatch[1]
|
| 218 |
-
if (authorMatch) author = authorMatch[1]
|
| 219 |
-
if (dateMatch) {
|
| 220 |
-
date = dateMatch[1].replace(/\\today/g, new Date().toLocaleDateString())
|
| 221 |
-
}
|
| 222 |
-
|
| 223 |
-
// Process the document content
|
| 224 |
-
let processedText = code
|
| 225 |
-
// Remove preamble
|
| 226 |
-
.replace(/\\documentclass\{[^}]+\}/g, '')
|
| 227 |
-
.replace(/\\usepackage\{[^}]+\}/g, '')
|
| 228 |
-
.replace(/\\title\{[^}]+\}/g, '')
|
| 229 |
-
.replace(/\\author\{[^}]+\}/g, '')
|
| 230 |
-
.replace(/\\date\{[^}]+\}/g, '')
|
| 231 |
-
.replace(/\\begin\{document\}/g, '')
|
| 232 |
-
.replace(/\\end\{document\}/g, '')
|
| 233 |
-
|
| 234 |
-
// Handle title
|
| 235 |
-
.replace(/\\maketitle/g, `
|
| 236 |
-
<div style="text-align: center; margin-bottom: 2em; padding-bottom: 1em; border-bottom: 1px solid #ccc;">
|
| 237 |
-
<h1 style="font-size: 2em; margin: 0.5em 0;">${title}</h1>
|
| 238 |
-
<p style="margin: 0.3em 0; font-size: 1.1em;">${author}</p>
|
| 239 |
-
<p style="margin: 0.3em 0; color: #666;">${date}</p>
|
| 240 |
-
</div>
|
| 241 |
-
`)
|
| 242 |
-
|
| 243 |
-
// Sections and structure
|
| 244 |
-
.replace(/\\section\{([^}]+)\}/g, '<h2 style="font-size: 1.5em; margin-top: 1.5em; margin-bottom: 0.5em; font-weight: bold;">$1</h2>')
|
| 245 |
-
.replace(/\\subsection\{([^}]+)\}/g, '<h3 style="font-size: 1.2em; margin-top: 1em; margin-bottom: 0.5em; font-weight: bold;">$1</h3>')
|
| 246 |
-
.replace(/\\subsubsection\{([^}]+)\}/g, '<h4 style="font-size: 1em; margin-top: 0.8em; margin-bottom: 0.3em; font-weight: bold;">$1</h4>')
|
| 247 |
-
.replace(/\\paragraph\{([^}]+)\}/g, '<p style="font-weight: bold; margin-top: 0.5em;">$1</p>')
|
| 248 |
-
|
| 249 |
-
// Text formatting
|
| 250 |
-
.replace(/\\textbf\{([^}]+)\}/g, '<strong>$1</strong>')
|
| 251 |
-
.replace(/\\textit\{([^}]+)\}/g, '<em>$1</em>')
|
| 252 |
-
.replace(/\\underline\{([^}]+)\}/g, '<u>$1</u>')
|
| 253 |
-
.replace(/\\emph\{([^}]+)\}/g, '<em>$1</em>')
|
| 254 |
-
.replace(/\\texttt\{([^}]+)\}/g, '<code style="font-family: monospace; background: #f5f5f5; padding: 2px 4px; border-radius: 3px;">$1</code>')
|
| 255 |
-
|
| 256 |
-
// Lists
|
| 257 |
-
.replace(/\\begin\{itemize\}/g, '<ul style="margin: 0.5em 0; padding-left: 1.5em;">')
|
| 258 |
-
.replace(/\\end\{itemize\}/g, '</ul>')
|
| 259 |
-
.replace(/\\begin\{enumerate\}/g, '<ol style="margin: 0.5em 0; padding-left: 1.5em;">')
|
| 260 |
-
.replace(/\\end\{enumerate\}/g, '</ol>')
|
| 261 |
-
.replace(/\\item\s+/g, '<li style="margin: 0.3em 0;">')
|
| 262 |
-
|
| 263 |
-
// Paragraphs and line breaks
|
| 264 |
-
.replace(/\\\\/g, '<br />')
|
| 265 |
-
.replace(/\\newline/g, '<br />')
|
| 266 |
-
.replace(/\\par\b/g, '</p><p>')
|
| 267 |
-
|
| 268 |
-
// Clean up extra whitespace
|
| 269 |
-
.replace(/\n\n+/g, '</p><p style="margin: 0.5em 0;">')
|
| 270 |
-
|
| 271 |
-
// Split by math delimiters to render KaTeX (improved regex for display and inline math)
|
| 272 |
-
const parts = processedText.split(/(\\\[[\s\S]*?\\\]|\$\$[\s\S]*?\$\$|\$[^$]+\$|\\\([\s\S]*?\\\))/)
|
| 273 |
-
previewRef.current.innerHTML = ''
|
| 274 |
-
|
| 275 |
-
parts.forEach(part => {
|
| 276 |
-
if (part.startsWith('\\[') || part.startsWith('$$') || part.startsWith('$') || part.startsWith('\\(')) {
|
| 277 |
-
// Determine if it's display math or inline math
|
| 278 |
-
const isDisplayMath = part.startsWith('\\[') || part.startsWith('$$')
|
| 279 |
-
|
| 280 |
-
// Extract the math content
|
| 281 |
-
let math = part
|
| 282 |
-
if (part.startsWith('\\[') && part.endsWith('\\]')) {
|
| 283 |
-
math = part.slice(2, -2)
|
| 284 |
-
} else if (part.startsWith('$$') && part.endsWith('$$')) {
|
| 285 |
-
math = part.slice(2, -2)
|
| 286 |
-
} else if (part.startsWith('$') && part.endsWith('$')) {
|
| 287 |
-
math = part.slice(1, -1)
|
| 288 |
-
} else if (part.startsWith('\\(') && part.endsWith('\\)')) {
|
| 289 |
-
math = part.slice(2, -2)
|
| 290 |
-
}
|
| 291 |
-
|
| 292 |
-
const span = document.createElement(isDisplayMath ? 'div' : 'span')
|
| 293 |
-
if (isDisplayMath) {
|
| 294 |
-
span.style.textAlign = 'center'
|
| 295 |
-
span.style.margin = '1em 0'
|
| 296 |
-
}
|
| 297 |
-
|
| 298 |
-
try {
|
| 299 |
-
katex.render(math, span, {
|
| 300 |
-
throwOnError: false,
|
| 301 |
-
displayMode: isDisplayMath,
|
| 302 |
-
strict: false
|
| 303 |
-
})
|
| 304 |
-
previewRef.current?.appendChild(span)
|
| 305 |
-
} catch (e) {
|
| 306 |
-
// If KaTeX fails, show the raw math
|
| 307 |
-
const errorSpan = document.createElement('span')
|
| 308 |
-
errorSpan.style.color = '#cc0000'
|
| 309 |
-
errorSpan.textContent = part
|
| 310 |
-
previewRef.current?.appendChild(errorSpan)
|
| 311 |
-
}
|
| 312 |
-
} else if (part.trim()) {
|
| 313 |
-
// Only add non-empty parts
|
| 314 |
-
const div = document.createElement('div')
|
| 315 |
-
div.innerHTML = part
|
| 316 |
-
div.style.margin = '0.5em 0'
|
| 317 |
-
previewRef.current?.appendChild(div)
|
| 318 |
-
}
|
| 319 |
-
})
|
| 320 |
-
} catch (e) {
|
| 321 |
-
console.error('Render error:', e)
|
| 322 |
-
}
|
| 323 |
-
}
|
| 324 |
-
}, [code])
|
| 325 |
-
|
| 326 |
-
const handleDownload = () => {
|
| 327 |
-
const blob = new Blob([code], { type: 'text/plain' })
|
| 328 |
-
const url = URL.createObjectURL(blob)
|
| 329 |
-
const a = document.createElement('a')
|
| 330 |
-
a.href = url
|
| 331 |
-
a.download = activeFileName || 'main.tex'
|
| 332 |
-
a.click()
|
| 333 |
-
URL.revokeObjectURL(url)
|
| 334 |
-
}
|
| 335 |
-
|
| 336 |
-
return (
|
| 337 |
-
<Window
|
| 338 |
-
id="latex-editor"
|
| 339 |
-
title="LaTeX Studio"
|
| 340 |
-
isOpen={true}
|
| 341 |
-
onClose={onClose}
|
| 342 |
-
onMinimize={onMinimize}
|
| 343 |
-
onMaximize={onMaximize}
|
| 344 |
-
onFocus={onFocus}
|
| 345 |
-
zIndex={zIndex}
|
| 346 |
-
width={1200}
|
| 347 |
-
height={800}
|
| 348 |
-
x={60}
|
| 349 |
-
y={60}
|
| 350 |
-
className="latex-studio-window"
|
| 351 |
-
>
|
| 352 |
-
{!isUnlocked ? (
|
| 353 |
-
<div className="flex h-full bg-[#1e1e1e] items-center justify-center">
|
| 354 |
-
<div className="bg-[#252526] p-8 rounded-lg shadow-xl border border-[#333] max-w-md w-full">
|
| 355 |
-
<h2 className="text-xl font-bold text-white mb-4">Enter Passkey</h2>
|
| 356 |
-
<p className="text-gray-400 mb-6">Enter your passkey to access LaTeX files</p>
|
| 357 |
-
<input
|
| 358 |
-
type="password"
|
| 359 |
-
value={tempPasskey}
|
| 360 |
-
onChange={(e) => setTempPasskey(e.target.value)}
|
| 361 |
-
onKeyPress={(e) => e.key === 'Enter' && handleUnlock()}
|
| 362 |
-
placeholder="Your passkey"
|
| 363 |
-
className="w-full px-4 py-2 bg-[#1e1e1e] border border-[#333] rounded text-white focus:outline-none focus:border-blue-500"
|
| 364 |
-
autoFocus
|
| 365 |
-
/>
|
| 366 |
-
<button
|
| 367 |
-
onClick={handleUnlock}
|
| 368 |
-
disabled={loading}
|
| 369 |
-
className="w-full mt-4 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded font-medium disabled:opacity-50"
|
| 370 |
-
>
|
| 371 |
-
{loading ? 'Loading...' : 'Unlock'}
|
| 372 |
-
</button>
|
| 373 |
-
</div>
|
| 374 |
-
</div>
|
| 375 |
-
) : (
|
| 376 |
-
<div className="flex h-full bg-[#1e1e1e] text-gray-300 font-sans">
|
| 377 |
-
{/* Sidebar */}
|
| 378 |
-
{showSidebar && (
|
| 379 |
-
<div className="w-64 bg-[#252526] border-r border-[#333] flex flex-col">
|
| 380 |
-
<div className="h-9 px-4 flex items-center text-xs font-bold text-gray-500 uppercase tracking-wider">
|
| 381 |
-
Explorer
|
| 382 |
-
</div>
|
| 383 |
-
<div className="flex-1 overflow-y-auto py-2">
|
| 384 |
-
<div className="px-2">
|
| 385 |
-
<div className="flex items-center gap-1 py-1 px-2 text-sm text-gray-300 hover:bg-[#2a2d2e] rounded cursor-pointer">
|
| 386 |
-
<CaretDown size={12} weight="bold" />
|
| 387 |
-
<span className="font-bold">PROJECT</span>
|
| 388 |
-
</div>
|
| 389 |
-
<div className="pl-4">
|
| 390 |
-
{files.map(file => (
|
| 391 |
-
<div key={file.id}>
|
| 392 |
-
<div className="flex items-center gap-2 py-1 px-2 text-sm hover:bg-[#2a2d2e] rounded cursor-pointer text-blue-400">
|
| 393 |
-
<CaretDown size={12} weight="bold" />
|
| 394 |
-
{file.name}
|
| 395 |
-
</div>
|
| 396 |
-
{file.children?.map(child => (
|
| 397 |
-
<div
|
| 398 |
-
key={child.id}
|
| 399 |
-
onClick={() => handleFileClick(child)}
|
| 400 |
-
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]'
|
| 401 |
-
}`}
|
| 402 |
-
>
|
| 403 |
-
<FileText size={14} />
|
| 404 |
-
{child.name}
|
| 405 |
-
</div>
|
| 406 |
-
))}
|
| 407 |
-
</div>
|
| 408 |
-
))}
|
| 409 |
-
</div>
|
| 410 |
-
</div>
|
| 411 |
-
</div>
|
| 412 |
-
</div>
|
| 413 |
-
)}
|
| 414 |
-
|
| 415 |
-
{/* Main Content */}
|
| 416 |
-
<div className="flex-1 flex flex-col min-w-0">
|
| 417 |
-
{/* Toolbar */}
|
| 418 |
-
<div className="h-10 bg-[#2d2d2d] border-b border-[#1e1e1e] flex items-center justify-between px-4">
|
| 419 |
-
<div className="flex items-center gap-2">
|
| 420 |
-
<button
|
| 421 |
-
onClick={() => setShowSidebar(!showSidebar)}
|
| 422 |
-
className={`p-1.5 rounded hover:bg-[#3e3e42] ${showSidebar ? 'text-white' : 'text-gray-500'}`}
|
| 423 |
-
title="Toggle Sidebar"
|
| 424 |
-
>
|
| 425 |
-
<SidebarSimple size={16} />
|
| 426 |
-
</button>
|
| 427 |
-
<div className="h-4 w-[1px] bg-gray-600 mx-2" />
|
| 428 |
-
<div className="flex items-center gap-2 px-3 py-1 bg-[#1e1e1e] rounded text-xs text-gray-400 border border-[#333]">
|
| 429 |
-
<FileText size={14} className="text-green-400" />
|
| 430 |
-
{activeFileName}
|
| 431 |
-
</div>
|
| 432 |
-
{lastSaved && (
|
| 433 |
-
<div className="flex items-center gap-1 text-xs text-gray-500 ml-2">
|
| 434 |
-
{isSaving ? (
|
| 435 |
-
<span className="text-yellow-500">Saving...</span>
|
| 436 |
-
) : (
|
| 437 |
-
<>
|
| 438 |
-
<Check size={12} className="text-green-500" />
|
| 439 |
-
<span>Saved {lastSaved.toLocaleTimeString()}</span>
|
| 440 |
-
</>
|
| 441 |
-
)}
|
| 442 |
-
</div>
|
| 443 |
-
)}
|
| 444 |
-
</div>
|
| 445 |
-
|
| 446 |
-
<div className="flex items-center gap-2">
|
| 447 |
-
<button
|
| 448 |
-
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"
|
| 449 |
-
>
|
| 450 |
-
<Play size={14} weight="fill" />
|
| 451 |
-
Compile
|
| 452 |
-
</button>
|
| 453 |
-
<button
|
| 454 |
-
onClick={handleDownload}
|
| 455 |
-
className="p-1.5 text-gray-400 hover:text-white hover:bg-[#3e3e42] rounded transition-colors"
|
| 456 |
-
title="Download PDF"
|
| 457 |
-
>
|
| 458 |
-
<Download size={16} />
|
| 459 |
-
</button>
|
| 460 |
-
</div>
|
| 461 |
-
</div>
|
| 462 |
-
|
| 463 |
-
{/* Split View: Editor & Preview */}
|
| 464 |
-
<div className="flex-1 flex overflow-hidden">
|
| 465 |
-
{/* Editor */}
|
| 466 |
-
<div className="flex-1 border-r border-[#333]">
|
| 467 |
-
<Editor
|
| 468 |
-
height="100%"
|
| 469 |
-
defaultLanguage="latex"
|
| 470 |
-
theme="vs-dark"
|
| 471 |
-
value={code}
|
| 472 |
-
onChange={(value) => setCode(value || '')}
|
| 473 |
-
options={{
|
| 474 |
-
minimap: { enabled: false },
|
| 475 |
-
fontSize: 14,
|
| 476 |
-
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
| 477 |
-
lineNumbers: 'on',
|
| 478 |
-
scrollBeyondLastLine: false,
|
| 479 |
-
automaticLayout: true,
|
| 480 |
-
padding: { top: 16, bottom: 16 },
|
| 481 |
-
renderLineHighlight: 'all',
|
| 482 |
-
smoothScrolling: true,
|
| 483 |
-
cursorBlinking: 'smooth',
|
| 484 |
-
cursorSmoothCaretAnimation: 'on'
|
| 485 |
-
}}
|
| 486 |
-
/>
|
| 487 |
-
</div>
|
| 488 |
-
|
| 489 |
-
{/* Preview */}
|
| 490 |
-
<div className="w-[500px] bg-[#525659] flex flex-col border-l border-[#333]">
|
| 491 |
-
<div className="h-8 bg-[#323639] border-b border-[#2b2b2b] flex items-center justify-between px-3 shadow-md z-10">
|
| 492 |
-
<span className="text-xs font-bold text-gray-400 uppercase">PDF Preview</span>
|
| 493 |
-
<div className="flex items-center gap-2">
|
| 494 |
-
<span className="text-xs text-gray-400">100%</span>
|
| 495 |
-
</div>
|
| 496 |
-
</div>
|
| 497 |
-
<div className="flex-1 overflow-y-auto p-8 flex justify-center">
|
| 498 |
-
<div
|
| 499 |
-
className="bg-white w-full max-w-[800px] min-h-[1000px] shadow-2xl p-12 text-black font-serif"
|
| 500 |
-
ref={previewRef}
|
| 501 |
-
style={{
|
| 502 |
-
boxShadow: '0 0 20px rgba(0,0,0,0.5)'
|
| 503 |
-
}}
|
| 504 |
-
>
|
| 505 |
-
{/* Content rendered via useEffect */}
|
| 506 |
-
</div>
|
| 507 |
-
</div>
|
| 508 |
-
</div>
|
| 509 |
-
</div>
|
| 510 |
-
</div>
|
| 511 |
-
</div>
|
| 512 |
-
)}
|
| 513 |
-
</Window>
|
| 514 |
-
)
|
| 515 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package-lock.json
CHANGED
|
@@ -20,8 +20,8 @@
|
|
| 20 |
"framer-motion": "^12.23.24",
|
| 21 |
"highlight.js": "^11.11.1",
|
| 22 |
"html-pdf-node": "^1.0.8",
|
| 23 |
-
"katex": "^0.16.25",
|
| 24 |
"latex": "^0.0.1",
|
|
|
|
| 25 |
"lucide-react": "^0.553.0",
|
| 26 |
"mammoth": "^1.11.0",
|
| 27 |
"monaco-editor": "^0.54.0",
|
|
@@ -2504,6 +2504,12 @@
|
|
| 2504 |
"node": ">= 20"
|
| 2505 |
}
|
| 2506 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2507 |
"node_modules/@phosphor-icons/react": {
|
| 2508 |
"version": "2.1.10",
|
| 2509 |
"resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz",
|
|
@@ -3926,6 +3932,15 @@
|
|
| 3926 |
"node": ">=10.0.0"
|
| 3927 |
}
|
| 3928 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3929 |
"node_modules/accepts": {
|
| 3930 |
"version": "2.0.0",
|
| 3931 |
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
|
@@ -5146,6 +5161,16 @@
|
|
| 5146 |
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
| 5147 |
"license": "MIT"
|
| 5148 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5149 |
"node_modules/content-disposition": {
|
| 5150 |
"version": "1.0.0",
|
| 5151 |
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
|
@@ -5712,6 +5737,69 @@
|
|
| 5712 |
"safe-buffer": "^5.0.1"
|
| 5713 |
}
|
| 5714 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5715 |
"node_modules/ee-first": {
|
| 5716 |
"version": "1.1.1",
|
| 5717 |
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
@@ -7867,6 +7955,28 @@
|
|
| 7867 |
"node": ">= 6"
|
| 7868 |
}
|
| 7869 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7870 |
"node_modules/iconv-lite": {
|
| 7871 |
"version": "0.6.3",
|
| 7872 |
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
@@ -7909,6 +8019,21 @@
|
|
| 7909 |
"node": ">= 4"
|
| 7910 |
}
|
| 7911 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7912 |
"node_modules/immediate": {
|
| 7913 |
"version": "3.0.6",
|
| 7914 |
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
|
@@ -7959,6 +8084,12 @@
|
|
| 7959 |
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 7960 |
"license": "ISC"
|
| 7961 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7962 |
"node_modules/inline-css": {
|
| 7963 |
"version": "3.0.0",
|
| 7964 |
"resolved": "https://registry.npmjs.org/inline-css/-/inline-css-3.0.0.tgz",
|
|
@@ -8564,6 +8695,70 @@
|
|
| 8564 |
"integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==",
|
| 8565 |
"license": "MIT"
|
| 8566 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8567 |
"node_modules/js-tokens": {
|
| 8568 |
"version": "4.0.0",
|
| 8569 |
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
|
@@ -8693,22 +8888,6 @@
|
|
| 8693 |
"safe-buffer": "^5.0.1"
|
| 8694 |
}
|
| 8695 |
},
|
| 8696 |
-
"node_modules/katex": {
|
| 8697 |
-
"version": "0.16.25",
|
| 8698 |
-
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz",
|
| 8699 |
-
"integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==",
|
| 8700 |
-
"funding": [
|
| 8701 |
-
"https://opencollective.com/katex",
|
| 8702 |
-
"https://github.com/sponsors/katex"
|
| 8703 |
-
],
|
| 8704 |
-
"license": "MIT",
|
| 8705 |
-
"dependencies": {
|
| 8706 |
-
"commander": "^8.3.0"
|
| 8707 |
-
},
|
| 8708 |
-
"bin": {
|
| 8709 |
-
"katex": "cli.js"
|
| 8710 |
-
}
|
| 8711 |
-
},
|
| 8712 |
"node_modules/keyv": {
|
| 8713 |
"version": "4.5.4",
|
| 8714 |
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
|
@@ -8750,6 +8929,53 @@
|
|
| 8750 |
"through": "~2.1.0"
|
| 8751 |
}
|
| 8752 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8753 |
"node_modules/lazystream": {
|
| 8754 |
"version": "1.0.1",
|
| 8755 |
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
|
|
@@ -10627,6 +10853,21 @@
|
|
| 10627 |
"dev": true,
|
| 10628 |
"license": "MIT"
|
| 10629 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10630 |
"node_modules/normalize-path": {
|
| 10631 |
"version": "3.0.0",
|
| 10632 |
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
|
@@ -11556,6 +11797,12 @@
|
|
| 11556 |
"url": "https://github.com/sponsors/wooorm"
|
| 11557 |
}
|
| 11558 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11559 |
"node_modules/proxy-addr": {
|
| 11560 |
"version": "2.0.7",
|
| 11561 |
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
|
@@ -11778,6 +12025,15 @@
|
|
| 11778 |
"url": "https://github.com/sponsors/ljharb"
|
| 11779 |
}
|
| 11780 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11781 |
"node_modules/queue-microtask": {
|
| 11782 |
"version": "1.2.3",
|
| 11783 |
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
|
@@ -12883,6 +13139,11 @@
|
|
| 12883 |
"node": ">= 0.8"
|
| 12884 |
}
|
| 12885 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12886 |
"node_modules/stop-iteration-iterator": {
|
| 12887 |
"version": "1.1.0",
|
| 12888 |
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
|
@@ -13211,6 +13472,21 @@
|
|
| 13211 |
"url": "https://github.com/sponsors/ljharb"
|
| 13212 |
}
|
| 13213 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13214 |
"node_modules/tailwindcss": {
|
| 13215 |
"version": "4.1.17",
|
| 13216 |
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
|
@@ -13883,6 +14159,15 @@
|
|
| 13883 |
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
| 13884 |
"license": "ISC"
|
| 13885 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13886 |
"node_modules/unpipe": {
|
| 13887 |
"version": "1.0.0",
|
| 13888 |
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
|
|
| 20 |
"framer-motion": "^12.23.24",
|
| 21 |
"highlight.js": "^11.11.1",
|
| 22 |
"html-pdf-node": "^1.0.8",
|
|
|
|
| 23 |
"latex": "^0.0.1",
|
| 24 |
+
"latex.js": "^0.12.6",
|
| 25 |
"lucide-react": "^0.553.0",
|
| 26 |
"mammoth": "^1.11.0",
|
| 27 |
"monaco-editor": "^0.54.0",
|
|
|
|
| 2504 |
"node": ">= 20"
|
| 2505 |
}
|
| 2506 |
},
|
| 2507 |
+
"node_modules/@one-ini/wasm": {
|
| 2508 |
+
"version": "0.1.1",
|
| 2509 |
+
"resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
|
| 2510 |
+
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
|
| 2511 |
+
"license": "MIT"
|
| 2512 |
+
},
|
| 2513 |
"node_modules/@phosphor-icons/react": {
|
| 2514 |
"version": "2.1.10",
|
| 2515 |
"resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz",
|
|
|
|
| 3932 |
"node": ">=10.0.0"
|
| 3933 |
}
|
| 3934 |
},
|
| 3935 |
+
"node_modules/abbrev": {
|
| 3936 |
+
"version": "2.0.0",
|
| 3937 |
+
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
|
| 3938 |
+
"integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
|
| 3939 |
+
"license": "ISC",
|
| 3940 |
+
"engines": {
|
| 3941 |
+
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
| 3942 |
+
}
|
| 3943 |
+
},
|
| 3944 |
"node_modules/accepts": {
|
| 3945 |
"version": "2.0.0",
|
| 3946 |
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
|
|
|
| 5161 |
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
| 5162 |
"license": "MIT"
|
| 5163 |
},
|
| 5164 |
+
"node_modules/config-chain": {
|
| 5165 |
+
"version": "1.1.13",
|
| 5166 |
+
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
|
| 5167 |
+
"integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
|
| 5168 |
+
"license": "MIT",
|
| 5169 |
+
"dependencies": {
|
| 5170 |
+
"ini": "^1.3.4",
|
| 5171 |
+
"proto-list": "~1.2.1"
|
| 5172 |
+
}
|
| 5173 |
+
},
|
| 5174 |
"node_modules/content-disposition": {
|
| 5175 |
"version": "1.0.0",
|
| 5176 |
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
|
|
|
| 5737 |
"safe-buffer": "^5.0.1"
|
| 5738 |
}
|
| 5739 |
},
|
| 5740 |
+
"node_modules/editorconfig": {
|
| 5741 |
+
"version": "1.0.4",
|
| 5742 |
+
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz",
|
| 5743 |
+
"integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==",
|
| 5744 |
+
"license": "MIT",
|
| 5745 |
+
"dependencies": {
|
| 5746 |
+
"@one-ini/wasm": "0.1.1",
|
| 5747 |
+
"commander": "^10.0.0",
|
| 5748 |
+
"minimatch": "9.0.1",
|
| 5749 |
+
"semver": "^7.5.3"
|
| 5750 |
+
},
|
| 5751 |
+
"bin": {
|
| 5752 |
+
"editorconfig": "bin/editorconfig"
|
| 5753 |
+
},
|
| 5754 |
+
"engines": {
|
| 5755 |
+
"node": ">=14"
|
| 5756 |
+
}
|
| 5757 |
+
},
|
| 5758 |
+
"node_modules/editorconfig/node_modules/brace-expansion": {
|
| 5759 |
+
"version": "2.0.2",
|
| 5760 |
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
| 5761 |
+
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
| 5762 |
+
"license": "MIT",
|
| 5763 |
+
"dependencies": {
|
| 5764 |
+
"balanced-match": "^1.0.0"
|
| 5765 |
+
}
|
| 5766 |
+
},
|
| 5767 |
+
"node_modules/editorconfig/node_modules/commander": {
|
| 5768 |
+
"version": "10.0.1",
|
| 5769 |
+
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
| 5770 |
+
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
| 5771 |
+
"license": "MIT",
|
| 5772 |
+
"engines": {
|
| 5773 |
+
"node": ">=14"
|
| 5774 |
+
}
|
| 5775 |
+
},
|
| 5776 |
+
"node_modules/editorconfig/node_modules/minimatch": {
|
| 5777 |
+
"version": "9.0.1",
|
| 5778 |
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
|
| 5779 |
+
"integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
|
| 5780 |
+
"license": "ISC",
|
| 5781 |
+
"dependencies": {
|
| 5782 |
+
"brace-expansion": "^2.0.1"
|
| 5783 |
+
},
|
| 5784 |
+
"engines": {
|
| 5785 |
+
"node": ">=16 || 14 >=14.17"
|
| 5786 |
+
},
|
| 5787 |
+
"funding": {
|
| 5788 |
+
"url": "https://github.com/sponsors/isaacs"
|
| 5789 |
+
}
|
| 5790 |
+
},
|
| 5791 |
+
"node_modules/editorconfig/node_modules/semver": {
|
| 5792 |
+
"version": "7.7.3",
|
| 5793 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
|
| 5794 |
+
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
| 5795 |
+
"license": "ISC",
|
| 5796 |
+
"bin": {
|
| 5797 |
+
"semver": "bin/semver.js"
|
| 5798 |
+
},
|
| 5799 |
+
"engines": {
|
| 5800 |
+
"node": ">=10"
|
| 5801 |
+
}
|
| 5802 |
+
},
|
| 5803 |
"node_modules/ee-first": {
|
| 5804 |
"version": "1.1.1",
|
| 5805 |
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
|
|
| 7955 |
"node": ">= 6"
|
| 7956 |
}
|
| 7957 |
},
|
| 7958 |
+
"node_modules/hyphenation.de": {
|
| 7959 |
+
"version": "0.2.1",
|
| 7960 |
+
"resolved": "https://registry.npmjs.org/hyphenation.de/-/hyphenation.de-0.2.1.tgz",
|
| 7961 |
+
"integrity": "sha512-s6Y4TFA8xWjRLneOPI6HV/+wzfm2c2yurTvFaXlznmsbeI6waZhMpxu94fSXGNGsrPxrzI1zTtYDEWeEeaANnw==",
|
| 7962 |
+
"dependencies": {
|
| 7963 |
+
"hypher": "*"
|
| 7964 |
+
}
|
| 7965 |
+
},
|
| 7966 |
+
"node_modules/hyphenation.en-us": {
|
| 7967 |
+
"version": "0.2.1",
|
| 7968 |
+
"resolved": "https://registry.npmjs.org/hyphenation.en-us/-/hyphenation.en-us-0.2.1.tgz",
|
| 7969 |
+
"integrity": "sha512-ItXYgvIpfN8rfXl/GTBQC7DsSb5PPsKh9gGzViK/iWzCS5mvjDebFJ6xCcIYo8dal+nSp2rUzvTT7BosrKlL8A==",
|
| 7970 |
+
"dependencies": {
|
| 7971 |
+
"hypher": "*"
|
| 7972 |
+
}
|
| 7973 |
+
},
|
| 7974 |
+
"node_modules/hypher": {
|
| 7975 |
+
"version": "0.2.5",
|
| 7976 |
+
"resolved": "https://registry.npmjs.org/hypher/-/hypher-0.2.5.tgz",
|
| 7977 |
+
"integrity": "sha512-kUTpuyzBWWDO2VakmjHC/cxesg4lKQP+Fdc+7lrK4yvjNjkV9vm5UTZMDAwOyyHTOpbkYrAMlNZHG61NnE9vYQ==",
|
| 7978 |
+
"license": "BSD-3-Clause"
|
| 7979 |
+
},
|
| 7980 |
"node_modules/iconv-lite": {
|
| 7981 |
"version": "0.6.3",
|
| 7982 |
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
|
|
| 8019 |
"node": ">= 4"
|
| 8020 |
}
|
| 8021 |
},
|
| 8022 |
+
"node_modules/image-size": {
|
| 8023 |
+
"version": "1.2.1",
|
| 8024 |
+
"resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz",
|
| 8025 |
+
"integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==",
|
| 8026 |
+
"license": "MIT",
|
| 8027 |
+
"dependencies": {
|
| 8028 |
+
"queue": "6.0.2"
|
| 8029 |
+
},
|
| 8030 |
+
"bin": {
|
| 8031 |
+
"image-size": "bin/image-size.js"
|
| 8032 |
+
},
|
| 8033 |
+
"engines": {
|
| 8034 |
+
"node": ">=16.x"
|
| 8035 |
+
}
|
| 8036 |
+
},
|
| 8037 |
"node_modules/immediate": {
|
| 8038 |
"version": "3.0.6",
|
| 8039 |
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
|
|
|
| 8084 |
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 8085 |
"license": "ISC"
|
| 8086 |
},
|
| 8087 |
+
"node_modules/ini": {
|
| 8088 |
+
"version": "1.3.8",
|
| 8089 |
+
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
| 8090 |
+
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
| 8091 |
+
"license": "ISC"
|
| 8092 |
+
},
|
| 8093 |
"node_modules/inline-css": {
|
| 8094 |
"version": "3.0.0",
|
| 8095 |
"resolved": "https://registry.npmjs.org/inline-css/-/inline-css-3.0.0.tgz",
|
|
|
|
| 8695 |
"integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==",
|
| 8696 |
"license": "MIT"
|
| 8697 |
},
|
| 8698 |
+
"node_modules/js-beautify": {
|
| 8699 |
+
"version": "1.14.11",
|
| 8700 |
+
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz",
|
| 8701 |
+
"integrity": "sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==",
|
| 8702 |
+
"license": "MIT",
|
| 8703 |
+
"dependencies": {
|
| 8704 |
+
"config-chain": "^1.1.13",
|
| 8705 |
+
"editorconfig": "^1.0.3",
|
| 8706 |
+
"glob": "^10.3.3",
|
| 8707 |
+
"nopt": "^7.2.0"
|
| 8708 |
+
},
|
| 8709 |
+
"bin": {
|
| 8710 |
+
"css-beautify": "js/bin/css-beautify.js",
|
| 8711 |
+
"html-beautify": "js/bin/html-beautify.js",
|
| 8712 |
+
"js-beautify": "js/bin/js-beautify.js"
|
| 8713 |
+
},
|
| 8714 |
+
"engines": {
|
| 8715 |
+
"node": ">=14"
|
| 8716 |
+
}
|
| 8717 |
+
},
|
| 8718 |
+
"node_modules/js-beautify/node_modules/brace-expansion": {
|
| 8719 |
+
"version": "2.0.2",
|
| 8720 |
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
| 8721 |
+
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
| 8722 |
+
"license": "MIT",
|
| 8723 |
+
"dependencies": {
|
| 8724 |
+
"balanced-match": "^1.0.0"
|
| 8725 |
+
}
|
| 8726 |
+
},
|
| 8727 |
+
"node_modules/js-beautify/node_modules/glob": {
|
| 8728 |
+
"version": "10.5.0",
|
| 8729 |
+
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
| 8730 |
+
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
| 8731 |
+
"license": "ISC",
|
| 8732 |
+
"dependencies": {
|
| 8733 |
+
"foreground-child": "^3.1.0",
|
| 8734 |
+
"jackspeak": "^3.1.2",
|
| 8735 |
+
"minimatch": "^9.0.4",
|
| 8736 |
+
"minipass": "^7.1.2",
|
| 8737 |
+
"package-json-from-dist": "^1.0.0",
|
| 8738 |
+
"path-scurry": "^1.11.1"
|
| 8739 |
+
},
|
| 8740 |
+
"bin": {
|
| 8741 |
+
"glob": "dist/esm/bin.mjs"
|
| 8742 |
+
},
|
| 8743 |
+
"funding": {
|
| 8744 |
+
"url": "https://github.com/sponsors/isaacs"
|
| 8745 |
+
}
|
| 8746 |
+
},
|
| 8747 |
+
"node_modules/js-beautify/node_modules/minimatch": {
|
| 8748 |
+
"version": "9.0.5",
|
| 8749 |
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
| 8750 |
+
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
| 8751 |
+
"license": "ISC",
|
| 8752 |
+
"dependencies": {
|
| 8753 |
+
"brace-expansion": "^2.0.1"
|
| 8754 |
+
},
|
| 8755 |
+
"engines": {
|
| 8756 |
+
"node": ">=16 || 14 >=14.17"
|
| 8757 |
+
},
|
| 8758 |
+
"funding": {
|
| 8759 |
+
"url": "https://github.com/sponsors/isaacs"
|
| 8760 |
+
}
|
| 8761 |
+
},
|
| 8762 |
"node_modules/js-tokens": {
|
| 8763 |
"version": "4.0.0",
|
| 8764 |
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
|
|
|
| 8888 |
"safe-buffer": "^5.0.1"
|
| 8889 |
}
|
| 8890 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8891 |
"node_modules/keyv": {
|
| 8892 |
"version": "4.5.4",
|
| 8893 |
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
|
|
|
| 8929 |
"through": "~2.1.0"
|
| 8930 |
}
|
| 8931 |
},
|
| 8932 |
+
"node_modules/latex.js": {
|
| 8933 |
+
"version": "0.12.6",
|
| 8934 |
+
"resolved": "https://registry.npmjs.org/latex.js/-/latex.js-0.12.6.tgz",
|
| 8935 |
+
"integrity": "sha512-spMTeSq9cP4vidMQuPgoYGKmsQTMElZhDtNF3NyLIRc03XkuuPvMA0tzb0ShS/otaIJrB7DIHDk0GL3knCisEw==",
|
| 8936 |
+
"license": "MIT",
|
| 8937 |
+
"dependencies": {
|
| 8938 |
+
"commander": "8.x",
|
| 8939 |
+
"fs-extra": "10.x",
|
| 8940 |
+
"hyphenation.de": "*",
|
| 8941 |
+
"hyphenation.en-us": "*",
|
| 8942 |
+
"js-beautify": "1.14.x",
|
| 8943 |
+
"stdin": "*",
|
| 8944 |
+
"svgdom": "^0.1.8"
|
| 8945 |
+
},
|
| 8946 |
+
"bin": {
|
| 8947 |
+
"latex.js": "bin/latex.js"
|
| 8948 |
+
},
|
| 8949 |
+
"engines": {
|
| 8950 |
+
"node": ">= 14.0"
|
| 8951 |
+
}
|
| 8952 |
+
},
|
| 8953 |
+
"node_modules/latex.js/node_modules/fs-extra": {
|
| 8954 |
+
"version": "10.1.0",
|
| 8955 |
+
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
| 8956 |
+
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
| 8957 |
+
"license": "MIT",
|
| 8958 |
+
"dependencies": {
|
| 8959 |
+
"graceful-fs": "^4.2.0",
|
| 8960 |
+
"jsonfile": "^6.0.1",
|
| 8961 |
+
"universalify": "^2.0.0"
|
| 8962 |
+
},
|
| 8963 |
+
"engines": {
|
| 8964 |
+
"node": ">=12"
|
| 8965 |
+
}
|
| 8966 |
+
},
|
| 8967 |
+
"node_modules/latex.js/node_modules/jsonfile": {
|
| 8968 |
+
"version": "6.2.0",
|
| 8969 |
+
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
| 8970 |
+
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
| 8971 |
+
"license": "MIT",
|
| 8972 |
+
"dependencies": {
|
| 8973 |
+
"universalify": "^2.0.0"
|
| 8974 |
+
},
|
| 8975 |
+
"optionalDependencies": {
|
| 8976 |
+
"graceful-fs": "^4.1.6"
|
| 8977 |
+
}
|
| 8978 |
+
},
|
| 8979 |
"node_modules/lazystream": {
|
| 8980 |
"version": "1.0.1",
|
| 8981 |
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
|
|
|
|
| 10853 |
"dev": true,
|
| 10854 |
"license": "MIT"
|
| 10855 |
},
|
| 10856 |
+
"node_modules/nopt": {
|
| 10857 |
+
"version": "7.2.1",
|
| 10858 |
+
"resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
|
| 10859 |
+
"integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
|
| 10860 |
+
"license": "ISC",
|
| 10861 |
+
"dependencies": {
|
| 10862 |
+
"abbrev": "^2.0.0"
|
| 10863 |
+
},
|
| 10864 |
+
"bin": {
|
| 10865 |
+
"nopt": "bin/nopt.js"
|
| 10866 |
+
},
|
| 10867 |
+
"engines": {
|
| 10868 |
+
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
| 10869 |
+
}
|
| 10870 |
+
},
|
| 10871 |
"node_modules/normalize-path": {
|
| 10872 |
"version": "3.0.0",
|
| 10873 |
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
|
|
|
| 11797 |
"url": "https://github.com/sponsors/wooorm"
|
| 11798 |
}
|
| 11799 |
},
|
| 11800 |
+
"node_modules/proto-list": {
|
| 11801 |
+
"version": "1.2.4",
|
| 11802 |
+
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
| 11803 |
+
"integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
|
| 11804 |
+
"license": "ISC"
|
| 11805 |
+
},
|
| 11806 |
"node_modules/proxy-addr": {
|
| 11807 |
"version": "2.0.7",
|
| 11808 |
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
|
|
|
| 12025 |
"url": "https://github.com/sponsors/ljharb"
|
| 12026 |
}
|
| 12027 |
},
|
| 12028 |
+
"node_modules/queue": {
|
| 12029 |
+
"version": "6.0.2",
|
| 12030 |
+
"resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
|
| 12031 |
+
"integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
|
| 12032 |
+
"license": "MIT",
|
| 12033 |
+
"dependencies": {
|
| 12034 |
+
"inherits": "~2.0.3"
|
| 12035 |
+
}
|
| 12036 |
+
},
|
| 12037 |
"node_modules/queue-microtask": {
|
| 12038 |
"version": "1.2.3",
|
| 12039 |
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
|
|
|
| 13139 |
"node": ">= 0.8"
|
| 13140 |
}
|
| 13141 |
},
|
| 13142 |
+
"node_modules/stdin": {
|
| 13143 |
+
"version": "0.0.1",
|
| 13144 |
+
"resolved": "https://registry.npmjs.org/stdin/-/stdin-0.0.1.tgz",
|
| 13145 |
+
"integrity": "sha512-2bacd1TXzqOEsqRa+eEWkRdOSznwptrs4gqFcpMq5tOtmJUGPZd10W5Lam6wQ4YQ/+qjQt4e9u35yXCF6mrlfQ=="
|
| 13146 |
+
},
|
| 13147 |
"node_modules/stop-iteration-iterator": {
|
| 13148 |
"version": "1.1.0",
|
| 13149 |
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
|
|
|
| 13472 |
"url": "https://github.com/sponsors/ljharb"
|
| 13473 |
}
|
| 13474 |
},
|
| 13475 |
+
"node_modules/svgdom": {
|
| 13476 |
+
"version": "0.1.22",
|
| 13477 |
+
"resolved": "https://registry.npmjs.org/svgdom/-/svgdom-0.1.22.tgz",
|
| 13478 |
+
"integrity": "sha512-NPf43Dha2ocSjgyVSDWrKuY5XfV6+nngTzeVypRFNj+OjKUNwWr4eWx9IrWQ8wXdaHguOTHvzEji0v46z0iwKQ==",
|
| 13479 |
+
"license": "MIT",
|
| 13480 |
+
"dependencies": {
|
| 13481 |
+
"fontkit": "^2.0.4",
|
| 13482 |
+
"image-size": "^1.2.1",
|
| 13483 |
+
"sax": "^1.4.1"
|
| 13484 |
+
},
|
| 13485 |
+
"funding": {
|
| 13486 |
+
"type": "github",
|
| 13487 |
+
"url": "https://github.com/sponsors/Fuzzyma"
|
| 13488 |
+
}
|
| 13489 |
+
},
|
| 13490 |
"node_modules/tailwindcss": {
|
| 13491 |
"version": "4.1.17",
|
| 13492 |
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz",
|
|
|
|
| 14159 |
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
|
| 14160 |
"license": "ISC"
|
| 14161 |
},
|
| 14162 |
+
"node_modules/universalify": {
|
| 14163 |
+
"version": "2.0.1",
|
| 14164 |
+
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
| 14165 |
+
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
| 14166 |
+
"license": "MIT",
|
| 14167 |
+
"engines": {
|
| 14168 |
+
"node": ">= 10.0.0"
|
| 14169 |
+
}
|
| 14170 |
+
},
|
| 14171 |
"node_modules/unpipe": {
|
| 14172 |
"version": "1.0.0",
|
| 14173 |
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
package.json
CHANGED
|
@@ -24,8 +24,8 @@
|
|
| 24 |
"framer-motion": "^12.23.24",
|
| 25 |
"highlight.js": "^11.11.1",
|
| 26 |
"html-pdf-node": "^1.0.8",
|
| 27 |
-
"katex": "^0.16.25",
|
| 28 |
"latex": "^0.0.1",
|
|
|
|
| 29 |
"lucide-react": "^0.553.0",
|
| 30 |
"mammoth": "^1.11.0",
|
| 31 |
"monaco-editor": "^0.54.0",
|
|
|
|
| 24 |
"framer-motion": "^12.23.24",
|
| 25 |
"highlight.js": "^11.11.1",
|
| 26 |
"html-pdf-node": "^1.0.8",
|
|
|
|
| 27 |
"latex": "^0.0.1",
|
| 28 |
+
"latex.js": "^0.12.6",
|
| 29 |
"lucide-react": "^0.553.0",
|
| 30 |
"mammoth": "^1.11.0",
|
| 31 |
"monaco-editor": "^0.54.0",
|
public/data/reuben/main.tex
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
\documentclass{article}
|
| 2 |
+
\usepackage{amsmath}
|
| 3 |
+
\title{My LaTeX Document}
|
| 4 |
+
\author{Reuben OS}
|
| 5 |
+
\date{\today}
|
| 6 |
+
|
| 7 |
+
\begin{document}
|
| 8 |
+
|
| 9 |
+
\maketitle
|
| 10 |
+
|
| 11 |
+
\section{Introduction}
|
| 12 |
+
Welcome to LaTeX Studio on Reuben OS.
|
| 13 |
+
|
| 14 |
+
\section{Mathematics}
|
| 15 |
+
Here is an equation:
|
| 16 |
+
\[ E = mc^2 \]
|
| 17 |
+
|
| 18 |
+
\end{document}
|