chore: 更新项目文档和工具状态

- 更新 AGENTS.md 项目知识库文档(多目录)
- 添加 docs/analyst.md 产品分析文档
- 添加 .omc/ 工具状态和会话记录
- 添加 .sisyphus/ 计划文档
- 添加 project-structure.txt 项目结构
- 删除 timeline 测试截图
This commit is contained in:
2026-02-20 21:48:05 +08:00
parent eb797450b5
commit 6cb4545a17
28 changed files with 1379 additions and 5 deletions
+67
View File
@@ -0,0 +1,67 @@
# .claude/ - Custom Claude Configuration
<!-- Parent: ../AGENTS.md -->
## OVERVIEW
Custom Claude Code agents, commands, and settings for the agent_park project.
## STRUCTURE
```
.claude/
├── settings.json # Claude Code project settings
├── agents/ # Custom agent definitions
│ ├── content-explorer-agent.md
│ └── api-submitter-agent.md
└── commands/ # Custom slash commands
└── discover-projects.md
```
## CUSTOM AGENTS
### content-explorer-agent
- **Purpose**: Explore GitHub projects and generate structured data
- **Capabilities**: Browser automation, content extraction, JSON generation
- **Output**: ProjectInputSchema-compliant JSON
### api-submitter-agent
- **Purpose**: Submit exploration results to production API
- **Capabilities**: Batch task updates, API submission, retry logic
- **Requires**: `WEBHOOK_API_KEY` environment variable
## CUSTOM COMMANDS
### /discover-projects
Automated project discovery workflow:
```bash
/discover-projects [count] [--batch=N]
```
- Default: 10 tasks, batch size 3
- Coordinates content-explorer + api-submitter agents
## FOR AI AGENTS
### When Adding Agents/Commands
1. Create `.md` file with agent/command definition
2. Follow existing templates (frontmatter + instructions)
3. Test with `/command-name` before committing
### Agent Template Structure
```markdown
---
name: agent-name
description: What this agent does
tools: [Read, Grep, Glob, Bash] # Allowed tools
---
# Role
[Agent role description]
# Instructions
[Detailed instructions]
```
<!-- MANUAL: Additional notes can be added below -->
+320
View File
@@ -0,0 +1,320 @@
{
"version": "1.0.0",
"lastScanned": 1771588891228,
"projectRoot": "/Users/caihaohan/Code/agent_park",
"techStack": {
"languages": [
{
"name": "JavaScript/TypeScript",
"version": null,
"confidence": "high",
"markers": [
"package.json"
]
},
{
"name": "TypeScript",
"version": null,
"confidence": "high",
"markers": [
"tsconfig.json"
]
}
],
"frameworks": [
{
"name": "next",
"version": "15.1.11",
"category": "fullstack"
},
{
"name": "react",
"version": "19.0.0",
"category": "frontend"
},
{
"name": "react-dom",
"version": "19.0.0",
"category": "frontend"
},
{
"name": "@playwright/test",
"version": "1.49.1",
"category": "testing"
},
{
"name": "vitest",
"version": "2.1.8",
"category": "testing"
}
],
"packageManager": "pnpm",
"runtime": null
},
"build": {
"buildCommand": "pnpm build",
"testCommand": "pnpm test",
"lintCommand": "pnpm lint",
"devCommand": "pnpm dev",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "vitest",
"test:e2e": "playwright test"
}
},
"conventions": {
"namingStyle": null,
"importStyle": null,
"testPattern": null,
"fileOrganization": "type-based"
},
"structure": {
"isMonorepo": false,
"workspaces": [],
"mainDirectories": [
"docs",
"scripts",
"src",
"tests"
],
"gitBranches": {
"defaultBranch": "main",
"branchingStrategy": null
}
},
"customNotes": [],
"directoryMap": {
"design": {
"path": "design",
"purpose": null,
"fileCount": 3,
"lastAccessed": 1771588891214,
"keyFiles": [
"detail.html",
"homepage.html",
"projectlist.html"
]
},
"docs": {
"path": "docs",
"purpose": "Documentation",
"fileCount": 7,
"lastAccessed": 1771588891218,
"keyFiles": [
"analyst.md",
"api-reference.md",
"api-testing-guide.md",
"discovery-workflow.md",
"e2e-testing-guide.md"
]
},
"n8n-workflows": {
"path": "n8n-workflows",
"purpose": null,
"fileCount": 2,
"lastAccessed": 1771588891218,
"keyFiles": [
"README.md",
"keyword-cloud-workflow.json"
]
},
"prisma": {
"path": "prisma",
"purpose": null,
"fileCount": 2,
"lastAccessed": 1771588891219,
"keyFiles": [
"schema.prisma",
"seed.ts"
]
},
"scripts": {
"path": "scripts",
"purpose": "Build/utility scripts",
"fileCount": 4,
"lastAccessed": 1771588891219,
"keyFiles": [
"list-tags.js",
"seed-historical-events.ts",
"seed-keyword-cloud.ts",
"test-keyword-api.ts"
]
},
"src": {
"path": "src",
"purpose": "Source code",
"fileCount": 1,
"lastAccessed": 1771588891219,
"keyFiles": [
"middleware.ts"
]
},
"tests": {
"path": "tests",
"purpose": "Test files",
"fileCount": 0,
"lastAccessed": 1771588891219,
"keyFiles": []
},
"prisma/migrations": {
"path": "prisma/migrations",
"purpose": "Database migrations",
"fileCount": 1,
"lastAccessed": 1771588891220,
"keyFiles": [
"migration_lock.toml"
]
},
"src/app": {
"path": "src/app",
"purpose": "Application code",
"fileCount": 3,
"lastAccessed": 1771588891220,
"keyFiles": [
"AGENTS.md",
"globals.css",
"layout.tsx"
]
},
"src/components": {
"path": "src/components",
"purpose": "UI components",
"fileCount": 1,
"lastAccessed": 1771588891220,
"keyFiles": [
"AGENTS.md"
]
},
"src/lib": {
"path": "src/lib",
"purpose": "Library code",
"fileCount": 6,
"lastAccessed": 1771588891220,
"keyFiles": [
"AGENTS.md",
"prisma.ts",
"slug.ts"
]
}
},
"hotPaths": [
{
"path": "AGENTS.md",
"accessCount": 6,
"lastAccessed": 1771593272139,
"type": "file"
},
{
"path": ".opencode/mcp.json",
"accessCount": 4,
"lastAccessed": 1771592799554,
"type": "file"
},
{
"path": "src/app/AGENTS.md",
"accessCount": 3,
"lastAccessed": 1771593311370,
"type": "file"
},
{
"path": "src/hooks/AGENTS.md",
"accessCount": 3,
"lastAccessed": 1771593529726,
"type": "file"
},
{
"path": "src/lib/AGENTS.md",
"accessCount": 3,
"lastAccessed": 1771593529730,
"type": "file"
},
{
"path": "src/components/AGENTS.md",
"accessCount": 2,
"lastAccessed": 1771593512972,
"type": "file"
},
{
"path": "prisma/schema.prisma",
"accessCount": 2,
"lastAccessed": 1771594876904,
"type": "file"
},
{
"path": ".env",
"accessCount": 1,
"lastAccessed": 1771589498743,
"type": "file"
},
{
"path": "package.json",
"accessCount": 1,
"lastAccessed": 1771593111981,
"type": "file"
},
{
"path": "CLAUDE.md",
"accessCount": 1,
"lastAccessed": 1771593111989,
"type": "file"
},
{
"path": "prisma/seed.ts",
"accessCount": 1,
"lastAccessed": 1771593360708,
"type": "file"
},
{
"path": "prisma/AGENTS.md",
"accessCount": 1,
"lastAccessed": 1771593361809,
"type": "file"
},
{
"path": "docs/analyst.md",
"accessCount": 1,
"lastAccessed": 1771594824291,
"type": "file"
},
{
"path": "docs/plans/2025-01-25-ai-timeline-feature-design.md",
"accessCount": 1,
"lastAccessed": 1771594834076,
"type": "file"
},
{
"path": "docs/plans/2026-01-25-keyword-cloud-system-design.md",
"accessCount": 1,
"lastAccessed": 1771594834081,
"type": "file"
},
{
"path": "docs/plans/2025-01-25-ai-timeline-implementation.md",
"accessCount": 1,
"lastAccessed": 1771594834472,
"type": "file"
},
{
"path": "docs/plans/2026-01-25-keyword-cloud-implementation.md",
"accessCount": 1,
"lastAccessed": 1771594834666,
"type": "file"
},
{
"path": "src/app/[locale]/timeline/page.tsx",
"accessCount": 1,
"lastAccessed": 1771594876522,
"type": "file"
},
{
"path": "src/app/[locale]/keyword-cloud/components/KeywordCloud.tsx",
"accessCount": 1,
"lastAccessed": 1771594892847,
"type": "file"
}
],
"userDirectives": []
}
@@ -0,0 +1 @@
{"t":0,"agent":"a37a7c5","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1 @@
{"t":0,"agent":"a8591e0","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1,2 @@
{"t":0,"agent":"a567d02","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a6f63da","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1,7 @@
{"t":0,"agent":"aa3c21d","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a13b2eb","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a791a1b","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a70151c","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a488f88","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a588376","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a0e7cd4","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1,4 @@
{"t":0,"agent":"a04e80e","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a9b9f24","agent_type":"explore","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a9b9f24","agent_type":"explore","event":"agent_stop","success":true,"duration_ms":60625}
{"t":0,"agent":"a895674","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1 @@
{"t":0,"agent":"af530c0","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1,4 @@
{"t":0,"agent":"aaa41bb","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"system","event":"skill_invoked","skill_name":"find-skills"}
{"t":0,"agent":"aa7d07a","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a604ba3","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1,2 @@
{"t":0,"agent":"system","event":"skill_invoked","skill_name":"oh-my-claudecode:hud"}
{"t":0,"agent":"afab084","agent_type":"unknown","event":"agent_stop","success":true}
+6
View File
@@ -0,0 +1,6 @@
{
"timestamp": "2026-02-20T13:40:21.901Z",
"backgroundTasks": [],
"sessionStartTimestamp": "2026-02-20T12:55:14.824Z",
"sessionId": "f1da58ef16330f2a"
}
+17
View File
@@ -0,0 +1,17 @@
{
"agents": [
{
"agent_id": "a9b9f24",
"agent_type": "oh-my-claudecode:explore",
"started_at": "2026-02-20T13:10:01.740Z",
"parent_mode": "none",
"status": "completed",
"completed_at": "2026-02-20T13:11:02.365Z",
"duration_ms": 60625
}
],
"total_spawned": 1,
"total_completed": 1,
"total_failed": 0,
"last_updated": "2026-02-20T13:47:26.026Z"
}
+281
View File
@@ -0,0 +1,281 @@
# Tag Janitor: n8n 每日 AI 标签合并
## TL;DR
目标:解决 AI 生成导致的“标签爆炸”,不改现有入库逻辑(允许自由生成 tags),通过 **n8n 每日任务**调用站点 API 自动做 **标签语义合并 + nameEn 补全**,并保证合并后项目仍正确绑定到新标签。
交付物:
- 新增 tags 维护 APIlist + maintenance/merge
- n8n workflow(每日运行):拉取 tags → AI 产出合并计划/英文补全 → 调用 API 执行 → 输出日报
- Vitest 自动化测试(route + 核心 merge 逻辑)
---
## Context
### 现状(已验证)
- Tag 数据模型:`prisma/schema.prisma``Tag` + `ProjectTag`
- `Tag.name` 全局唯一,`Tag.slug` 全局唯一,`Tag.nameEn` 可空,`Tag.createdAt` 存在
- `ProjectTag` 复合主键 `@@id([projectId, tagId])`(同一项目不能重复绑定同一 tag
- Tag 入库:
- Webhook`src/app/api/webhook/projects/route.ts`(批量 upsert tags;使用 `generateSlug()`;存在 slug 冲突兜底)
- Discovery`src/app/api/discovery/lib/discovery-service.ts``upsertTags()` 与 webhook 类似)
- UI 过滤:
- 项目列表按 `tag slug` 过滤:`src/hooks/useProjects.ts``where.tags.some.tag.slug = tag`
- TagCloud 搜索大小写不敏感,但展示/过滤依赖 slug:`src/components/project/TagCloud.tsx`
- Slug 生成:`src/lib/slug.ts`(优先 nameEn,否则 namelowercase;保留中文;空格→-;最长 100)
### 约束与偏好(用户确认)
- 不做复杂治理:无审核 UI、无回滚系统、无 alias/redirect 层、无 merge memory。
- 合并后旧标签直接删除。
- 新项目入库仍可自由生成 tags(不做入库侧规范化/映射),一切靠每日合并流程兜底。
- 标签双语:需要 `name` + `nameEn`(可相同,例如 Python)。
- AI 判定逻辑放在 n8n;站点 API 只负责“安全执行合并”。
- 需要自动化测试(Vitest)。
---
## 关键设计(最小可用)
### 1) API 只做“合并执行器”
站点侧不做语义判断、不接 embedding、不做候选生成;只接收 n8n 给出的“合并计划”,并以 Prisma 事务保证一致性。
必须满足:
- 合并后 `ProjectTag` 绑定迁移完成(不会丢失项目-标签关系)
- 避免因 `@@id([projectId, tagId])` 造成重复冲突(迁移时要去重)
- 执行完删除旧 tagssource tags
- 合并完触发缓存更新(ISR)以尽快反映 tag counts
### 2) n8n 负责 AI:语义聚类 + 合并计划 + nameEn 补全
最简单闭环:
- 每日拉取全量 tags(含 projectCount
- 让 LLM 输出:
- merges[{ target: {name, nameEn}, sourceTagIds: [...] }]
- updates[{ tagId, nameEn }]
- 调用 API 执行 merges/updates
- 输出日报(合并数量、删除 tags 数、nameEn 补全数、失败明细)
---
## 默认策略(无须再问用户)
- 旧 slug 不做重定向:合并后如果用户用旧 `?tag=oldSlug` 访问,页面会显示 0 结果(符合“不要复杂化”的约束)。
- Merge 过程中不强制改动 `slug`
- 对“新建 canonical tag”由 API 按 `generateSlug(name, nameEn)` 生成 slug
- 对“复用已有 tagId 作为 target”不强制改 slug(避免破坏既有链接);只更新 `name/nameEn`(如果 n8n 传了)
- API 鉴权复用 `WEBHOOK_API_KEY`(与 `src/app/api/webhook/projects/route.ts` 同一密钥),用 `crypto.timingSafeEqual()`
---
## Execution Strategy (Waves)
Wave 1 (Design + Audit)
- Task 1: 明确 API contractlist + maintenance+ 错误码
- Task 2: 设计 n8n workflow(节点、prompt、chunking、重试策略)
Wave 2 (Backend Implementation)
- Task 3: 新增 tags list API(给 n8n 拉取 tags + counts
- Task 4: 新增 tags maintenance APImerges + updates;事务;去重;删除旧 tag)
- Task 5: 触发 ISR revalidate(项目列表页)
Wave 3 (Automation + Tests)
- Task 6: 写 Vitest 测试(route + merge 逻辑)
- Task 7: 搭建 n8n workflow 并做一次 dry run + 一次真实合并
Critical Path: Task 1 → Task 4 → Task 6 → Task 7
---
## TODOs
### 1) 定义 API Contractlist + maintenance
**What to do**:
- 设计 2 个 endpoint
- `GET /api/tags`:返回 tags 列表(id/name/nameEn/slug/createdAt/\_count.projects
- `POST /api/tags/maintenance`:鉴权 + 批量执行 `updates``merges`
- 约定响应结构:`{ success: boolean, ... }`,失败时带 `error` + `details`
**Must NOT do**:
- 不引入 alias/redirect
- 不引入 merge history/回滚
**References**:
- `prisma/schema.prisma` - `Tag`/`ProjectTag` 约束(unique/复合主键)
- `src/app/api/webhook/projects/route.ts` - API key 鉴权 + Zod safeParse + 返回结构风格
- `src/lib/validations.ts` - Zod schema 放置位置/风格
**Acceptance Criteria**:
- 产出一个明确的 JSON schema(写进实现用的 Zod schema),覆盖:空 merges、重复 tagId、自合并、无效 tagId、nameEn 缺失。
---
### 2) 设计 n8n 每日 Tag Janitor Workflow
**What to do**:
- 节点建议:Cron → HTTP(GET /api/tags) → Code(预处理/分块) → LLM(生成 merges+updates) → Code(JSON 校验/拆批) → HTTP(POST /api/tags/maintenance) → 汇总通知
- Prompt 输出必须是严格 JSON(避免解析失败)
- 需要考虑 LLM 输入过大时的分块策略(按字母/按 projectCount/按 createdAt
**Must NOT do**:
- 不要求人工审核
**References**:
- `src/hooks/useProjects.ts` - tags 的 `_count.projects` 查询方式(作为 /api/tags 的实现依据)
- `src/components/project/TagCloud.tsx` - UI 依赖 slug(理解合并后旧 slug 失效的表现)
**Acceptance Criteria**:
- n8n workflow 最终只依赖两个站点接口(GET /api/tags, POST /api/tags/maintenance
- LLM 输出 JSON 包含:`merges`(数组)与 `updates`(数组)
---
### 3) 实现 `GET /api/tags`
**What to do**:
- 新增 route`src/app/api/tags/route.ts`(实现 GET
- 用 Prisma 查询 Tag 并 include `_count.projects`
**References**:
- `src/hooks/useProjects.ts` - `getAllTags()` 的 Prisma 查询(可直接复用/抽成共享函数)
**Acceptance Criteria (agent-executable)**:
```bash
curl -s http://localhost:3000/api/tags | jq '.success'
# Assert: true
curl -s http://localhost:3000/api/tags | jq '.tags[0] | has("id") and has("slug") and has("_count")'
# Assert: true
```
---
### 4) 实现 `POST /api/tags/maintenance`updates + merges
**What to do**:
- 新增 route`src/app/api/tags/maintenance/route.ts`(实现 POST
- 鉴权:复用 `WEBHOOK_API_KEY` + `crypto.timingSafeEqual()`(参考 webhook
- Zod 校验请求体
- Prisma 事务执行:
1. 执行 `updates`(主要是 nameEn 补全;允许 name/nameEn 相同)
2. 执行 `merges`
- target tag:若 n8n 给的是 id → 使用;若给的是 name/nameEn → upsert/createslug 用 `generateSlug()`
- 迁移 `ProjectTag`:把 sourceTagIds 关联的 projectId 迁移到 targetTagId(需去重,避免复合主键冲突)
- 删除 source tags`Tag` 记录)
3. 结束后 revalidate 相关路径
**References**:
- `prisma/schema.prisma` - `ProjectTag` 复合主键导致去重需求
- `src/lib/slug.ts` - `generateSlug()`
- `src/app/api/webhook/projects/route.ts` - `timingSafeEqual` 鉴权与错误返回格式
**Acceptance Criteria (agent-executable)**:
```bash
# 1) 未授权
curl -s -X POST http://localhost:3000/api/tags/maintenance \
-H 'Content-Type: application/json' \
-d '{"updates":[],"merges":[]}' \
| jq '.error'
# Assert: "Unauthorized" (or equivalent)
# 2) 授权空操作成功
curl -s -X POST http://localhost:3000/api/tags/maintenance \
-H 'Content-Type: application/json' \
-d '{"apiKey":"'"$WEBHOOK_API_KEY"'","updates":[],"merges":[]}' \
| jq '.success'
# Assert: true
```
---
### 5) ISR/缓存刷新策略
**What to do**:
- 在 maintenance 成功后调用 `revalidatePath()` 刷新 projects 列表页(至少 `/{locale}/projects` 对应的路径)
**References**:
- `src/hooks/AGENTS.md` - 建议在数据更新后使用 `revalidatePath()`
**Acceptance Criteria**:
- 合并后 1 次刷新内(无需等待 5 分钟)能看到 tags count 变化(用 Playwright 或 curl + 页面渲染断言)。
---
### 6) Vitest 测试(route + merge 逻辑)
**What to do**:
- 新增测试文件(参考风格):`src/app/api/events/route.test.ts`
- 至少覆盖:
- 未授权 401
- body schema 校验失败 400
- 合并执行时:
- 迁移 projectTag 去重逻辑不会抛错
- source tag 会被删除
- target tag 的 nameEn 会被补全/更新
**References**:
- `src/app/api/events/route.test.ts` - NextRequest 调用 route handler 的测试方式
**Acceptance Criteria (agent-executable)**:
```bash
pnpm test
# Assert: exit code 0
```
---
### 7) n8n Workflow 落地与一次端到端验证
**What to do**:
- 在 n8n 中配置:
- 站点 baseUrl
- `WEBHOOK_API_KEY`
- LLM 凭证
- 先 dry-run(只输出 merges/updates,不调用 maintenance
- 再 real-run(调用 maintenance
**Acceptance Criteria (agent-executable)**:
- 运行一次后:
- `/api/tags` 返回的 tag 总数下降(或至少不增长)
- 采样 1-2 个被合并的 source tag,其 slug 再用于 `/{locale}/projects?tag=` 时返回 0 项(可接受)
- 被合并的项目在新 canonical tag slug 下能被筛出来(`src/hooks/useProjects.ts` 的 tag filter 生效)
---
## Notes / Gotchas
- `Tag.name``Tag.slug` 均为唯一:当 AI 生成 canonical 名称时,可能撞车;API 需要在创建/更新时处理并返回可读错误(让 n8n 重试或改名)。
- 合并迁移时必须去重:`ProjectTag``@@id([projectId, tagId])` 会在“同一项目已拥有 target tag”时导致冲突。
- 如果未来 tag 规模增大,n8n 端需要升级候选生成(先 lexical/embedding 粗筛,再让 LLM 决策)。本计划先按“规模尚可”实现全量日更。
+40 -2
View File
@@ -1,12 +1,12 @@
# PROJECT KNOWLEDGE BASE
**Generated:** 2026-02-01
**Generated:** 2026-02-01 | **Updated:** 2026-02-20
**Commit:** N/A (new generation)
**Branch:** main
## OVERVIEW
Next.js 15 multilingual AI project navigation website with App Router, TypeScript, Prisma ORM, and Tailwind CSS neo-brutalism design. Features automated project discovery and quarterly AI keyword cloud system.
Next.js 15 multilingual AI project navigation website with App Router, TypeScript, Prisma ORM, and Tailwind CSS neo-brutalism design. Features automated project discovery, quarterly AI keyword cloud system, and AI Timeline historical events.
## STRUCTURE
@@ -256,3 +256,41 @@ pnpm prisma studio # Database inspector
- **src/components/**: Neo-brutalism design system, component organization (see `src/components/AGENTS.md`)
- **src/lib/**: Validation layer, utilities, GitHub integration (see `src/lib/AGENTS.md`)
- **src/hooks/**: Server-side data fetching, query optimization (see `src/hooks/AGENTS.md`)
## N8N PRODUCTION WORKFLOWS
Production workflows tagged with "生产" in n8n instance:
| Workflow | Status | Nodes | Purpose |
|----------|--------|-------|---------|
| 项目描述向量化 | ✅ Active | 11 | Generate embeddings for project descriptions (RAG) |
| Github项目分析入库 | ⏸️ Inactive | 16 | Analyze GitHub repos and ingest into database |
| RAG项目搜索 | ✅ Active | 6 | AI-powered project search using vector similarity |
| 每日Github Trending项目计划新增 | ✅ Active | 17 | Daily trending repos → discovery tasks |
| Topic项目计划新增 | ⏸️ Inactive | 16 | Topic-based project discovery tasks |
### Workflow Data Flow
```
GitHub Trending/Topic → Discovery Tasks (PENDING)
Content Explorer Agent
Project Ingestion API
向量化 Workflow → Embeddings
RAG项目搜索 ← User Query
```
## AI TIMELINE SYSTEM
Historical AI events tracking system:
- **Model**: `AIEvent` in `prisma/schema.prisma`
- **Fields**: title/titleEn, eventDate, description/descriptionEn, imageUrl, sourceUrl
- **API**: `src/app/api/events/route.ts`
- **Frontend**: `src/app/[locale]/timeline/`
- **Index**: `eventDate` (descending) for chronological queries
<!-- MANUAL: Additional notes can be added below -->
+55
View File
@@ -0,0 +1,55 @@
# docs/ - Project Documentation
<!-- Parent: ../AGENTS.md -->
## OVERVIEW
Technical documentation, design plans, and implementation guides for the agent_park project.
## STRUCTURE
```
docs/
├── api-reference.md # API endpoint documentation
├── api-testing-guide.md # API testing instructions
├── discovery-workflow.md # Project discovery system docs
├── e2e-testing-guide.md # Playwright E2E guide
├── verification-report.md # Feature verification reports
├── analyst.md # Product analysis notes
├── n8n/ # n8n workflow documentation
│ ├── historical-workflow-design.md
│ ├── incremental-workflow-design.md
│ └── tag-janitor-workflow.md
└── plans/ # Design & implementation plans
├── 2026-01-25-ai-search-system-design.md
├── 2026-01-25-ai-search-implementation.md
├── 2025-01-25-ai-timeline-feature-design.md
├── 2025-01-25-ai-timeline-implementation.md
├── 2026-01-25-keyword-cloud-system-design.md
└── 2026-01-25-keyword-cloud-implementation.md
```
## KEY DOCUMENTS
| Document | Purpose |
|----------|---------|
| `api-reference.md` | Complete API documentation |
| `discovery-workflow.md` | Project discovery architecture |
| `plans/*.md` | Feature design & implementation specs |
| `n8n/*.md` | n8n workflow design docs |
## FOR AI AGENTS
### When Adding Documentation
1. Place design docs in `plans/` with date prefix
2. Update this AGENTS.md with new file entries
3. Follow existing markdown formatting
### Related Files
- `CLAUDE.md` - Main project context (root)
- `AGENTS.md` - Project knowledge base (root)
- `.claude/` - Custom agents & commands
<!-- MANUAL: Additional notes can be added below -->
+158
View File
@@ -0,0 +1,158 @@
---
## 🎯 现状问题诊断
### 1. 信息架构问题
- **标签体系混乱**:有的项目标了"本地部署/macOS/iOS/Android"4个标签),有的只有"✨",标准不统一
- **缺乏分类维度**:用户无法快速筛选"开源/闭源"、"可商用/个人项目"、"多智能体/单智能体"
- **没有时间维度**:看不出项目是新发布还是经典项目,对 Agent 这个快速迭代的领域很关键
### 2. 用户体验断层
- **卡片信息过载/不足并存**:描述长短不一,但缺少关键决策信息(GitHub stars?是否开源?演示链接?)
- **无快速筛选**:当项目超过20个时,浏览成本会指数级上升
- **缺乏"为什么值得关注"**:单纯罗列不如策展(Curation),需要策展人视角的推荐理由
### 3. 技术呈现问题
- **移动端适配**:从代码结构看是响应式,但卡片在手机上可能过于拥挤
- **无暗色模式**:开发者群体对暗色模式有强需求,实现成本低(CSS media query
- **加载性能**:如果 n8n 工作流生成的是静态 Markdown,建议预渲染为 HTML 提升首屏速度
---
## 💡 轻量级但高价值的功能设计
基于"不做重,但要做巧"的原则,推荐以下功能:
### 阶段一:核心体验完善(2周内)
**1. 极简标签系统(三层维度)**
```
类型标签:🤖 Chatbot | 🔄 Multi-Agent | 🧠 Memory | 👁️ Vision | 🛠️ Tool Use
形态标签:📦 开源 | ☁️ SaaS | 💻 本地部署 | 📱 App
热度标签:🔥 Trending | ⭐ Classic | 🆕 New
```
*实现:纯 CSS 过滤,无需后端,前端 JS 筛选即可*
**2. "30秒决策"信息卡片**
每个项目卡片补充3个关键字段(n8n工作流抓取时补充):
- **GitHub Stars**(如果是开源)
- **体验方式**Live Demo / 下载 / 仅代码
- **适用场景**:一句话场景(如"适合搭建个人知识库")
**3. 每日/每周精选(Newsletter 化)**
不增加功能,而是**内容运营策略**:
- 首页顶部固定"本周编辑推荐"(3个项目+一句话推荐理由)
- 底部增加邮件订阅框(用 Buttondown 或 Revue,零成本)
- 归档页面按周聚合(`/week-04-2025`
### 阶段二:社区感与互动(1个月内)
**4. "使用报告"轻互动**
不同于评论系统(太重),采用**投票+标签**:
- "你在用吗?" 👍 / 👎(匿名)
- "适用场景"多选标签(用户可添加,类似 StackOverflow 的标签系统)
- 数据存储:Airtable 或 Notion API(轻量级数据库)
**5. Agent 项目 Twitter 趋势墙**
无需自己生成内容,聚合展示:
- 嵌入 Twitter List(创建一个"Agent Builders"列表)
- 或展示特定 hashtag(如 `#AIAgent`)的最新热门推文
- 实现:Twitter 嵌入式时间线,零维护成本
**6. 极简提交表单优化**
当前"立即提交"大概率跳转到表单,优化为:
- 预填项目模板(GitHub URL 自动抓取信息)
- 支持提交者写"推荐语"(策展人视角)
- 审核流:GitHub Issues 或 Airtable 表单(不用开发后台)
### 阶段三:开发者工具化(2个月内)
**7. Agent 项目 RSS 聚合**
开发者刚需:一站式追踪所有 Agent 项目更新
- 为每个项目生成 RSS 源监控(GitHub releases
- 提供聚合 RSS(用户订阅一个即可看全站更新)
- 技术:RSSHub 或 n8n 自动生成
**8. "Agent 构建模式"分类**
垂直领域细分(这是你的差异化):
- **ReAct 模式**项目集合
- **Plan-and-Execute**项目
- **Multi-Agent 协作框架**
- **Function Calling 工具库**
帮助开发者按技术方案选型,而非只看功能。
---
## 📋 分阶段实施计划( Roadmap )
### Week 1-2:基础优化
- [ ] 统一标签体系(3层9个标签以内)
- [ ] 修改项目卡片模板(增加 Stars/Demo 链接字段)
- [ ] 增加暗色模式(`prefers-color-scheme` 媒体查询)
- [ ] 优化移动端卡片布局(单列+横向滚动标签)
### Week 3-4:内容运营
- [ ] 建立编辑推荐机制(每周手动精选3个)
- [ ] 上线邮件订阅(嵌入 Buttondown
- [ ] 优化提交表单(GitHub URL 自动拉取)
- [ ] 创建 Twitter/X 账号同步发布精选
### Month 2:互动功能
- [ ] 增加轻量级投票系统(用 Upstash Redis 或 Airtable
- [ ] 上线 RSS 订阅功能
- [ ] 增加"技术模式"分类维度
- [ ] 发布首份《Agent Landscape 月报》(PDF 轻量报告)
### Month 3:生态扩展
- [ ] 推出"Agent Builder 访谈"(轻量级文字访谈,每月2期)
- [ ] 建立 Discord/Telegram 群组(社区沉淀)
- [ ] 尝试"项目雷达"功能(预测下周可能火的项目)
---
## 💰 变现可能性分析(从轻到重)
基于"保持轻量"的前提,按可行性排序:
### 1. **策展付费(轻量,推荐优先尝试)**
- **模式**:每周付费 Newsletter($5/月或$50/年),提供更深度的项目分析、代码解读、创始人访谈
- **可行性**:⭐⭐⭐⭐⭐ 你已经用 n8n 做内容聚合,增加深度分析即可,无需改技术架构
- **受众**:Agent 开发者愿意为高质量信息付费,参考 Lenny's Newsletter(产品经理领域)
### 2. **精准职位板(轻量)**
- **模式**"Agent 相关岗位"板块,公司付费发帖($100/月)
- **可行性**:⭐⭐⭐⭐ 当下 Agent 工程师需求旺盛,但供给分散,你的受众正是招聘方想要的
- **优势**:比大型招聘站更精准,比 LinkedIn 更垂直
### 3. **开源项目赞助分成(超轻量)**
- **模式**:项目卡片增加"赞助该项目"按钮,跳转 GitHub Sponsors,你收取 5-10% 导流费或获得 affiliate 返点
- **可行性**:⭐⭐⭐ 需要与项目方谈合作,但初期可以作为增值服务免费提供,建立信任后变现
### 4. **轻量级广告/赞助(需谨慎)**
- **模式**:接受 Agent 框架、云服务(如 LangSmith、Langfuse)的广告位
- **可行性**:⭐⭐⭐ 需要流量基础(月UV 1万+),且要保持克制避免破坏体验
- **建议**:以"赞助商推荐"形式融入内容,而非 banner 广告
### 5. **数据/洞察服务(未来方向)**
- **模式**:出售 Agent 趋势数据报告(GitHub 增长趋势、技术栈迁移方向)
- **可行性**:⭐⭐ 需要积累 6-12 个月数据,且需要品牌背书
- **风险**:会变重,建议保持轻量,只出季度免费报告建立权威性
### ❌ 不建议的变现方式
- **付费墙阻断访问**:违背导航站开放属性
- **复杂的会员系统**:开发与维护成本高
- **交易佣金**:涉及支付、合规,太重
---
## 🚀 立即可做的3个改动(本周)
如果你只想快速优化,先做这三件:
1. **标签标准化**:删除所有重复/冗长标签,只用 类型+形态 二维(如 `🤖Chatbot` `📦开源`
2. **增加"一键体验"按钮**:在卡片上直接放 🔗Demo 或 💻GitHub 图标,减少用户点击成本
3. **顶部增加时间线**:小字标注"本周新增 X 个项目,共收录 Y 个",营造更新感
**核心建议**:Agent Park 现在的定位应该是 **"Agent 爱好者的 Hacker News + Product Hunt 混合体"**,保持策展人(Curator)视角比做全量数据库更有价值。你的 n8n 工作流是护城河,但**人工精选的品味**才是核心竞争力。
需要我针对某个具体功能(比如 n8n 工作流优化方案、暗色模式 CSS、或者邮件订阅文案)展开详细方案吗?
+63
View File
@@ -0,0 +1,63 @@
# n8n-workflows/ - Automation Workflows
<!-- Parent: ../AGENTS.md -->
## OVERVIEW
n8n workflow definitions for automated data collection, project discovery, and keyword cloud generation. Workflows sync with production n8n instance.
## KEY FILES
| File | Purpose |
|------|---------|
| `keyword-cloud-workflow.json` | Google Trends → Keywords API |
| `README.md` | Workflow documentation |
## PRODUCTION WORKFLOWS (n8n Instance)
| Workflow | Status | Nodes | Purpose |
|----------|--------|-------|---------|
| 项目描述向量化 | ✅ Active | 11 | Generate project embeddings for RAG |
| Github项目分析入库 | ⏸️ Inactive | 16 | Analyze & ingest GitHub repos |
| RAG项目搜索 | ✅ Active | 6 | AI-powered vector search |
| 每日Github Trending项目计划新增 | ✅ Active | 17 | Daily trending → discovery tasks |
| Topic项目计划新增 | ⏸️ Inactive | 16 | Topic-based discovery |
## DATA FLOW
```
GitHub Trending API
每日Github Trending项目计划新增
ProjectDiscoveryTask (PENDING)
Content Explorer Agent (Claude)
Project Ingestion API
项目描述向量化 → Embeddings (pgvector)
RAG项目搜索 ← User Query
```
## FOR AI AGENTS
### When Modifying Workflows
1. Export from n8n UI → update JSON file
2. Document changes in README.md
3. Test with n8n-mcp tools before production
### Related API Endpoints
- `POST /api/discovery/tasks` - Create discovery tasks
- `POST /api/keyword-cloud/keywords` - Bulk keyword upload
- `PATCH /api/tags/maintenance` - Tag cleanup (n8n integration)
### Environment Variables (n8n)
- `API_URL` - Production API endpoint
- `API_KEY` - Same as `WEBHOOK_API_KEY`
<!-- MANUAL: Additional notes can be added below -->
+128
View File
@@ -0,0 +1,128 @@
# prisma/ - Database Layer
<!-- Parent: ../AGENTS.md -->
## OVERVIEW
Prisma ORM configuration for PostgreSQL (Neon serverless) with multilingual support, vector embeddings, and automated workflows integration.
## KEY FILES
| File | Purpose |
|------|---------|
| `schema.prisma` | Database models, enums, indexes |
| `seed.ts` | Initial data seeding script |
| `migrations/` | Migration history |
## DATABASE MODELS
### Core Models
| Model | Purpose | Key Fields |
|-------|---------|------------|
| `Project` | AI projects | name/nameEn, slug (unique), description, content, embedding (vector) |
| `Tag` | Project tags | name (unique), slug (unique) |
| `ExternalLink` | Project links | type (WEBSITE/GITHUB/HUGGINGFACE/PAPER), url |
| `ProjectTag` | Junction table | projectId, tagId |
### Keyword Cloud System
| Model | Purpose |
|-------|---------|
| `Quarter` | Quarterly metadata |
| `Keyword` | Trending keywords with visual config |
| `VisualStyleRule` | Color/size/border rules by score range |
| `KeywordCloudErrorLog` | Error tracking for n8n workflows |
### Discovery System
| Model | Purpose |
|-------|---------|
| `ProjectDiscoveryTask` | Exploration task tracking |
### AI Timeline System
| Model | Purpose |
|-------|---------|
| `AIEvent` | Historical AI events with dates |
## ENUMS
- `ProjectStatus`: ACTIVE | ARCHIVED
- `LinkType`: WEBSITE | GITHUB | HUGGINGFACE | PAPER
- `TaskStatus`: PENDING | IN_PROGRESS | COMPLETED | FAILED
## INDEXES
| Index | Model | Purpose |
|-------|-------|---------|
| `idx_project_slug` | Project | Unique slug lookup |
| `idx_project_status_createdAt` | Project | Filter by status, sort by date |
| `idx_project_embedding_cosine` | Project | Vector similarity search |
| `idx_link_type_url` | ExternalLink | URL deduplication |
| `idx_task_status_created` | ProjectDiscoveryTask | Task queue queries |
| `idx_keyword_quarterId` | Keyword | Quarterly keyword lookup |
| `idx_quarter_displayOrder` | Quarter | Quarter ordering |
## MULTILINGUAL PATTERN
Most models follow the dual-language pattern:
```
name: String // Chinese (primary)
nameEn: String? // English (optional)
description: String
descriptionEn: String?
content: String?
contentEn: String?
```
## CASCADE DELETIONS
- `ExternalLink``Project` (onDelete: Cascade)
- `ProjectTag``Project` and `Tag` (onDelete: Cascade)
- `Keyword``Quarter` (onDelete: Cascade)
## FOR AI AGENTS
### Working With This Directory
1. **After schema changes**:
```bash
pnpm prisma migrate dev --name description
pnpm prisma generate
```
2. **Seed database**:
```bash
pnpm prisma db seed
```
3. **Inspect database**:
```bash
pnpm prisma studio
```
### Common Patterns
- **Unique constraints**: Handle with try-catch + fallback (see webhook tag upsert)
- **Multilingual queries**: Always check both `field` and `fieldEn` variants
- **Vector search**: Use `Unsupported("vector")` type with pgvector extension
### Anti-Patterns
- NEVER modify schema without running migrations
- NEVER skip `pnpm prisma generate` after schema changes
- NEVER use raw SQL when Prisma methods are available
- NEVER commit `.env` files with DATABASE_URL
## N8N INTEGRATION
n8n workflows interact with these models:
- **Keywords**: Written by keyword-cloud workflow
- **ProjectDiscoveryTask**: Created by trending/topic workflows
- **Project**: Updated by analysis workflows
- **Tag**: Maintained by tag-janitor workflow
<!-- MANUAL: Additional notes can be added below -->
+148
View File
@@ -0,0 +1,148 @@
# 项目目录结构
## 📁 根目录结构
### 🎯 主要源代码目录
```
src/
├── app/ # Next.js App Router (核心应用)
│ ├── [locale]/ # 国际化路由
│ │ ├── keyword-cloud/ # 词云功能页面
│ │ ├── projects/ # 项目页面
│ │ └── timeline/ # 时间线功能页面
│ └── api/ # API 路由
│ ├── discovery/ # 项目发现 API
│ ├── events/ # 事件 API
│ ├── keyword-cloud/ # 词云 API
│ ├── projects/ # 项目 API
│ ├── search/ # 搜索 API
│ └── webhook/ # Webhook API
├── components/ # React 组件
│ ├── layout/ # 布局组件
│ ├── locale/ # 国际化组件
│ ├── project/ # 项目相关组件
│ ├── search/ # 搜索相关组件
│ └── timeline/ # 时间线相关组件
├── hooks/ # 服务端数据获取函数
├── i18n/ # 国际化配置
├── lib/ # 工具库
│ └── github/ # GitHub 相关工具
└── messages/ # 翻译文件
```
### ⚙️ 配置文件
```
├── next.config.js # Next.js 配置
├── tailwind.config.ts # Tailwind CSS 配置
├── tsconfig.json # TypeScript 配置
├── components.json # Radix UI 组件配置
├── .eslintrc.json # ESLint 配置
└── .prettierrc.json # Prettier 配置
```
### 🗄️ 数据库配置
```
prisma/
├── schema.prisma # 数据库模式
└── migrations/ # 数据库迁移
```
### 🔧 自定义 Agents
```
.claude/
└── agents/ # Claude Code 自定义 Agent
```
### 🔄 n8n 工作流
```
n8n-workflows/
├── keyword-cloud-workflow.json # 词云采集工作流
└── README.md # 工作流文档
docs/n8n/
├── historical-workflow-design.md # 历史工作流设计
├── incremental-workflow-design.md # 增量工作流设计
└── tag-janitor-workflow.json # 标签清理工作流
```
### 📚 文档
```
docs/
├── api-reference.md # API 参考
├── api-testing-guide.md # API 测试指南
├── discovery-workflow.md # 项目发现工作流
├── e2e-testing-guide.md # E2E 测试指南
├── plans/ # 项目规划文档
└── n8n/ # n8n 相关文档
```
### 🎨 设计资源
```
design/
├── hotpot cloud/ # Hotpot 云相关设计
│ └── screen.png
└── timeline/ # 时间线设计
└── screen.png
```
### 📜 数据库种子脚本
```
scripts/
├── list-tags.js # 标签列表脚本
├── seed-historical-events.ts # 历史事件种子数据
├── seed-keyword-cloud.ts # 词云种子数据
└── test-keyword-api.ts # 词云 API 测试
```
### 🧪 测试
```
tests/
└── screenshots/ # 截图测试
```
### 🚀 其他配置
```
opencode/ # OpenCode 配置
└── mcp.json # MCP 服务器配置
.env # 环境变量
.env.example # 环境变量模板
AGENTS.md # Agent 文档
CLAUDE.md # 项目说明文档
package.json # 项目依赖
```
## 🎯 特殊功能目录
### 1. 项目发现系统 (Project Discovery)
- **位置**: `src/app/api/discovery/`
- **功能**: 自动化探索和收录 AI 项目
- **特点**: 双 Agent 协作架构
### 2. 词云系统 (Keyword Cloud)
- **位置**: `src/app/[locale]/keyword-cloud/` 和 `src/app/api/keyword-cloud/`
- **功能**: 季度 AI 热点词云展示
- **特点**: n8n 自动化采集、实时数据展示
### 3. 时间线系统 (Timeline)
- **位置**: `src/app/[locale]/timeline/`
- **功能**: AI 历史事件时间线
- **特点**: 渐进式加载、历史事件展示
### 4. n8n 工作流
- **位置**: `n8n-workflows/` 和 `docs/n8n/`
- **功能**:
- keyword-cloud-workflow.json: Google Trends 数据采集
- tag-janitor-workflow.json: 标签清理自动化
- **特点**: 季度触发、错误处理、API 集成
### 5. 项目管理系统
- **位置**: `src/app/[locale]/projects/` 和 `src/app/api/projects/`
- **功能**: AI 项目展示、搜索、分类
- **特点**: 多语言支持、标签系统、Webhook 集成
### 6. 搜索系统
- **位置**: `src/app/api/search/` 和 `src/components/search/`
- **功能**: AI 项目搜索和过滤
- **特点**: 多条件搜索、分页、状态过滤
```
+47
View File
@@ -0,0 +1,47 @@
# src/ - Source Code
<!-- Parent: ../AGENTS.md -->
## OVERVIEW
Application source code organized by Next.js 15 App Router conventions with internationalization support.
## STRUCTURE
```
src/
├── app/ # Next.js App Router (pages + API)
├── components/ # React components (neo-brutalism design)
├── hooks/ # Server-side data fetching (NOT React hooks)
├── lib/ # Utilities, validation, Prisma client
├── i18n/ # next-intl configuration
├── messages/ # Translation files (zh.json, en.json)
└── middleware.ts # Locale detection & routing
```
## KEY DIRECTORIES
| Directory | Purpose | See |
|-----------|---------|-----|
| `app/` | App Router pages & API routes | `app/AGENTS.md` |
| `components/` | UI components by domain | `components/AGENTS.md` |
| `hooks/` | Server data fetching functions | `hooks/AGENTS.md` |
| `lib/` | Core utilities & validation | `lib/AGENTS.md` |
## FOR AI AGENTS
### When Adding New Features
1. **Pages**: Add to `app/[locale]/` following locale pattern
2. **API**: Add to `app/api/` with Zod validation
3. **Components**: Add to appropriate domain folder
4. **Translations**: Update BOTH `zh.json` AND `en.json`
### Common Patterns
- **Locale routing**: `middleware.ts` handles locale detection
- **Data fetching**: Use functions from `hooks/` in Server Components
- **Validation**: All schemas in `lib/validations.ts`
- **Styling**: Neo-brutalism with Tailwind (see `components/AGENTS.md`)
<!-- MANUAL: Additional notes can be added below -->
+20 -2
View File
@@ -1,16 +1,34 @@
# src/app/ - App Router Implementation
<!-- Parent: ../AGENTS.md -->
## OVERVIEW
Next.js 15 App Router with next-intl locale routing and multilingual project discovery platform
## STRUCTURE
- `[locale]/` - Locale-scoped routes (zh/en), home, projects, keyword-cloud
- `api/` - API endpoints (webhook, discovery, keyword-cloud, search)
- `[locale]/` - Locale-scoped routes (zh/en), home, projects, keyword-cloud, timeline
- `api/` - API endpoints (webhook, discovery, keyword-cloud, search, events, tags)
- `globals.css` - Tailwind + neo-brutalism styles
- `layout.tsx` - Root layout
## KEY FILES
| File | Purpose |
|------|---------|
| `[locale]/page.tsx` | Home page |
| `[locale]/projects/page.tsx` | Project listing |
| `[locale]/projects/[id]/page.tsx` | Project detail (ISR 5min) |
| `[locale]/keyword-cloud/page.tsx` | Quarterly keyword cloud |
| `[locale]/timeline/page.tsx` | AI historical events timeline |
| `api/webhook/projects/route.ts` | Project ingestion webhook |
| `api/discovery/tasks/route.ts` | Discovery task CRUD |
| `api/search/ai/route.ts` | AI-powered RAG search |
| `api/events/route.ts` | AI Timeline events API |
| `api/tags/route.ts` | Tags CRUD |
| `api/tags/maintenance/route.ts` | Tag cleanup (n8n integration) |
## WHERE TO LOOK
- **Locale routing**: `src/middleware.ts`, `src/app/[locale]/layout.tsx`
+3 -1
View File
@@ -1,4 +1,6 @@
# Component System Architecture
# src/components/ - Component System
<!-- Parent: ../AGENTS.md -->
## Neo-Brutalism Design Principles
+2
View File
@@ -1,5 +1,7 @@
# src/hooks/ - Server-Side Data Fetching
<!-- Parent: ../AGENTS.md -->
**Purpose**: Server-side data fetching functions (NOT React hooks). Exported functions for use in Server Components and API routes.
## Architecture
+2
View File
@@ -1,5 +1,7 @@
# src/lib/ - Core Utilities
<!-- Parent: ../AGENTS.md -->
## Overview
Shared utilities and infrastructure layers used across the application.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 951 KiB