Skip to content

Commit 59ec3e5

Browse files
committed
feat: implement static blog post loading at build time
1 parent b9c02dc commit 59ec3e5

File tree

4 files changed

+271
-212
lines changed

4 files changed

+271
-212
lines changed

src/app/page.tsx

Lines changed: 5 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -1,210 +1,7 @@
1-
"use client";
1+
import { MathStudio } from "@/components/math-studio";
2+
import { getStaticPosts } from "@/lib/posts";
23

3-
import React, { useState, useEffect } from "react";
4-
import { MarkdownEditor } from "@/components/editor";
5-
import { MarkdownPreview } from "@/components/preview";
6-
import { Sidebar } from "@/components/sidebar";
7-
import { Navbar } from "@/components/navbar";
8-
import { useFileSystem } from "@/hooks/use-file-system";
9-
import { cn } from "@/lib/utils";
10-
import { Columns, Eye, Code2 } from "lucide-react";
11-
12-
export default function Home() {
13-
const fileSystem = useFileSystem();
14-
const { files, activeFileId, updateFileContent } = fileSystem;
15-
16-
const [mounted, setMounted] = useState(false);
17-
const [viewMode, setViewMode] = useState<"split" | "editor" | "preview">("split");
18-
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
19-
20-
// Sidebar resizing & Mobile state
21-
const [sidebarWidth, setSidebarWidth] = useState(256);
22-
const [isResizing, setIsResizing] = useState(false);
23-
const [isMobile, setIsMobile] = useState(false);
24-
25-
useEffect(() => {
26-
setMounted(true);
27-
const checkMobile = () => {
28-
const mobile = window.innerWidth < 768;
29-
setIsMobile(mobile);
30-
if (mobile) {
31-
setIsSidebarOpen(false);
32-
if (viewMode === "split") setViewMode("editor");
33-
} else {
34-
setIsSidebarOpen(true);
35-
}
36-
};
37-
38-
checkMobile();
39-
window.addEventListener("resize", checkMobile);
40-
return () => window.removeEventListener("resize", checkMobile);
41-
}, []);
42-
43-
const startResizing = React.useCallback((e: React.MouseEvent) => {
44-
e.preventDefault();
45-
setIsResizing(true);
46-
}, []);
47-
48-
const stopResizing = React.useCallback(() => {
49-
setIsResizing(false);
50-
}, []);
51-
52-
const resize = React.useCallback(
53-
(mouseMoveEvent: MouseEvent) => {
54-
if (isResizing) {
55-
const newWidth = mouseMoveEvent.clientX;
56-
if (newWidth > 150 && newWidth < 600) {
57-
setSidebarWidth(newWidth);
58-
}
59-
}
60-
},
61-
[isResizing]
62-
);
63-
64-
useEffect(() => {
65-
window.addEventListener("mousemove", resize);
66-
window.addEventListener("mouseup", stopResizing);
67-
return () => {
68-
window.removeEventListener("mousemove", resize);
69-
window.removeEventListener("mouseup", stopResizing);
70-
};
71-
}, [resize, stopResizing]);
72-
73-
const activeFile = activeFileId ? files[activeFileId] : null;
74-
75-
const handleContentChange = (newContent: string) => {
76-
if (activeFileId) {
77-
updateFileContent(activeFileId, newContent);
78-
}
79-
};
80-
81-
if (!mounted) return null;
82-
83-
return (
84-
<div className="flex flex-col h-screen w-screen bg-background text-foreground overflow-hidden">
85-
<Navbar fileSystem={fileSystem} />
86-
87-
<div className="flex-1 flex overflow-hidden relative">
88-
{/* Mobile Backdrop */}
89-
{isMobile && isSidebarOpen && (
90-
<div
91-
className="absolute inset-0 bg-black/50 z-40"
92-
onClick={() => setIsSidebarOpen(false)}
93-
/>
94-
)}
95-
96-
{/* Sidebar */}
97-
<div
98-
className={cn(
99-
"h-full bg-card border-r border-border flex-shrink-0 relative z-50",
100-
isMobile ? "absolute left-0 top-0 bottom-0 shadow-2xl transition-transform duration-300" : "transition-[width] ease-linear duration-0",
101-
!isSidebarOpen && isMobile && "-translate-x-full",
102-
!isSidebarOpen && !isMobile && "w-0 border-none overflow-hidden"
103-
)}
104-
style={{ width: isMobile ? "80%" : (isSidebarOpen ? sidebarWidth : 0) }}
105-
>
106-
<Sidebar fileSystem={fileSystem} className="border-none" />
107-
108-
{/* Resizer Handle (Desktop only) */}
109-
{!isMobile && isSidebarOpen && (
110-
<div
111-
className="absolute right-0 top-0 w-1 h-full cursor-col-resize hover:bg-primary/50 transition-colors z-50 translate-x-1/2"
112-
onMouseDown={startResizing}
113-
/>
114-
)}
115-
</div>
116-
117-
{/* Main Area */}
118-
<main className="flex-1 flex flex-col overflow-hidden min-w-0 bg-background relative z-0">
119-
120-
{/* Toolbar */}
121-
<div className="h-10 border-b border-border flex items-center justify-between px-4 bg-muted/20 shrink-0">
122-
<div className="flex items-center gap-2 overflow-hidden">
123-
<button
124-
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
125-
className="p-1 hover:bg-accent rounded text-muted-foreground shrink-0"
126-
title="Toggle Sidebar"
127-
>
128-
{isSidebarOpen ? (
129-
<svg width="16" height="16" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.5 3C1.22386 3 1 3.22386 1 3.5V11.5C1 11.7761 1.22386 12 1.5 12H13.5C13.7761 12 14 11.7761 14 11.5V3.5C14 3.22386 13.7761 3 13.5 3H1.5ZM1 3.5C1 2.67157 1.67157 2 2.5 2H12.5C13.3284 2 14 2.67157 14 3.5V11.5C14 12.3284 13.3284 13 12.5 13H2.5C1.67157 13 1 12.3284 1 11.5V3.5Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path><path d="M5 3V12H4V3H5Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>
130-
) : (
131-
<svg width="16" height="16" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.5 3C1.22386 3 1 3.22386 1 3.5V11.5C1 11.7761 1.22386 12 1.5 12H13.5C13.7761 12 14 11.7761 14 11.5V3.5C14 3.22386 13.7761 3 13.5 3H1.5ZM1 3.5C1 2.67157 1.67157 2 2.5 2H12.5C13.3284 2 14 2.67157 14 3.5V11.5C14 12.3284 13.3284 13 12.5 13H2.5C1.67157 13 1 12.3284 1 11.5V3.5Z" fill="currentColor" fillRule="evenodd" clipRule="evenodd"></path></svg>
132-
)}
133-
</button>
134-
<span className="text-sm text-muted-foreground truncate">
135-
{activeFile ? activeFile.name : "No file selected"}
136-
</span>
137-
</div>
138-
139-
<div className="flex items-center gap-1 border border-input rounded-md overflow-hidden bg-background shrink-0">
140-
<button
141-
onClick={() => setViewMode("editor")}
142-
className={cn(
143-
"p-1.5 hover:bg-accent transition-colors",
144-
viewMode === "editor" && "bg-accent text-accent-foreground"
145-
)}
146-
title="Editor Only"
147-
>
148-
<Code2 size={16} />
149-
</button>
150-
<button
151-
onClick={() => setViewMode("split")}
152-
className={cn(
153-
"p-1.5 hover:bg-accent transition-colors",
154-
viewMode === "split" && "bg-accent text-accent-foreground"
155-
)}
156-
title="Split View"
157-
>
158-
<Columns size={16} />
159-
</button>
160-
<button
161-
onClick={() => setViewMode("preview")}
162-
className={cn(
163-
"p-1.5 hover:bg-accent transition-colors",
164-
viewMode === "preview" && "bg-accent text-accent-foreground"
165-
)}
166-
title="Preview Only"
167-
>
168-
<Eye size={16} />
169-
</button>
170-
</div>
171-
</div>
172-
173-
{/* Editor/Preview Area */}
174-
<div className="flex-1 flex overflow-hidden relative">
175-
{activeFile && activeFile.type === 'file' ? (
176-
<>
177-
{(viewMode === "split" || viewMode === "editor") && (
178-
<div className={cn(
179-
"h-full border-r border-border transition-all",
180-
viewMode === "split" ? "w-1/2" : "w-full"
181-
)}>
182-
<MarkdownEditor
183-
value={activeFile.content}
184-
onChange={handleContentChange}
185-
/>
186-
</div>
187-
)}
188-
189-
{(viewMode === "split" || viewMode === "preview") && (
190-
<div className={cn(
191-
"h-full bg-background transition-all",
192-
viewMode === "split" ? "w-1/2" : "w-full"
193-
)}>
194-
<MarkdownPreview content={activeFile.content} />
195-
</div>
196-
)}
197-
</>
198-
) : (
199-
<div className="flex-1 flex items-center justify-center text-muted-foreground">
200-
{activeFile && activeFile.type === 'folder'
201-
? "Select a file to edit"
202-
: "Select or create a file to start editing"}
203-
</div>
204-
)}
205-
</div>
206-
</main>
207-
</div>
208-
</div>
209-
);
4+
export default function Page() {
5+
const posts = getStaticPosts();
6+
return <MathStudio initialPosts={posts} />;
2107
}

0 commit comments

Comments
 (0)