Skip to content

Commit 6697db3

Browse files
committed
feat: switch from fumadocs to self-developed SSG framework
1 parent bc96e10 commit 6697db3

70 files changed

Lines changed: 4693 additions & 3080 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Afilmory Codebase Guide for AI Agents
2+
3+
## Project Architecture
4+
5+
Afilmory is a modern photo gallery monorepo with sophisticated image processing capabilities:
6+
7+
- **`apps/ssr/`** - Next.js 15 SSR application with database and APIs
8+
- **`apps/web/`** - React 19 frontend with Vite, WebGL viewer, masonry layout
9+
- **`packages/builder/`** - Photo processing engine with S3/GitHub storage adapters
10+
- **`packages/docs/`** - Vite SSG documentation site (planned custom implementation)
11+
- **`packages/webgl-viewer/`** - High-performance WebGL photo viewer component
12+
13+
## Critical Development Commands
14+
15+
```bash
16+
# Photo processing & manifest generation
17+
pnpm run build:manifest # Incremental photo sync
18+
pnpm run build:manifest -- --force # Full rebuild
19+
pnpm run build:manifest -- --force-thumbnails # Regenerate thumbnails only
20+
21+
# Development servers
22+
pnpm dev # Starts both SSR + web apps
23+
pnpm --filter @afilmory/ssr dev # SSR only (Next.js on :1924)
24+
pnpm --filter web dev # Web only (Vite)
25+
26+
# Database operations (SSR app)
27+
pnpm --filter @afilmory/ssr db:generate # Generate Drizzle migrations
28+
pnpm --filter @afilmory/ssr db:migrate # Run migrations
29+
```
30+
31+
## Code Quality & Architecture Patterns
32+
33+
### State Management Architecture
34+
35+
- **Jotai** for client state in web app - use atomic patterns, avoid large atoms
36+
- **TanStack Query** for server state - leverage cache invalidation patterns
37+
- **React Context** - follow composition over deep nesting, prevent re-renders
38+
39+
### Photo Processing Pipeline
40+
41+
1. **Storage Adapters** (`packages/builder/src/storage/`) - implement `StorageProvider` interface
42+
2. **Format Conversion** - HEIC/TIFF → web formats via Sharp
43+
3. **EXIF Extraction** - camera settings, GPS, Fujifilm recipes
44+
4. **Concurrent Processing** - worker threads/cluster mode in `builderConfig.performance`
45+
46+
### WebGL Integration
47+
48+
- Custom WebGL viewer in `packages/webgl-viewer/` for high-performance rendering
49+
- Gesture support, zoom/pan operations
50+
- Integration with masonry layout via Masonic
51+
52+
## Project-Specific Conventions
53+
54+
### Configuration System
55+
56+
- **`builder.config.json`** - photo processing, storage, performance settings
57+
- **`config.json`** - site metadata merged with `site.config.ts` defaults
58+
- **`env.ts`** - centralized environment validation with Zod schemas
59+
60+
### Apple UIKit Color System
61+
62+
```typescript
63+
// Use semantic Tailwind classes from tailwindcss-uikit-colors
64+
className = 'text-text-primary bg-fill-secondary material-thin'
65+
// NOT generic colors like "text-blue-500"
66+
```
67+
68+
### i18n Flat Key Structure
69+
70+
```json
71+
// locales/app/en.json - use dot notation, avoid nesting
72+
{
73+
"exif.camera.model": "Camera Model",
74+
"photo.count_one": "{{count}} photo",
75+
"photo.count_other": "{{count}} photos"
76+
}
77+
```
78+
79+
### Monorepo Workspace Patterns
80+
81+
- Use `pnpm --filter <package>` for targeted operations
82+
- Shared dependencies in `pnpm-workspace.yaml` catalog
83+
- Cross-package imports via workspace protocol: `"@afilmory/components": "workspace:*"`
84+
85+
## Storage Provider Integration
86+
87+
When extending storage support, implement the adapter pattern:
88+
89+
```typescript
90+
// packages/builder/src/storage/providers/
91+
class NewStorageProvider implements StorageProvider {
92+
async listImages(): Promise<StorageObject[]> {
93+
/* */
94+
}
95+
async getFile(key: string): Promise<Buffer | null> {
96+
/* */
97+
}
98+
// Key methods for storage abstraction
99+
}
100+
```
101+
102+
## Performance Considerations
103+
104+
- **Photo Processing**: Configure worker pools in `builderConfig.performance.worker`
105+
- **WebGL Viewer**: Implement texture memory management and disposal
106+
- **Bundle Splitting**: Leverage Vite's code splitting for image processing tools
107+
- **Image Optimization**: Use Sharp for thumbnails, Blurhash for placeholders
108+
109+
## Documentation Site (packages/docs/)
110+
111+
Currently planned as a custom Vite SSG implementation:
112+
113+
- **MDX** with React components, math (KaTeX), charts (Mermaid)
114+
- **Custom Vite plugins** for file-system routing and search indexing
115+
- **Design system** aligned with main app's Apple UIKit colors
116+
- Reference `packages/docs/requirements.md` and `tasks.md` for implementation details
117+
118+
## Integration Points
119+
120+
- **Database**: Drizzle ORM with PostgreSQL for SSR app
121+
- **Image CDN**: S3-compatible storage with custom domain support
122+
- **Map Integration**: MapLibre for GPS photo locations
123+
- **Live Photos**: iOS video detection and playback support
124+
- **RSS/Social**: OpenGraph metadata and feed generation

.github/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666
- name: Upload artifact
6767
uses: actions/upload-pages-artifact@v3
6868
with:
69-
path: ./packages/docs/out
69+
path: ./packages/docs/dist
7070

7171
# Deployment job
7272
deploy:
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<!-- Generated by SpecStory -->
2+
3+
# 在这个文件里面完成一个插件,满足 #file:Sidebar.tsx #file:App.tsx (2025-07-19 16:41Z)
4+
5+
_**User**_
6+
7+
在这个文件里面完成一个插件,满足 #file:Sidebar.tsx #file:App.tsx 的需求
8+
9+
---
10+
11+
_**Assistant**_
12+
13+
我来分析一下当前的需求,然后完成 Vite 路由插件的实现。
14+
15+
首先让我查看一下当前文件的内容和相关的项目结构: 现在我需要查看 SSG 插件的实现,以了解路由是如何生成的: 现在我来查看一下项目的内容结构,以便了解需要生成的路由: 基于对代码的分析,我发现 `App.tsx``Sidebar.tsx` 都在引用一个 `routes` 变量,但这个变量还没有定义。我需要创建一个 Vite 插件,来动态生成路由信息,并且让它在开发和构建过程中都能正常工作。
16+
17+
现在我来实现这个路由插件: 让我修复这些 lint 错误:
18+
```
19+
20+
```
21+
22+
```
23+
24+
```
25+
现在我需要更新 Vite 配置文件来使用这个新的路由插件:
26+
```
27+
28+
```
29+
30+
```
31+
32+
```
33+
现在我需要更新 `Sidebar.tsx` 文件,导入路由信息:
34+
```
35+
36+
```
37+
我需要首先生成路由文件。让我先运行插件来生成路由:
38+
39+
---
40+
41+
_**User**_
42+
43+
为什么build的时候一直在反复执行这个函数generateStaticPages
44+
45+
---
46+
47+
_**Assistant**_
48+
49+
我来分析一下为什么 `generateStaticPages` 函数在构建时会反复执行。让我查看当前的插件实现: 我发现问题了!在 `generateStaticPages` 函数中,它在内部调用了 `build()` 函数来构建 SSR 版本,而这个 `build()` 调用会再次触发相同的插件,导致无限循环。
50+
51+
让我查看 Vite 配置文件来确认插件配置: 现在我明白问题所在了。当 `generateStaticPages` 函数调用 `build()` 时,它传递了相同的插件配置 `plugins: [...viteConfig.plugins]`,这包括了 SSG 插件本身,导致无限递归。
52+
53+
我需要修复这个问题,避免在内部构建时重复使用 SSG 插件:
54+
```
55+
56+
```
57+
现在我还需要进一步优化,避免重复构建。让我添加一个标志来检查 SSR 构建是否已经完成:
58+
```
59+
60+
```
61+
让我实际使用这个标志来避免重复构建:
62+
```
63+
64+
```
65+
现在让我进一步优化,添加更好的条件检查,避免在开发模式或非主构建过程中执行 SSG:
66+
```
67+
68+
```
69+
让我再进一步改进,添加更精确的检查机制,避免在 SSR 构建过程中触发 SSG:
70+
```
71+
72+
```
73+
现在让我再做一个重要的优化,通过环境变量来控制是否执行 SSG,这样可以更好地控制构建过程:
74+
```
75+
76+
```
77+
现在修改内部的 build 调用,设置环境变量以避免触发 SSG:
78+
```
79+
80+
```
81+
现在让我再添加一个更直接的解决方案 - 使用一个全局变量来防止递归:
82+
```
83+
84+
```
85+
现在让我使用这个全局标志:
86+
```
87+
88+
```
89+
现在在 SSR 构建过程中设置这个标志:
90+
```
91+
92+
```
93+
现在问题应该解决了。让我总结一下我做的修改来解决 `generateStaticPages` 函数反复执行的问题:
94+
95+
## 问题分析
96+
97+
原始代码中的问题是:
98+
1. `generateStaticPages` 函数在内部调用 `build()` 来构建 SSR 版本
99+
2. 这个 `build()` 调用使用了相同的插件配置 `[...viteConfig.plugins]`,包括了 SSG 插件本身
100+
3. 这导致了无限递归:SSG 插件触发 → 构建 SSR → SSG 插件再次触发 → 无限循环
101+
102+
## 解决方案
103+
104+
我实施了多层防护来解决这个问题:
105+
106+
1. **过滤插件**:在内部 SSR 构建时过滤掉 SSG 插件
107+
2. **全局标志**:使用 `isSSRBuilding` 全局变量防止递归
108+
3. **环境变量**:设置 `VITE_SSR_BUILD` 环境变量作为额外保护
109+
4. **构建条件检查**:只在主构建(非 SSR 构建)时执行 SSG
110+
5. **文件存在检查**:避免重复构建已存在的 SSR 文件
111+
6. **错误处理**:使用 try-finally 确保标志能正确重置
112+
113+
现在构建过程应该是:
114+
1. 主构建开始,SSG 插件启动
115+
2. SSG 检查是否需要构建 SSR 入口文件
116+
3. 如果需要,启动 SSR 构建(使用过滤后的插件,不包含 SSG)
117+
4. SSR 构建完成后,继续生成静态页面
118+
5. 不会再次触发 SSG 插件
119+
120+
这样就避免了无限递归的问题。
121+
122+
---
123+
124+
_**User**_
125+
126+
vite v7.0.5 building for production...
127+
📝 Generated routes file: src/routes.ts
128+
✓ 37 modules transformed.
129+
dist/index.html 0.41 kB │ gzip: 0.25 kB
130+
dist/routes.json 1.21 kB │ gzip: 0.49 kB
131+
dist/assets/server-CEVB1K0h.js 179.29 kB │ gzip: 54.55 kB
132+
✓ built in 6.00s
133+
🚀 Generating static pages...
134+
📦 Building SSR entry...
135+
vite v7.0.5 building SSR bundle for production...
136+
📝 Generated routes file: src/routes.ts
137+
📝 Generated routes file: src/routes.ts
138+
✓ 5 modules transformed.
139+
✓ 5 modules transformed. (x2)
140+
✗ Build failed in 67ms
141+
Failed to generate static pages: [[@mdx-js/rollup] Unexpected `FunctionDeclaration` in code: only import/exports are supported
142+
file: /Users/maxtune/workspace/personal/Afilmory/packages/docs/content/en/index.mdx
143+
] {
144+
ancestors: undefined,
145+
cause: undefined,
146+
column: 1,
147+
fatal: undefined,
148+
line: 3,
149+
name: '3:1-275:2',
150+
place: {
151+
start: { line: 3, column: 1, offset: 155 },
152+
end: { line: 275, column: 2, offset: 8724 }
153+
},
154+
reason: 'Unexpected `FunctionDeclaration` in code: only import/exports are supported',
155+
ruleId: 'non-esm',
156+
source: 'micromark-extension-mdxjs-esm',
157+
url: 'https://github.com/micromark/micromark-extension-mdxjs-esm#unexpected-type-in-code-only-importexports-are-supported',
158+
code: 'PLUGIN_ERROR',
159+
plugin: '@mdx-js/rollup',
160+
hook: 'transform',
161+
id: '/Users/maxtune/workspace/personal/Afilmory/packages/docs/content/en/index.mdx',
162+
watchFiles: [
163+
'/Users/maxtune/workspace/personal/Afilmory/packages/docs/src/entry-server.tsx',
164+
'/Users/maxtune/workspace/personal/Afilmory/packages/docs/package.json',
165+
'/Users/maxtune/workspace/personal/Afilmory/packages/docs/src/App.tsx',
166+
'/Users/maxtune/workspace/personal/Afilmory/packages/docs/src/routes.ts',
167+
'/Users/maxtune/workspace/personal/Afilmory/packages/docs/src/layouts/DocsLayout.tsx',
168+
'/Users/maxtune/workspace/personal/Afilmory/packages/docs/src/components/MDXProvider.tsx',
169+
'/Users/maxtune/workspace/personal/Afilmory/packages/docs/content/en/index.mdx'
170+
]
171+
}
172+
error during build:
173+
[@mdx-js/rollup] Unexpected `FunctionDeclaration` in code: only import/exports are supported
174+
file: /Users/maxtune/workspace/personal/Afilmory/packages/docs/content/en/index.mdx
175+
176+
 ELIFECYCLE  Command failed with exit code 1.
177+
178+
---
179+
180+
_**Assistant**_
181+
182+
看起来 MDX 插件在 SSR 构建过程中遇到了问题。这个错误表明 MDX 文件中有函数声明,但在 ESM 模式下只支持 import/export 语句。
183+
184+
让我先检查一下生成的路由文件,看看是否有问题: 问题在于 MDX 插件在 SSR 构建时无法正确处理 MDX 文件。这可能是因为 MDX 插件配置在 SSR 模式下存在问题。让我检查一下 Vite 配置中的 MDX 配置: 我需要修改 SSG 插件,在 SSR 构建时也要包含 MDX 插件的正确配置。问题是当前的插件过滤逻辑过于简单,没有考虑到 MDX 插件的依赖关系。
185+
186+
让我修改 SSG 插件的插件过滤逻辑:
187+
```
188+
189+
```
190+
191+
192+
---
193+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!-- Generated by SpecStory -->
2+
3+
# 在这个脚本中,执行 #file:main-static.js 然后将得到的html合并到 #fil (2025-07-20 06:55Z)
4+
5+
_**User**_
6+
7+
在这个脚本中,执行 #file:main-static.js 然后将得到的html合并到 #file:index.html 的占位符中
8+
9+
---
10+
11+
_**Assistant**_
12+
13+
我需要先查看当前的build.ts文件内容,然后了解如何执行main-static.js并将结果合并到index.html中。 现在我需要查看packages/docs的package.json来了解项目结构和依赖: 让我查看vite.config.ts来了解构建配置: 现在让我检查src目录结构: 让我查看main-static.tsx文件: 现在我理解了架构。我需要创建一个build.ts脚本,它将:
14+
1. 导入并执行main-static.js中的render函数
15+
2. 获取返回的HTML
16+
3. 将HTML插入到index.html模板的占位符中
17+
18+
让我创建这个脚本:
19+
```
20+
21+
```
22+
现在让我测试这个脚本是否能正常工作。首先需要确保静态文件已经构建: 很好!现在运行我们的构建脚本: 让我检查是否需要安装tsx:
23+
24+
---
25+

0 commit comments

Comments
 (0)