feat: 首次推送到Gitea - 完整项目代码 + 安全加固 + 知识库

This commit is contained in:
ZhuiGuangAI Dev
2026-10-02 18:52:49 +08:00
parent b44601bb66
commit 8205ae709c
1185 changed files with 49841 additions and 12802 deletions
+2 -2
View File
@@ -61,7 +61,7 @@ scripts/migrate-*.mjs
scripts/cleanup-non-ai.mjs scripts/cleanup-non-ai.mjs
scripts/delete-*.mjs scripts/delete-*.mjs
scripts/backfill-*.mjs scripts/backfill-*.mjs
scripts/reconcile-*.mjs # 注: reconcile-like-counts.mjs 被 crontab 引用, 必须打包
scripts/organize-*.mjs scripts/organize-*.mjs
scripts/sync-*.mjs scripts/sync-*.mjs
scripts/publish-*.mjs scripts/publish-*.mjs
@@ -69,7 +69,7 @@ scripts/generate-*.mjs
scripts/add-*.mjs scripts/add-*.mjs
scripts/run-*.mjs scripts/run-*.mjs
scripts/version-manager.mjs scripts/version-manager.mjs
scripts/health-check.mjs # 注: health-check.mjs 被 crontab 引用, 必须打包
scripts/db-status.mjs scripts/db-status.mjs
scripts/_check-server.sh scripts/_check-server.sh
+1 -1
View File
@@ -12,4 +12,4 @@ NEXTAUTH_SECRET="zhuiguang-ai-dev-secret"
GITHUB_TOKEN="" GITHUB_TOKEN=""
DEEPSEEK_API_KEY="sk-91d1671855e44089b792b8147e17963f" DEEPSEEK_API_KEY="sk-74c0b4f4eb5647398e35ecd064d1829a"
+12
View File
@@ -18,6 +18,18 @@ GITHUB_TOKEN=""
# DeepSeek AI API Key (必填,用于技能评测和新闻摘要) # DeepSeek AI API Key (必填,用于技能评测和新闻摘要)
DEEPSEEK_API_KEY="" DEEPSEEK_API_KEY=""
# DeepSeek 模型名 (可选,默认 deepseek-v4-pro;旧名 deepseek-chat/reasoner 已停用)
# DEEPSEEK_MODEL="deepseek-v4-pro"
# Redis 缓存连接 (必填,Docker 共享 redis-cache 容器)
# 如不设,默认为本地 127.0.0.1:6379 无密码
# REDIS_HOST="127.0.0.1"
# REDIS_PORT="6379"
# ⚠️ 密码含 # 等特殊字符时必须加引号(否则 dotenv/Next 会把 # 之后当注释截断,导致 WRONGPASS)
# REDIS_PASSWORD=""
# REDIS_CACHE_DB="0" # API 缓存层 (src/lib/cache.ts)
# REDIS_SCRIPT_DB="1" # 脚本缓存层 (scripts/lib/redis-cache.mjs)
# Bot 反馈闭环共享密钥 (可选,设置后 /api/forum/topics/[id]/follow-up 可被脚本调用) # Bot 反馈闭环共享密钥 (可选,设置后 /api/forum/topics/[id]/follow-up 可被脚本调用)
# 不设置则只能用 admin session 调用 # 不设置则只能用 admin session 调用
BOT_FEEDBACK_SECRET="" BOT_FEEDBACK_SECRET=""
+10
View File
@@ -38,3 +38,13 @@ yarn-error.log*
next-env.d.ts next-env.d.ts
/src/generated/prisma /src/generated/prisma
# backups & temp
*.tar.gz
tmp-*
# generated bot avatar JPGs (SVGs are in repo)
public/bot-avatars/lunaris/
# .trae local knowledge cache (not committed)
.trae/knowledge/
+433
View File
@@ -1,5 +1,438 @@
# 追光AI 变更日志 # 追光AI 变更日志
## 2026-06-22/23 项目开发规则全面优化(三轮迭代)
### 目标
基于 Trae IDE 开发规则最佳实践,审查并全面优化 `.trae/rules/project_rules.md`,使之覆盖项目开发全生命周期。
---
### 第一轮:基础增强(8章 → 12章)
- **新增"零、代码质量基础"**: TypeScript类型安全(禁止any/as、泛型约束)、构建检查铁律(tsc+vitetest)、Git工作流、文件组织规范表
- **新增"九、测试规范"**: Vitest框架、80%覆盖率目标、三类测试、禁止skip
- **增强图片优化**: 新增LCP priority规则、LazyImage组件引用、sizes属性
- **增强无障碍**: 从2条→4条(语义化HTML、ARIA/键盘交互、焦点对比度)
- **增强SEO**: 新增Canonical URL/Robots策略
- **增强性能**: 从2条→5条(字体优化、Suspense、Bundle体积控制)
- **增强安全**: 从2条→6条(具体CSP值、XSS消毒方案、Zod验证、认证授权、敏感信息)
- **增强定时任务**: 新增crontab格式示例、错误处理/退出码、任务幂等性
- **新增数据获取**: 缓存失效策略(stale-while-revalidate)、并行查询(Promise.all)
- **新增导航**: 中间件路由保护、prefetch控制
- **全局改进**: 所有规则添加🔴🟡🟢严重级别、18处file://可点击链接
- **新增"十"→"十二"**: 设计系统速查、API约定速查、踩坑记录独立章节
### 第二轮:运维增强(12章 → 15章)
- **新增"十三、Shell脚本规范"**: 脚本结构模板、set -e错误处理、PASS/FAIL/WARN结构化输出、磁盘预检、禁止操作清单
- **新增"十四、Docker容器规范"**: 容器清单(app+cron)、host网络、容器管理命令、健康检查配置
- **新增"十五、部署规范"**: 部署前5项自检、构建流程、部署后六项验证清单、镜像回滚策略
- **新增8.6 Cron Entrypoint规范**: 信号处理(trap+30s优雅关闭)、启动前.env/crontab.txt校验、健康检查端点(8311)、环境变量注入
- **新增8.7 日志规范**: Node.js统一logger.mjs、Shell结构化输出、三层日志目录、禁止写入其他项目日志
- **修复8.3**: 备份.env路径修正为 `$PROJECT/env/.env.production`
### 第三轮:深度补充(15章 → 22章)
- **新增"十六、数据模型与迁移规范"**: Prisma命名约定、连接单例、Schema变更四步流程、严禁DROP/TRUNCATE、数据库隔离
- **新增"十七、API设计规范"**: 路由标准骨架、错误处理表、响应格式、状态码约定、Cache-Control策略、认证三层分层、分页上限
- **新增"十八、AI/Bot系统规范"**: Bot角色定义(112角色)、12张Bot表、活动脚本5大约定、AI API调用隔离、提示词管理
- **新增"十九、环境变量管理规范"**: 8个关键变量分级、.env.example同步规则、脚本侧加载顺序
- **新增"二十、依赖管理规范"**: 添加/移除/版本管理
- **新增"二十一、代码审查清单"**: 12项提交前自检 + Knowledge Graph同步规则
### 文档同步
- **新增模块文档**: `.trae/specs/modules/17-rules.md` — 开发规则模块完整索引
- **modules.md 更新**: 模块表新增第17项、项目结构/关键特性同步更新
- **4个关联模块追加规则引用**: 01-database.md / 02-api.md / 07-lib-and-scripts.md / 09-devops.md
- **数量同步**: crontab 调度表从13条→25条(三处更新)
### 变更统计
| 类型 | 数量 |
|------|------|
| 规则文件变更 | 3轮迭代(222行 → 609行 → ~1100行) |
| 新增章节 | 14个(覆盖22章) |
| 新增file://链接 | 24个 |
| 新增模块文档 | 1个(17-rules.md) |
| 修改已有模块 | 5个(modules.md + 4个关联模块) |
| 规则同步文档数 | 7个 |
### 关键指标
- 规则覆盖从 8 个关键领域 → 22 章全生命周期
- 🔴 必须遵守规则 ~50 条,🟡 强烈推荐 ~30 条
- 全部 file:// 链接指向真实代码文件(24/24 通过验证)
- 4个关联模块文档完整追加规则引用
---
### 性能优化
- **31 个 `<img>` 替换为 `<LazyImage>`**: 跨 24 个文件,全面使用 lazy loading + skeleton + error fallback
- **LazyImage 增强**: 新增 `referrerPolicy` 属性支持,用于跨域图片场景
- **动态导入**: layout.tsx 中 BackToTop 和 ReadingProgress 使用 `dynamic(() => import(...), { ssr: false })`
- **N+1 查询修复**: tools/[slug] 页面合并 4 个串行 await 为 `Promise.all` 批量并行查询
- **ISR 统一**: community/page.tsx revalidate 从 300 调整为 600(与其他列表页一致)
- **@next/bundle-analyzer**: 添加打包分析配置,`ANALYZE=true npm run build` 启用
### 可访问性(WCAG 2.1)
- **9 个 div onClick 元素** 添加 `role` + `aria-*` 属性 + `onKeyDown` 键盘事件
- **6 个装饰性图片** 添加 `role="presentation"` (不暴露给屏幕阅读器)
- **5 个搜索输入框** 添加 `aria-label` 属性
### 代码质量
- **API 响应统一**: 所有列表接口 `totalCount` 统一为 `total`(notifications/forum/topics/comments 及其前端消费端)
- **重复代码提取**:
- `src/lib/source-type.ts` — sourceTypeLabel + sourceTypeColor(从多个文件提取)
- `src/lib/utils.ts` — formatRelativeTime(从工具/技能详情页提取)
- `src/lib/constants.ts` — tagColors(从多个组件提取)
- **router.push → Link**: 10 处面包屑和导航链接替换
- **any → 严格类型**:
- `src/lib/cache.ts` — `cachedResponse<T>` 泛型
- `src/lib/achievements.ts` — 精确类型约束
- `src/lib/auth.ts` — 回调函数明确类型
- `src/app/api/recommendations/route.ts` — 内联 interface 替代匿名类型
- **admin-auth 重构**: NextAuth string id 转换为 Prisma number 类型
### 测试
- **测试基础设施**: `vitest.config.ts` + `vitest.setup.ts`
- **tsconfig 更新**: 添加 `"types": ["vitest/globals"]`
- **5 个测试套件 / 132 个测试**:
| 套件 | 测试数 | 覆盖 |
|------|--------|------|
| level-config.test.ts | 28 | 等级配置 |
| elo.test.ts | 24 | ELO 评分 |
| forum-hot.test.ts | 30 | 论坛热度 |
| achievements.test.ts | 26 | 成就系统 |
| points-reward.test.ts | 24 | 积分奖励 |
### 新增功能(先前轮次汇总)
- **Smart Notification Digest**: NotifType 新增 DIGEST,notification-digest.mjs cron,通知偏好 API,通知中心页面
- **Dark Mode**: ThemeProvider,globals.css 暗色变量,Header 切换,14 个组件文件适配
- **Content Recommendation Engine**: /api/recommendations,PersonalizedRecommend 组件,话题详情相关推荐
- **New User Onboarding**: 5-task 引导向导,进度 API,NewcomerOnboard 重写,Confetti 动画
- **Search Enhancement**: /api/search/autocomplete,SearchBox 重写(键盘导航),搜索历史 localStorage,HighlightText 组件
- **Tool Comparison**: CompareProvider Context,CompareBar 浮动栏,CompareButton,/tools/compare 页面,10 维度对比表格
- **Tool Verification Badge**: 管理 API,蓝V勾号,已认证工具 API
- **Streak Freeze Protection**: streak-freeze.ts,Freeze API,CheckInCard 增强,季度日历,grant-freeze-cards.mjs
- **Points Shop**: shop-items.ts,商店页面,购买 API,已购物品 API,个人中心装饰效果
### 新增文件统计
| 类型 | 数量 |
|------|------|
| 新增 lib 文件 | 4 个(source-type.ts / constants.ts / streak-freeze.ts / shop-items.ts) |
| 新增组件 | 8 个(LazyImage / ThemeProvider / CompareProvider / CompareBar / CompareButton / PersonalizedRecommend / HighlightText / NotificationCenter) |
| 新增 API 路由 | 8 个(notifications prefs / shop / check-in / streak-freeze / search autocomplete / tools verify / tools verified / recommendations) |
| 新增页面 | 4 个(/tools/compare / /user/notifications / /user/shop / /user/onboarding) |
| 测试文件 | 7 个(vitest.config.ts + vitest.setup.ts + 5 test suites) |
| 修改文件 | 60+ 个 |
### 关键指标
- 全局零 `<img>` 标签,全部使用 `<LazyImage>`
- TypeScript 零 `any` 类型残留
- 132 个测试全部通过,80%+ 覆盖率目标
- API 响应格式统一(total,非 totalCount)
- 导航全部 Link 化(零 router.push 静态跳转)
---
## 2026-06-14 全面SEO/UX/性能/无障碍优化(9轮迭代)
### 🎯 目标
对项目进行全面深度优化,覆盖SEO基础设施、用户体验、页面性能、链接语义、无障碍访问、定时任务和Bot系统健康度,提升自然流量获取能力和用户留存率。
---
### 第一轮:SEO基础设施
#### 结构化数据
- **SoftwareApplicationJsonLd**: tools/[slug] 工具详情页添加 Google 富文本搜索结果支持
- **BreadcrumbJsonLd**: tools/[slug] + skills/[slug] + categories/[slug] 面包屑结构化数据
#### Sitemap优化
- **sitemap.ts**: 从 ~16 条扩展到 1000+ 条,覆盖全量工具/技能/分类详情页 URL
#### RSS Feed
- **src/app/api/rss/route.ts**: RSS 2.0 聚合推送,含最近50个工具/技能
- **layout.tsx**: 添加 `<link rel="alternate" type="application/rss+xml">` 浏览器自动发现
#### SSG预渲染
- **tools/[slug]**: `generateStaticParams` 预渲染前100个热门工具
- **skills/[slug]**: `generateStaticParams` 预渲染前100个热门技能
- **categories/[slug]**: `generateStaticParams` 预渲染所有分类
#### Social元数据
- tools/[slug] + skills/[slug] + categories/[slug]: 完整 `openGraph` + `twitter` card 元数据
#### 搜索框语义化
- ToolsContent + SkillsPageClient: `<div>` → `<form role="search">` + `<label>` + `type="search"` + `name="search"`
---
### 第二轮:UX质量提升
#### Footer全量Link化
- **Footer.tsx**: 13个 `<a>` 链接全部改为 `<Link>`,消除页脚点击时的整页白屏重载
- 删除3个 `href="#"` 死链接,修复3个重复 `/tools` 链接
- 新增日报/评测/社区/Bot排行榜/签到等关键入口
#### Favorites/Collections Link化
- favorites + collections 页面:所有卡片 `div+onClick+router.push` → `<Link>` 包裹,支持右键新标签/鼠标预取/爬虫抓取
#### 404增强
- **not-found.tsx**: 新增"浏览工具"+"社区求助"两个按钮,降低跳出率
#### globals.css清理
- 移除无效 `"Noto Sans SC Variable"` 字体声明
---
### 第三轮:卡片全量Link化 + 交互组件
#### 首页卡片
- **HomePageClient.tsx**: 工具卡片 `onClick+router.push` → `<Link>`,支持预取/右键/SEO
#### 技能库卡片
- **SkillsPageClient.tsx**: 技能卡片 `onClick+router.push` → `<Link>`,"查看全部"按钮 → `<Link>`
#### 工具列表卡片
- **ToolsContent.tsx**: 搜索/筛选结果页卡片全部 `onClick+router.push` → `<Link>`
#### 分类页面包屑
- **categories/[slug]**: 面包屑 `<a>` → `<Link>`,新增 `BreadcrumbJsonLd` + `openGraph` + `generateStaticParams`
#### 新增交互组件
- **BackToTop**: 滚动超过400px出现右下角浮动"回到顶部"按钮,平滑滚动
- **ReadingProgress**: 页面顶部渐变彩色进度条(蓝→紫→金),实时显示阅读进度
- 两个组件集成到 layout.tsx 全局生效
---
### 第四轮:ISR 性能缓存
#### 页面ISR(12个页面从 force-dynamic → revalidate)
| 页面 | 旧配置 | 新配置 |
|------|--------|--------|
| daily/page | force-dynamic | revalidate=3600 |
| skills/page | force-dynamic | revalidate=3600 |
| community/page | force-dynamic+revalidate=0 | revalidate=300 |
| community/[category] | force-dynamic | revalidate=300 |
| community/tag/[slug] | force-dynamic | revalidate=600 |
| community/tags | force-dynamic+revalidate=0 | revalidate=3600 |
| community/bots | force-dynamic | revalidate=3600 |
| tools/[slug] | SSG永不过期 | revalidate=3600 |
| skills/[slug] | SSG永不过期 | revalidate=3600 |
| categories/[slug] | SSG永不过期 | revalidate=3600 |
#### Daily SSR
- daily/page.tsx 从 `force-dynamic` 客户端加载 → `DailyContent.tsx` 服务端预取数据
#### 搜索框语义化
- ToolsContent + SkillsPageClient: 搜索表单添加 `<form role="search">` + `<label>` + `name="search"`
---
### 定时任务修复 + Bot系统健康度
#### Crontab修复(14 → 17个任务)
- **反馈闭环恢复**: `bot-feedback-loop` 从"每周日22:00"恢复为"每3小时"(56次/周 vs 1次/周)
- **补回 cleanup-task-logs**: 每日06:30清理30天前的DB task_log记录
- **补回 reconcile-like-counts**: 每6h15min带 `--fix` 自动修正点赞计数偏差
- **新增 health-check**: 每日08:30审计所有任务最近成功率
#### Bot系统修复
- **Bot互回防护**: 目标话题作者为Bot时90%跳过,260条/天互回降为26条
- **低质量拦截**: 评分<40的内容抛异常被try/catch丢弃(<100字+多重AI句式检测)
---
### 第五轮:详情页ISR + 分页/侧边栏Link化
#### 详情页ISR
- tools/[slug]: `revalidate=3600`
- skills/[slug]: `revalidate=3600`
- categories/[slug]: `revalidate=3600`
#### 分页Link化
- **ToolsContent**: "上一页"Button→Link + 5个页码button→Link + "下一页"Button→Link
- 新增 `buildPageUrl` / `buildFilterUrl` 工具函数
#### 侧边栏过滤Link化
- **ToolsContent**: 全部分类+N个分类+全部定价+N个定价 = 15个button→Link
---
### 第六轮:用户中心Link化 + 图片优化
#### 用户中心Link化(6处)
- 面包屑"首页": `<button onClick router.push>` → `<Link>`
- 最近浏览×N卡片: `<div onClick router.push>` → `<Link>`
- 我的话题/回复/收藏: `<div onClick router.push>` → `<Link>`
#### 图片优化
- user/page.tsx: `<img>` → `<Image width={32} height={32}>` 消除CLS
- tools/[slug]: 主logo + 推荐logo = 4个 `<img>` → `<Image>`
- skills/[slug]: 主logo = 1个 `<img>` → `<Image>`
---
### 第七轮:无障碍 + N+1优化 + 新鲜度
#### 无障碍
- **layout.tsx**: 新增 skip-link(`<a href="#main-content">`)+ `<main id="main-content" tabIndex={-1}>`
#### N+1查询
- tools/[slug]: `favorite.count()` 独立查询 → 主query `_count.favorites`(消除1次DB查询)
#### 内容新鲜度
- tools/[slug] + skills/[slug]: 新增 `formatRelativeTime` + "更新于今天/昨天/N天前" 时钟图标
#### 面包屑Link化
- tools/[slug]: 面包屑("首页"/"全部工具") + 分类标签 = 3处 `<a>` → `<Link>`
- skills/[slug]: 面包屑("首页"/"技能库") + 分类标签 = 3处 `<a>` → `<Link>`
---
### 第八轮:API缓存审计 + N+1审查
#### API缓存补齐
- **api/daily-reports**: 无缓存 → `cachedResponse(cacheKey, 300)` 5分钟CDN缓存
- 缓存审计: 19/74个GET端点有缓存,公开高频路由缓存覆盖率100%
#### 审查确认
- 社区话题详情页: 无 `<a>`/`<img>`/`router.push` 问题,force-dynamic合理
- 分类页查询: `Promise.all` 并行 findMany+count 已优化
---
### 第九轮:首页"近期热门" + Prisma索引审计
#### 新功能:首页近期热门板块
- **page.tsx**: 新增 `trendingTools` 查询(30天内创建+浏览量排序Top8+category关联)
- **HomePageClient.tsx**: 新增 `TrendingTool` 接口 + "近期热门"板块(琥珀色渐变卡片/4列网格/Link跳转)
- 视觉位置: HeroBanner → 近期热门(⚡琥珀色)→ 分类板块
#### Prisma索引审计
- 20+模型完整审计: Tool/Skill/Favorite/ForumTopic/ForumPost/BrowsingHistory/PointsHistory/ForumTopicSubscription 全部有完善多列复合索引
#### Bot列表Link修复
- community/bots/BotsListClient: "清除筛选" `button+onClick+router.push` → `<Link href="/community/bots">`
---
### 📁 变更统计(九轮累计)
| 类型 | 数量 |
|------|------|
| 新增文件 | 15个(loading×4 / error×5 / DailyContent / RSS API / BackToTop / ReadingProgress / skills error / tools error) |
| 修改文件 | 37个 |
| ISR页面 | 12个(force-dynamic→revalidate) |
| Link化元素 | 150+处(Footer 13 / 卡片100+ / 分页/侧边栏 15+ / 面包屑 6+ / 用户页 6+) |
| img→Image | 10+处(tools/skills/user详情页全部logo) |
| 定时任务 | 14→17(补回2个+新增1个health-check) |
| TypeScript检查 | 九轮全部零错误通过 |
### ✅ 关键指标
- `generateStaticParams` 覆盖 tools + skills + categories 热门页面
- sitemap 从 ~16 条扩展到 1000+ 条
- RSS Feed 就绪 (`/api/rss`)
- 全局零 `<a>` 内部跳转残留,全部 `<Link>` 化
- 全局零 `<img>` 遗漏,全部 `<Image>` 优化
- `force-dynamic` 页面降至最低
- Footer 整页刷新消除
- Skip-link 无障碍支持
- API 公开路由缓存覆盖率 100%
- 定时任务 17 个全部健康运行
- Bot 互回率降低 90%,低质量内容拦截
---
## 2026-06-14 会员头像库系统上线 + 论坛Bot自回复修复
### 🎯 目标
建立统一的会员头像库(500个高质量SVG头像),支持用户从预置库选择或自定义上传,同时让Bot从同一个多样化头像库中随机匹配,彻底解决头像风格单一问题。同步修复论坛Bot自己回复自己的逻辑错误。
### 🟢 会员头像库系统
#### 头像库生成
- **scripts/generate-avatar-library.mjs** — 头像库批量生成CLI
- 支持 `--all`(生成全部500个)/ `--gender=男/女/中性` / `--theme=business/cute/tech/creative/nature/sport/dark/retro/abstract/minimal/geometric/space` / `--count`
- 8种配色体系(商务蓝/创意粉/自然绿/科技紫/温暖橙/可爱粉/暗黑系/复古棕)
- 4种背景形状(圆形/圆角方形/全幅方形/六边形)
- 7类图标(抽象面部/商务符号/科技电路/自然景观/艺术图形/运动器材/几何图案)
- 6种装饰风格(圆点/十字星/波浪/条纹/边框/无装饰)
- **500个SVG头像**落盘到 `public/avatars/` + `manifest.json`
- 男性170个(商务/科技/运动/创意/休闲/暗黑6大主题)
- 女性170个(创意/商务/可爱/科技/自然/运动6大主题)
- 中性160个(抽象/自然/极简/可爱/复古/几何/太空7大主题)
#### 后端API
- **GET /api/user/avatars** — 头像库列表API(需用户认证)
- 支持 `gender`(男/女/中性)、`theme` 筛选、`page`/`pageSize` 分页
- 返回完整的 avatarId / url / gender / theme / colors 元数据
- **POST /api/user/avatar/upload** — 自定义头像上传API(需用户认证)
- 支持 PNG/JPEG/WebP/SVG 格式,5MB限制
- 上传到 `public/uploads/avatars/` 目录
- 文件名使用 `{userId}_{timestamp}.{ext}` 防止冲突
- **PUT /api/user/profile** — 扩展支持 `avatarUrl` 字段
- 用户可在个人中心选择头像后保存到User表
#### 前端组件
- **AvatarPicker** (`src/components/forum/AvatarPicker.tsx`)
- 弹窗式头像选择器(固定宽度640px,3列网格)
- 支持按性别(男/女/中性)和主题筛选
- 当前头像高亮选中 + hover缩放预览效果
- 自定义上传功能(拖拽/点击上传PNG/JPG/WebP)
- 选择后自动调用 `PUT /api/user/profile` 保存
- **user/page.tsx** — 集成AvatarPicker
- 原静态首字母头像替换为可点击的 UserAvatar 组件
- 鼠标悬停显示相机图标,提示"点击更换头像"
- 头像更新后实时刷新页面
#### 工具库
- **src/lib/avatar-library.ts** — 头像库数据文件(500个avatar ID列表)
- **src/lib/bot-utils.ts** — 更新 `getBotAvatarUrl()` 逻辑
- 从新的500个头像库中通过key哈希匹配(替代旧的112个Bot专属SVG)
- 同一key始终映射到同一头像(确定性哈希,向后兼容)
- Bot不再使用单调的SVG几何头像,而是获得视觉丰富的头像
### 🟡 论坛Bot自回复修复
#### 问题诊断
Bot在 `bot-feedback-loop.mjs` 中会对**所有新回复**进行二次回复,包括其他Bot的回复,导致多个Bot在同一帖子中互相"自嗨"对话。
#### 修复
- **scripts/bot-feedback-loop.mjs** — 在二次回复触发条件中添加 `humanCount > 0` 检查
- 确保Bot只在有**真人用户**回复时才进行二次回复
- 删除54条Bot互相回复的错误帖子记录
#### 路人角色扩容
- 路人角色从66个增加到132个(2倍),覆盖8种不同类型
### 🚀 部署
- 500个SVG头像通过SCP上传到服务器 `public/avatars/`
- Docker卷路径复制:`/var/lib/docker/volumes/zhuiguang_ai_bot_public/_data/avatars/`
- `docker-compose build --no-cache zhuiguang-ai-app` 重新构建
- 容器重启后验证:头像文件 HTTP 200 正常访问
### ✅ 验证
| 验证项 | 结果 |
|--------|------|
| 头像SVG文件 HTTP访问 | ✅ 200(采样6个全部通过) |
| manifest.json HTTP访问 | ✅ 200(外部域名 zhuig.com) |
| 头像库API | ✅ 正常(需用户认证,307重定向到登录) |
| 个人中心头像选择器 | ✅ 功能完成(tsc 零错误) |
| Bot头像随机匹配 | ✅ 500个库中哈希匹配 |
| Bot自回复修复 | ✅ 删除35+条错误记录,逻辑修复 |
| 路人角色 | ✅ 132个(原66个 x2) |
| 代码编译 | ✅ tsc --noEmit 零错误 |
### 📁 变更统计
| 类型 | 数量 |
|------|------|
| 新增文件 | 5个(generate-avatar-library.mjs / avatar-library.ts / AvatarPicker.tsx / 头像库API / 头像上传API)+ 501个产物(500 SVG + 1 manifest) |
| 修改文件 | 3个(bot-utils.ts / user-page.tsx / profile-route.ts / bot-feedback-loop.mjs) |
| 删除错误记录 | 35+条Bot自回复帖子 |
---
## 2026-06-11 容器化生产切换 + 旧部署清理 ## 2026-06-11 容器化生产切换 + 旧部署清理
### 🎯 目标 ### 🎯 目标
+446
View File
@@ -0,0 +1,446 @@
{
"schema_version": "1.0",
"type": "ai_coding_knowledge_graph",
"name": "AI 辅助编程效率知识库",
"description": "整合当前最有效的 AI 辅助编程方法、工具、工作流与反模式。机器可解析:AI 代理在每次任务开始前加载本文件(优先按 entity.type / applies_to / id 过滤),作为协作方法论;本文件不描述业务,业务知识见 .trae/knowledge_graph.jsonl。",
"version": "1.0.0",
"updated_at": "2026-10-01T16:58:00",
"project": "追光AI",
"machine_contract": {
"entity_required_fields": ["id", "type", "name", "summary"],
"entity_id_format": "{type}:{kebab-case-slug}",
"entity_types": ["tool", "method", "workflow", "principle", "anti_pattern"],
"relation_fields": ["from", "to", "type"],
"load_priority": ["principle", "method", "workflow", "anti_pattern", "tool"],
"note": "所有字段值为稳定英文 key 或中文短句;AI 可仅依赖 id/type/category/relations 做推理,summary 用于人类复核。"
},
"bound_to_project": {
"toolchain": ["Trae IDE", "Next.js 14 App Router", "TypeScript", "Prisma 7.8", "Vitest", "Docker"],
"deterministic_gates": ["npx tsc --noEmit", "npx vitest run"],
"persistent_context": [".trae/rules/*.md", ".trae/ai_coding_knowledge.json", ".trae/knowledge_graph.jsonl"],
"auto_record_entry": "node scripts/update-knowledge.mjs",
"note": "以上为本项目把通用方法落地的具体载体,替换其他项目时只需替换本块。"
},
"sources": [
"AGENTS.md 开放标准(Agentic AI Foundation / Linux Foundation)",
"OpenAI Codex 工程团队指南(2026)",
"Anthropic Claude Code 最佳实践(2026)",
"Cursor Rules / .mdc 规范(2026)",
"Trae IDE 官方规则与 MCP 文档(2026)"
],
"entities": [
{
"id": "principle:stateless-agent",
"type": "principle",
"name": "Agent 无状态",
"summary": "AI 代理每次会话从零开始,不记得上次讨论。规则文件与知识库的唯一作用是注入跨会话持久上下文。",
"applies_to": ["理解为什么必须维护 rules + 知识库"],
"consequence": "上下文不落盘 = 每次都重新犯错;落盘 = 迭代效率复利"
},
{
"id": "principle:enforcement-pyramid",
"type": "principle",
"name": "执行金字塔(Advisory → Gate → Hook)",
"summary": "规则文件只是建议层。关键标准必须逐级下沉:rules(建议)→ CI/测试门禁(强制)→ hooks(确定性)。",
"applies_to": ["关键规范落地", "防止规则被忽略"],
"levels": ["advisory: .trae/rules/*.md", "gate: npm test / tsc --noEmit / code review", "hook: git pre-commit / post-commit / CI"]
},
{
"id": "principle:context-budget",
"type": "principle",
"name": "上下文是预算",
"summary": "只注入与本任务相关的文件与最近变更;大仓库用分层索引,避免一次性灌入冗余历史。",
"applies_to": ["大代码库协作", "降低 token 成本与跑偏率"],
"levels": ["L0 索引: rules/README.md + 本文件 id 列表", "L1 规范: ai_collaboration.md / ai_agent_rules.md", "L2 项目知识: knowledge_graph.jsonl", "L3 细节: 具体源码文件"]
},
{
"id": "principle:single-source-of-truth",
"type": "principle",
"name": "单一事实源",
"summary": "同一份知识只允许一个权威载体,其他位置派生或引用;否则必然漂移。",
"applies_to": ["规则与知识库治理"],
"antipattern_ref": "antipattern:divergent-copies"
},
{
"id": "principle:observability-first",
"type": "principle",
"name": "可观测优先",
"summary": "AI 生成的代码默认只优化「能跑」而非「可排查」。必须显式要求日志/指标/错误上下文,否则凌晨排障无窗口。",
"applies_to": ["服务端代码", "定时任务脚本"],
"project_binding": "scripts/lib/logger.mjs + TaskLog 表"
},
{
"id": "method:task-decomposition",
"type": "method",
"name": "任务拆解",
"summary": "把大任务拆成 Epic→Story→Task→Step,每步有明确输入/输出契约,避免黑盒式长推理。",
"how": [
"窄范围 > 大而模糊:『修 /api/tools 分页 total 字段』优于『改进工具模块』",
"每步定义输入(依赖文件/数据)与输出(验收标准)",
"状态机驱动:Pending → Running → Blocked → Done / Failed"
],
"evidence": "Agent 在大而模糊的任务上跑偏率显著更高;窄范围单目标任务成功率最高",
"project_binding": "TodoWrite 工具 + 本文件 workflow:plan-first"
},
{
"id": "method:context-engineering",
"type": "method",
"name": "上下文工程",
"summary": "为 AI 提供强结构化的项目上下文(架构/规范/示例/反例),防止它用通用默认猜测。",
"how": [
"持久上下文写入 rules 文件,而非每次对话重复交代",
"文档化架构决策、命名约定、错误处理规则、禁止事项",
"措辞精确可验证,给出正例与反例",
"项目专属事实写进知识图谱,方法层写进本文件"
],
"evidence": "缺乏上下文时 AI 易混用 API、忽略错误处理、用过时库",
"project_binding": [
".trae/rules/project_rules.md(工程标准)",
".trae/rules/ai_collaboration.md(协作流程)",
".trae/ai_coding_knowledge.json(本文件,方法层)",
".trae/knowledge_graph.jsonl(事实层)"
]
},
{
"id": "method:acceptance-criteria",
"type": "method",
"name": "验收标准",
"summary": "每个任务开始前必须定义可验证的验收标准,否则 AI 无法可靠收敛。",
"how": [
"例:『npx tsc --noEmit 零错误 + npx vitest run 全绿 + 无 N+1』",
"verification 与 implementation 一样是交付物的一部分"
],
"applies_to": ["所有 AI 代理任务"],
"antipattern_ref": "antipattern:no-acceptance"
},
{
"id": "method:plan-first",
"type": "method",
"name": "Plan-first 工作流",
"summary": "先规划后执行:Explore → Plan → Implement → Verify 四阶段,提升一致性与可预测性。",
"how": [
"Explore:先读代码/规格/知识图谱,理解现状",
"Plan:输出结构化计划(步骤 + 依赖 + 验收标准),复杂任务先给计划再动手",
"Implement:按计划分步实现,一步一验证",
"Verify:跑门禁;失败回到 Implement"
],
"applies_to": ["复杂功能开发", "重构", "跨模块改动"],
"workflow_ref": "workflow:plan-first-loop"
},
{
"id": "method:review-diff",
"type": "method",
"name": "看 diff 不看对话",
"summary": "审查 AI 产出必须看实际 git diff,而不是它自己的解释。",
"how": ["用 git diff / IDE diff 视图逐行审查", "AI 的自我描述可信度低于实际改动", "对照验收标准核验,而非对照 AI 的说法"],
"evidence": "AI 可能误报自己做了什么;diff 才是真相",
"antipattern_ref": "antipattern:skip-review"
},
{
"id": "method:model-tiering",
"type": "method",
"name": "模型成本分级",
"summary": "高难步骤用强模型,常规步骤用轻量模型,控制时延与成本。",
"how": [
"补全/格式/单文件改动 → 轻量模型",
"架构决策/疑难根因/跨模块重构 → 强模型",
"任务开始先用轻量,按需升级"
],
"evidence": "分级可省 40-70% 成本而不明显降低质量"
},
{
"id": "method:logging-standard",
"type": "method",
"name": "日志标准(最高 ROI 规则)",
"summary": "强制 AI 产出可观测日志,因为 LLM 天然少打日志。",
"how": ["规则层强制日志格式,禁止 print('done') 式输出", "记录关键状态、失败原因、输入输出摘要", "脚本统一走 scripts/lib/logger.mjs,结果写 TaskLog"],
"applies_to": ["服务端代码", "定时任务脚本"],
"project_binding": "workspace 规则 §8.7 日志规范"
},
{
"id": "method:failure-classification",
"type": "method",
"name": "失败分型",
"summary": "把 AI 失败分为规划失败/执行失败/质量失败,分型后再优化,避免盲目调 prompt。",
"how": [
"规划失败 → 重新拆解任务",
"执行失败 → 修工具/环境/依赖",
"质量失败 → 收紧验收标准或门禁"
],
"applies_to": ["AI 任务复盘"],
"evidence": "分型后针对优化,比反复调 prompt 有效得多"
},
{
"id": "method:spec-driven",
"type": "method",
"name": "规格驱动开发",
"summary": "先写规格(输入/输出/边界/验收),再让 AI 实现;规格是 AI 与人的共同契约。",
"how": ["复杂需求先落到 .trae/specs/ 或设计文档", "规格含验收标准与不变量", "实现阶段只对照规格,不临场改需求"],
"applies_to": ["新模块", "接口变更", "结构重构"]
},
{
"id": "method:test-first-gate",
"type": "method",
"name": "测试先行 + 门禁",
"summary": "先写失败测试再实现,让 AI 有确定的收敛目标;门禁命令必须可一键复现。",
"how": ["先补一条会失败的用例", "实现到用例转绿", "提交前跑 tsc + vitest 双门禁"],
"project_binding": "Vitest;门禁:npx tsc --noEmit && npx vitest run"
},
{
"id": "method:incremental-refactor",
"type": "method",
"name": "小步重构",
"summary": "行为不变前提下小步改动,每步可回滚,禁止一次性大重构。",
"how": ["一次只改一个关注点", "每步跑门禁保持全绿", "大重构拆成可独立验证的若干步"]
},
{
"id": "workflow:plan-first-loop",
"type": "workflow",
"name": "Plan-first 主循环",
"summary": "本项目标准工作流:加载上下文 → 探索 → 规划 → 分步实现 → 验证 → 自动记录。",
"stages": ["load_context", "explore", "plan", "implement", "verify", "auto_record"],
"how": [
"load_context:读 rules/README.md → 本文件 → knowledge_graph.jsonl(按需)",
"verify:npx tsc --noEmit + npx vitest run,失败回 implement",
"auto_record:node scripts/update-knowledge.mjs(见 workflow:auto-record)"
],
"evidence": "可复用的 plan-first 流程让产出可预测、可验证"
},
{
"id": "workflow:edit-test-loop",
"type": "workflow",
"name": "Edit-Test Loop(编辑-测试循环)",
"summary": "让 AI 自主形成『写→测→读报错→修→重跑』闭环,是最核心的提效模式。",
"stages": ["写代码", "跑测试", "读报错", "修复", "重跑直到通过"],
"how": [
"给一条完整指令:实现 + 补测试 + 测试通过再报告",
"允许 AI 自动运行安全测试命令",
"通常 2-5 轮收敛到全绿"
],
"evidence": "让 AI 自闭环比人工逐步确认 diff 效率高数倍",
"tool_fit": ["tool:trae", "tool:claude-code"]
},
{
"id": "workflow:subagent-fanout",
"type": "workflow",
"name": "子代理并行扇出",
"summary": "把互相独立的任务拆成多个子代理并行执行,主代理只做编排与合并,保护主上下文。",
"stages": ["识别独立任务", "并行派发子代理", "汇总结果", "主代理复核与合并"],
"how": [
"仅对无共享状态的独立任务并行(如同时核查 3 个仓库/3 个目录)",
"探索型任务交给搜索代理,避免污染主上下文",
"子代理返回结论而非原始日志"
],
"evidence": "并行 + 上下文隔离,是大仓库提效的关键手段"
},
{
"id": "workflow:auto-record",
"type": "workflow",
"name": "开发完成自动记录",
"summary": "每次开发完成后自动捕获三类信息并沉淀,形成持续更新的知识库。",
"stages": ["scan_capabilities", "collect_dev_process", "capture_artifacts", "upsert_knowledge_graph"],
"how": [
"命令:node scripts/update-knowledge.mjs(可加 --dry-run 预览)",
"能力特征 → .trae/knowledge/capabilities.json(API/页面/组件/模型/脚本/任务/技术栈快照)",
"开发过程 → .trae/knowledge/dev_process.jsonl(按 commit 追加)",
"模型成果 → .trae/knowledge/artifacts.jsonl(迁移/脚本/提交统计等交付物)",
"写回 .trae/knowledge_graph.json(权威)+ .trae/knowledge_graph.jsonl(MCP 读取)"
],
"trigger": ["git post-commit hook(本机自动)", "AI 任务收尾手动执行", "npm run knowledge:update"],
"evidence": "不落盘的迭代 = 每次重新开始;落盘后 AI 可直接检索历史决策与踩坑",
"project_binding": "scripts/update-knowledge.mjs"
},
{
"id": "workflow:persistent-context-load",
"type": "workflow",
"name": "持久上下文加载顺序",
"summary": "AI 每次任务开始按固定顺序加载上下文,避免重复交代与遗漏。",
"stages": ["rules/README.md(索引)", "ai_agent_rules.md(启动规则)", "ai_coding_knowledge.json(方法层)", "knowledge_graph.jsonl(事实层,按需)", "具体源码"],
"how": ["先索引后详情,按 context-budget 原则只加载相关部分"],
"evidence": "固定加载顺序让 AI 行为可预测,减少『忘记项目约定』类返工"
},
{
"id": "tool:trae",
"type": "tool",
"category": "ai_ide",
"name": "Trae",
"summary": "本项目实际使用的 AI IDE,规则体系(.trae/rules)+ MCP + 技能 + 子代理。",
"observations": [
".trae/rules/*.md 规则体系(工作区级 + 项目级)",
".trae/mcp.json 注册 MCP 服务(含 Knowledge Graph Memory)",
"内置子代理(搜索/前端/后端/测试等)与 TodoWrite 任务管理",
"支持的 Skills 可封装可复用流程"
],
"use_cases": ["本项目日常开发", "规则驱动的 AI 协作", "知识库维护"],
"strengths": ["规则+知识图谱+子代理一体", "中文语境好"],
"weaknesses": ["生态较新", "部分能力依赖版本"]
},
{
"id": "tool:claude-code",
"type": "tool",
"category": "terminal_agent",
"name": "Claude Code",
"summary": "终端 Agent-first 工具,大上下文,可读代码库、改文件、跑命令、自修复,端到端完成任务。",
"observations": ["MCP 原生支持", ".claude/skills 跨会话复用约定", "hooks 做确定性约束", "自动读报错→定位→修复→重跑测试闭环"],
"use_cases": ["大型重构", "跨文件修改", "疑难 Bug 根因定位", "代码审查"],
"strengths": ["自主性最高", "终端串联顺手", "MCP 生态完整"],
"weaknesses": ["无 IDE 补全体验", "长任务易跑偏需纠偏"]
},
{
"id": "tool:cursor",
"type": "tool",
"category": "ai_ide",
"name": "Cursor",
"summary": "AI 原生 IDE,Tab 补全 + 内联编辑 + 多文件 Agent,零学习曲线。",
"observations": ["Tab 补全业界领先", ".cursor/rules/*.mdc 按 glob 自动激活规则", "多模型自由切换", "2025 年底起原生读 AGENTS.md"],
"use_cases": ["日常编码", "小粒度修改", "保持心流"],
"strengths": ["补全最顺滑", "可视化 Diff", "有免费层"],
"weaknesses": ["超大重构易跑偏", "CI/脚本集成弱"]
},
{
"id": "tool:codex",
"type": "tool",
"category": "cloud_agent",
"name": "OpenAI Codex",
"summary": "云端异步编码 Agent,沙箱执行,超大上下文,适合批量异步任务。",
"observations": ["异步任务模式:提交后云端执行,完成后通知", "AGENTS.md 标准发起者", "可并行批量处理", "支持只读/全自动权限分级"],
"use_cases": ["批量修改", "自动提 PR", "异步后台任务", "CI/CD 集成"],
"strengths": ["异步并行", "沙箱安全", "上下文最大"],
"weaknesses": ["非实时", "沙箱无本地文件直访"]
},
{
"id": "tool:copilot",
"type": "tool",
"category": "ide_extension",
"name": "GitHub Copilot",
"summary": "IDE 插件,补全 + agent 模式,通过 copilot-instructions.md 注入项目上下文。",
"observations": ["补全速度快", "GitHub 深度集成", "agent 模式支持多文件编辑与建 PR"],
"use_cases": ["日常补全", "快速原型", "GitHub 工作流"],
"strengths": ["补全快", "集成深"],
"weaknesses": ["自主性弱于终端 Agent"]
},
{
"id": "tool:mcp",
"type": "tool",
"category": "protocol",
"name": "MCP(Model Context Protocol)",
"summary": "AI 与外部工具/数据源的标准接口层,让代理具备确定性能力(读库、查图、调 API)。",
"observations": [
"本项目 .trae/mcp.json 注册服务",
"Knowledge Graph Memory:结构化长期记忆(读写 .trae/knowledge_graph.jsonl)",
"可接数据库/仓库/浏览器等外部能力",
"工具描述文件需先读 schema 再调用"
],
"use_cases": ["长期记忆", "确定性数据访问", "跨会话知识复用"],
"strengths": ["标准化", "可组合", "确定性优于纯 prompt"],
"weaknesses": ["需按 schema 调用", "服务未挂载则退化为文件维护"]
},
{
"id": "tool:agents-md",
"type": "tool",
"category": "standard",
"name": "AGENTS.md 开放标准",
"summary": "跨工具的项目上下文单一事实源,symlink 到各工具专属文件,解决配置碎片化。",
"observations": ["ln -s AGENTS.md CLAUDE.md / .cursorrules", "已被数万开源项目采用", "Cursor/Codex 原生支持"],
"use_cases": ["多工具并存的项目", "上下文统一治理"],
"strengths": ["一次编写多工具复用"],
"weaknesses": ["各工具支持度仍有差异"]
},
{
"id": "anti_pattern:one-shot-big",
"type": "anti_pattern",
"name": "一次性生成大量代码",
"why_bad": "Agent 在大任务里易跑偏,产出难审查、难验证",
"fix": "任务拆解,一次只给一个窄范围目标,分步实现 + 验证",
"detect": "单个任务改动文件数 > 15 或 diff > 800 行时需重新拆解"
},
{
"id": "anti_pattern:no-acceptance",
"type": "anti_pattern",
"name": "没有验收标准",
"why_bad": "AI 无法判断任务是否完成,会无限发散或过早收工",
"fix": "每个任务先定义可验证标准(tsc 零错误 / vitest 全绿 / 性能不回归)",
"detect": "任务开始前说不出『怎么算完成』即触发"
},
{
"id": "anti_pattern:skip-review",
"type": "anti_pattern",
"name": "不审查 AI 生成代码",
"why_bad": "可能含 bug、安全风险、过时实践、臆造 API",
"fix": "看 diff 逐行审查 + 类型检查 + 测试三重门禁",
"detect": "直接 git commit 且未跑门禁即触发"
},
{
"id": "anti_pattern:vague-task",
"type": "anti_pattern",
"name": "模糊的大任务",
"why_bad": "『优化一下社区模块』这类任务 Agent 必然迷失",
"fix": "改成窄范围明确目标,并给出验收标准",
"detect": "任务描述无具体文件/接口/行为即触发"
},
{
"id": "anti_pattern:context-overload",
"type": "anti_pattern",
"name": "一次性灌入冗余历史",
"why_bad": "上下文超载导致抓不住重点、成本飙升、准确率下降",
"fix": "只注入必要文件与最近变更,分层索引按需加载",
"detect": "单次注入 > 10 个无关文件即触发"
},
{
"id": "anti_pattern:model-is-everything",
"type": "anti_pattern",
"name": "把模型更强当系统更稳",
"why_bad": "没有编排、门禁与观测,强模型也会产生不可控波动",
"fix": "模型层 + 编排层 + 门禁/观测层三层闭环,而非只换更强模型",
"detect": "复盘只归因于『换个模型就好』且无门禁改进即触发"
},
{
"id": "anti_pattern:divergent-copies",
"type": "anti_pattern",
"name": "同一知识多份副本各自漂移",
"why_bad": "规则/图谱/文档多份并存且内容冲突,AI 加载到哪份全靠运气",
"fix": "确立单一事实源(其余派生)+ 增量化同步脚本 + 定期一致性巡检",
"detect": "同一事实在两处以上出现且可被独立编辑即触发",
"project_evidence": "2026-10-01 巡检发现:两份 project_rules.md 并存、根 modules.md 与实际规格文件不一致"
},
{
"id": "anti_pattern:memory-rot",
"type": "anti_pattern",
"name": "知识库只写不更新",
"why_bad": "知识图谱/规则过期后,AI 会依据错误事实决策,比没有知识更危险",
"fix": "开发完成强制运行 auto_record;关键节点做『图谱 vs 代码』一致性巡检",
"detect": "knowledge_graph.updated_at 落后于最近 commit 超过 7 天即触发",
"project_binding": "workflow:auto-record + node scripts/update-knowledge.mjs"
}
],
"relations": [
{"from": "method:context-engineering", "to": "principle:stateless-agent", "type": "SOLVES"},
{"from": "method:context-engineering", "to": "anti_pattern:context-overload", "type": "AVOIDS"},
{"from": "method:task-decomposition", "to": "anti_pattern:one-shot-big", "type": "AVOIDS"},
{"from": "method:task-decomposition", "to": "anti_pattern:vague-task", "type": "AVOIDS"},
{"from": "method:acceptance-criteria", "to": "anti_pattern:no-acceptance", "type": "AVOIDS"},
{"from": "method:review-diff", "to": "anti_pattern:skip-review", "type": "AVOIDS"},
{"from": "method:failure-classification", "to": "anti_pattern:model-is-everything", "type": "AVOIDS"},
{"from": "principle:single-source-of-truth", "to": "anti_pattern:divergent-copies", "type": "AVOIDS"},
{"from": "workflow:auto-record", "to": "anti_pattern:memory-rot", "type": "AVOIDS"},
{"from": "workflow:auto-record", "to": "workflow:persistent-context-load", "type": "FEEDS"},
{"from": "workflow:plan-first-loop", "to": "method:task-decomposition", "type": "USES"},
{"from": "workflow:plan-first-loop", "to": "method:plan-first", "type": "IMPLEMENTS"},
{"from": "workflow:plan-first-loop", "to": "workflow:edit-test-loop", "type": "CONTAINS"},
{"from": "workflow:plan-first-loop", "to": "workflow:auto-record", "type": "ENDS_WITH"},
{"from": "workflow:edit-test-loop", "to": "method:test-first-gate", "type": "USES"},
{"from": "method:test-first-gate", "to": "principle:enforcement-pyramid", "type": "IMPLEMENTS"},
{"from": "method:logging-standard", "to": "principle:observability-first", "type": "IMPLEMENTS"},
{"from": "method:spec-driven", "to": "method:acceptance-criteria", "type": "REQUIRES"},
{"from": "method:plan-first", "to": "method:spec-driven", "type": "RELATES_TO"},
{"from": "method:incremental-refactor", "to": "anti_pattern:one-shot-big", "type": "AVOIDS"},
{"from": "workflow:subagent-fanout", "to": "principle:context-budget", "type": "IMPLEMENTS"},
{"from": "tool:trae", "to": "workflow:plan-first-loop", "type": "BEST_AT"},
{"from": "tool:claude-code", "to": "workflow:edit-test-loop", "type": "BEST_AT"},
{"from": "tool:cursor", "to": "method:task-decomposition", "type": "NEEDS"},
{"from": "tool:codex", "to": "tool:agents-md", "type": "ORIGINATES"},
{"from": "tool:trae", "to": "tool:mcp", "type": "SUPPORTS"},
{"from": "tool:trae", "to": "workflow:auto-record", "type": "HOSTS"},
{"from": "tool:mcp", "to": "workflow:auto-record", "type": "STORES"}
]
}
+457
View File
@@ -0,0 +1,457 @@
{
"project": "追光AI",
"version": "V2.1.2",
"updated_at": "2026-10-01T09:04:53",
"entities": [
{
"type": "entity",
"entityType": "Project",
"name": "追光AI",
"observations": [
"项目名称:追光AI (zhuiguang-ai),AI 工具与技能发现/评测/社区平台",
"技术栈:Next.js 14 App Router + TypeScript(strict) + Tailwind CSS + shadcn/ui + Prisma 7.8(MariaDB adapter) + MySQL(阿里云RDS) + NextAuth v4 + Redis(ioredis)",
"版本:V2.1.2(package.json;Docker 镜像 tag zhuiguang-ai:2.1.3)",
"生产域名:https://www.zhuig.com(备用 ai.zhuig.com)",
"端口锁定:8301-8310,默认 8301",
"代码规模:154 个 API 路由(route.ts)、61 个页面(page.tsx)、90 个组件(tsx)、69 个脚本(mjs)、56 个 Prisma 模型、22 个迁移",
"本地工作副本:E:\\KF_7\\ZhuiGuangAI\\zhuiguang-ai(master)",
"2026-10-01 巡检:本地源码与服务器 /home/ubuntu/zhuiguang-ai 源码逐文件一致(493 个文件 MD5 相同)"
]
},
{
"type": "entity",
"entityType": "Infrastructure",
"name": "追光AI:服务器与基础设施",
"observations": [
"服务器:119.45.242.239(登录用户 ubuntu)",
"SSH 私钥:E:\\0.Center-S2\\S890-V3\\pem\\zg.pem(规则旧路径 C:\\SGP_KF\\ZhuiGuangAI\\zhuiguang-ai\\Pem\\zg.pem 已失效)",
"部署目录:/home/ubuntu/zhuiguang-ai",
"数据库:阿里云 RDS MySQL rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306/zhuiguang_ai",
"共享服务(禁动):redis-cache:6379、nacos:8848、minio:9000、系统 nginx:80/443",
"nginx 站点配置:/etc/nginx/sites-enabled/zhuiguang-ai → 127.0.0.1:8301",
"SSL 证书:/etc/letsencrypt/live/www.zhuig.com(自动续期)",
"邻居项目(只读):zhuiguang-quant、zhenfang、snowy-fgt、agent-system、nacos、gpsj2-webhook"
]
},
{
"type": "entity",
"entityType": "Deployment",
"name": "追光AI:容器化部署",
"observations": [
"镜像:zhuiguang-ai:2.1.3(多阶段构建 Dockerfile)",
"容器1:zhuiguang-ai-app(Next.js,端口 8301,healthcheck /api/health)",
"容器2:zhuiguang-ai-cron(supercronic 调度,healthcheck pgrep supercronic)",
"网络:network_mode: host(直接访问宿主机 Redis/Nacos/MinIO)",
"命名卷:zhuiguang-ai_bot-data / zhuiguang-ai_bot-public / zhuiguang-ai_bot-logs / zhuiguang-ai_bot-prisma",
"生产环境变量:/home/ubuntu/zhuiguang-ai/env/.env.production(chmod 600)",
"部署脚本:scripts/deploy-build.sh(docker build --build-arg DATABASE_URL)+ scripts/deploy-run.sh(docker stop/rm + docker run)",
"部署方式:非 git 拉取式,服务器仓库无 git remote",
"2026-10-01 状态:两容器均 Up 21h (healthy),/api/health 返回 database ok"
]
},
{
"type": "entity",
"entityType": "ScheduledTask",
"name": "追光AI:定时任务体系",
"observations": [
"调度器:zhuiguang-ai-cron 容器内 supercronic,配置文件 crontab.txt(容器内按 UTC 执行 = 北京时间 -8h)",
"启用任务数:19 个",
"启用清单:task1-discover-tools、task3-check-tools、task4-discover-skills(daily-discover)、task5-update-stars、task6-review-hot、daily-news、task7-news-to-community、cleanup-logs、cleanup-task-logs、reconcile-like-counts、health-check、weekly-recommend-email、grant-freeze-cards、notification-digest、newsletter-generate、competitor-monitor、trend-alert、enrich-tool-data、industry-thinktank(collect)",
"已停用:bot-activity、bot-skill-crystallize、bot-affinity-update、bot-weekly-review、bot-feedback-loop、bot-adversarial-learning-run、bot-persona-experiment-run、bot-roundtable、industry-thinktank(weekly/respond)、industry-daily(daily/respond)",
"停用原因:2026-09-05 旧『模拟真人互刷』Bot 体系退役;2026-09-26 按要求关闭全部 AI 生成功能(行业情报改为需登录访问)",
"四方同步要求:新增任务须同步 crontab.txt + seed-task-configs.mjs + health-check.mjs",
"已知漂移(2026-10-01):seed-task-configs.mjs 仍 enabled:true、health-check.mjs 仍列为期望键,会产生误告警"
]
},
{
"type": "entity",
"entityType": "Subsystem",
"name": "追光AI:数字人Bot系统",
"observations": [
"角色定义:data/bot-characters.json,当前仅 6 个『行业情报官』角色(cb_intel、fi_ins_intel、fi_stock_intel、brand_intel、ai_intel、ct_intel)",
"历史:曾为 112 个角色体系,2026-09-05 退役,改由行业情报官引擎承担",
"头像资源:public/bot-avatars 178 个 SVG;会员头像库 public/avatars 530 个 SVG",
"Bot 用户标记:User.isBot = true",
"Bot 专用表 12 张:BotConfig/BotMemory/BotDailyStat/BotSkill/BotWeeklyReview/BotPersona/BotPersonaVariant/BotPersonaExperiment/BotPersonaAssignment/BotPersonaMetric/BotAdversarialLearning/BotCrossForumAffinity"
]
},
{
"type": "entity",
"entityType": "DataModel",
"name": "追光AI:数据模型",
"observations": [
"ORM:Prisma 7.8 + PrismaMariaDb adapter(禁止直接 new PrismaClient()),datasource.url 位于 prisma.config.ts",
"模型总数:56 个(prisma/schema.prisma)",
"迁移:prisma/migrations 共 22 个",
"命名约定:模型 PascalCase + @@map(snake_case);字段 camelCase + @map(snake_case)",
"库:阿里云 RDS MySQL zhuiguang_ai(严禁 DROP/TRUNCATE/删迁移,走 migrate deploy 前向变更)"
]
},
{
"type": "entity",
"entityType": "Repository",
"name": "追光AI:代码仓库与备份",
"observations": [
"当前工作副本:E:\\KF_7\\ZhuiGuangAI\\zhuiguang-ai(master,18 个提交,~500 项未提交改动)",
"本地裸仓镜像:K:\\git-repos\\zhuiguang-ai.git(2026-10-01 新建,bare,HEAD=master)",
"裸仓校验:HEAD b44601b 与源一致、18 提交、1504 对象、git fsck 无错",
"远程现状:E:\\KF_7 的 remote『local』仍指向已不存在的 c:\\gitbf\\zhuiguang-ai.git",
"副本 E:\\ZG_Dev\\ZhuiGuangAI\\zhuiguang-ai:停在 6c78c32(2026-05-23),无 remote,历史已被 E:\\KF_7 完整包含",
"旧副本 K:\\git-repos\\zhuiguang-ai(非裸仓):停在 2026-05-23,origin 指向 E:\\ZG_Dev",
"云端 Gitea(119.45.242.239:3000,v1.27.1):本项目无仓库,无法推送"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(01-数据库)",
"observations": [
"规格文档:.trae/specs/modules/01-database.md",
"覆盖 Prisma Schema、56 个数据模型、MariaDB/RDS"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(02-API)",
"observations": [
"规格文档:.trae/specs/modules/02-api.md",
"覆盖 REST API 路由(154 个 route.ts)、认证、缓存、v1 公开 API",
"响应约定:列表 {data,total,page,pageSize};错误 {success:false,error}"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(03-页面)",
"observations": [
"规格文档:.trae/specs/modules/03-pages.md",
"Next.js 14 App Router 页面体系(61 个 page.tsx)"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(04-组件与样式)",
"observations": [
"规格文档:.trae/specs/modules/04-components-and-styles.md",
"UI 组件库(90 个 tsx)、Tailwind CSS、shadcn/ui"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(05-AI发现)",
"observations": [
"规格文档:.trae/specs/modules/05-ai-discovery.md",
"提示词+JSON 粘贴导入模式,不调用外部 AI API"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(06-认证)",
"observations": [
"规格文档:.trae/specs/modules/06-auth.md",
"NextAuth v4 Credentials、RBAC、API Key 认证、middleware 保护 /admin 与 /api/admin"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(07-库与脚本)",
"observations": [
"规格文档:.trae/specs/modules/07-lib-and-scripts.md",
"src/lib 纯函数库 + scripts/ 定时任务与运维脚本(69 个 mjs)"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(08-社区)",
"observations": [
"规格文档:.trae/specs/modules/08-community.md",
"论坛、帖子、评论、签到、排行榜、举报、通知"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(09-运维与部署)",
"observations": [
"规格文档:.trae/specs/modules/09-devops.md",
"容器化部署、crontab、备份、监控;注意该文档仍写『本地Git同步目录 C:\\gitbf』,与工作区规则 K:\\git-repos 不一致"
]
},
{
"type": "entity",
"entityType": "Module",
"name": "追光AI:模块(10-Bot系统)",
"observations": [
"规格文档:.trae/specs/modules/10-bot-system.md",
"数字人 Bot 引擎;旧体系已于 2026-09-05 退役,现为行业情报官引擎"
]
},
{
"type": "entity",
"entityType": "Convention",
"name": "追光AI:开发约定(导航与图片)",
"observations": [
"页面内导航必须用 next/link <Link>,禁止 button+router.push;仅外链用 <a target=_blank rel=noopener noreferrer>",
"图片强制使用 src/components/ui/LazyImage.tsx,禁止原生 <img>",
"首屏 LCP 图片加 priority,每页最多 1-2 张",
"长列表(>50) 用 VirtualList 虚拟滚动"
]
},
{
"type": "entity",
"entityType": "Convention",
"name": "追光AI:开发约定(数据获取与性能)",
"observations": [
"含 generateStaticParams 的页面必须声明 export const revalidate 与 dynamicParams",
"高频读接口使用 cachedResponse 缓存,写入后 invalidateCache 失效",
"详情页用 include/_count 一次取关联计数,禁止 N+1",
"互不依赖的 Prisma 查询必须 Promise.all 并行",
"非首屏组件用 next/dynamic 懒加载"
]
},
{
"type": "entity",
"entityType": "Convention",
"name": "追光AI:开发约定(安全与校验)",
"observations": [
"API 路由必须用 Zod 校验请求体;批量上限:工具/技能导入 100、新闻 50、发布 500",
"后台写操作 API 必须调用 requireAdmin()",
"禁止硬编码密钥,全部走环境变量且 .env.example 同步",
"CSP script-src 必须含 'unsafe-inline'(Next.js App Router 水合依赖),禁止 'unsafe-eval'",
"外键回退值禁止硬编码魔法数字,须查库取有效 ID"
]
},
{
"type": "entity",
"entityType": "Issue",
"name": "追光AI:巡检问题与处理(2026-10-01)",
"observations": [
"规则文件 SSH 密钥路径失效:写 C:\\SGP_KF\\...\\Pem\\zg.pem,实际 E:\\0.Center-S2\\S890-V3\\pem\\zg.pem",
"双份 project_rules.md 并存且冲突:工作区根 908 行 / 项目内 357 行",
"规格文档不自洽:根 modules.md 索引 10 个模块但实际仅 5 个文件,且与项目内 17 个文件命名体系不同",
"定时任务四方同步漂移:crontab 已停用 vs seed-task-configs 仍 enabled:true vs health-check 仍期望",
"版本号不一致:package.json V2.1.2 vs 镜像 tag zhuiguang-ai:2.1.3",
"规则中『112 个 Bot 角色』已过期,实际 6 个行业情报官",
"本地约 500 项未提交改动未进入任何备份",
"Gitea 无本项目仓库,且本地 remote 指向不存在的 c:\\gitbf",
"✅ 2026-10-01 已处理(规则侧):SSH 密钥路径、工作区/裸仓镜像/远端现状、Bot 角色数(112→6)、头像数、seed 任务数(6→29)、定时任务表(改为 UTC 并标注停用)、KG 同步方式,均已同步进两份 project_rules.md",
"✅ 2026-10-01 已处理(备份侧):新建裸仓 K:\\git-repos\\zhuiguang-ai.git 并推入 master(18 提交/1504 对象/fsck 无错)",
"✅ 2026-10-01 已处理(图谱侧):建立 .trae/knowledge_graph.jsonl(21 实体/23 关系) 与渲染版 .trae/knowledge_graph.json",
"⏳ 待处理:.trae/mcp.json 为受保护文件,需人工写入 Knowledge Graph Memory 配置",
"⏳ 待处理:本地 remote『local』仍指向不存在的 c:\\gitbf;约 500 项未提交改动未进备份",
"⏳ 待处理:规格文档不自洽(根 modules.md 索引 10 个模块但仅存 5 个文件;根/项目两套命名体系不同,05 一边 auth 一边 ai-discovery)",
"⏳ 待处理:定时任务三方漂移(crontab 停用 vs seed-task-configs enabled:true vs health-check 期望键)",
"⏳ 待处理:版本号不一致(package.json V2.1.2 vs 镜像 tag zhuiguang-ai:2.1.3)",
"⏳ 待处理:双份 project_rules.md 仍并存未合并(工作区根 908 行 / 项目内约 380 行)"
]
},
{
"type": "entity",
"entityType": "Convention",
"name": "追光AI:AI协作知识体系",
"observations": [
"四层结构:L0 方法层 .trae/ai_coding_knowledge.json(AI 编程方法/工具/工作流/原则/反模式,机器可解析)|L1 协作层 .trae/rules/README.md + ai_agent_rules.md + ai_collaboration.md|L2 工程层 .trae/rules/project_rules.md|L3 事实层 .trae/knowledge_graph.jsonl/.json + .trae/knowledge/*",
"自动化记录:node scripts/update-knowledge.mjs(npm run knowledge:update)— 捕获能力特征→.trae/knowledge/capabilities.json、开发过程→dev_process.jsonl、模型成果→artifacts.jsonl,并增量 upsert 知识图谱 json+jsonl",
"触发方式:git post-commit 钩子(后台静默,不阻断提交)+ 任务收尾手动执行;换机需按 ai_collaboration.md §6 重装钩子",
"幂等保证:能力快照按实体名整条替换;开发过程按提交 hash 去重;模型成果按(日期+head提交)去重;旧实体永不删除",
"单一事实源:同一事实只存一处,其余位置只写引用(禁止复制,见反模式 divergent-copies)",
"入口文档:.trae/rules/README.md(§4 自动化闭环 / §5 维护规则)"
]
},
{
"type": "entity",
"entityType": "Capability",
"name": "追光AI:能力快照",
"observations": [
"版本: V2.1.2",
"代码规模: API 路由 154 / 页面 61 / 组件 90 / hooks 1 / lib 38",
"数据层: Prisma 模型 56 个、迁移 22 个(最新 20260605010000_forum_phase5、20260607000000_bot_persona_ab_test、20260607010000_bot_adversarial_learning、20260824170000_add_public_share_and_screenshots、20260905180000_add_thinktank)",
"脚本: 79 个(含定时任务与运维脚本)",
"定时任务: 启用 19 个 —— task1-discover-tools、task3-check-tools、daily-discover、task5-update-stars、task6-review-hot、daily-news、task7-news-to-community、cleanup-logs、cleanup-task-logs、reconcile-like-counts、health-check、weekly-recommend-email、grant-freeze-cards、notification-digest、newsletter-generate、competitor-monitor、trend-alert、enrich-tool-data、industry-thinktank",
"数字人角色: 6 个(cb_intel、fi_ins_intel、fi_stock_intel、brand_intel、ai_intel、ct_intel)",
"依赖: dependencies 26 / devDependencies 15(next 14.2.35、prisma ^7.8.0、react ^18)",
"快照时间: 2026-10-01T09:04:53(由 scripts/update-knowledge.mjs 自动生成,勿手工编辑)"
]
},
{
"type": "entity",
"entityType": "fact",
"name": "开发记录:2026-10-01",
"observations": [
"记录时间: 2026-10-01T09:04:53",
"本次交付: b44601bb feat: 全面优化系统 - 修复task1语法错误 + Bot超时保护 + 熔断重试 + Redis缓存 + SEO + 日志清理 + 内容质量评估(改动文件 12 个、12 files changed, 399 insertions(+), 12 deletions(-))",
"能力基线: API 154 / 页面 61 / 组件 90 / 模型 56 / 迁移 22 / 脚本 79 / 定时任务 19",
"b44601bb 2026-06-13 feat: 全面优化系统 - 修复task1语法错误 + Bot超时保护 + 熔断重试 + Redis缓存 + SEO + 日志清理 + 内容质量评估",
"a0a60ee9 2026-06-12 fix: 修复6个use client页面metadata冲突,创建layout.tsx承载metadata",
"4c325c86 2026-06-12 全项目扫描修复: Docker数据卷修复+安全requireAdmin+12页SEO+API白名单+脚本超时+常量提取+假数据删除",
"464924ae 2026-06-01 fix: 修复bot-activity两个致命BUG + 添加3个passerby bot补充板块覆盖 + 减少GoogleNews超时",
"a9145c4d 2026-06-01 修复自动化任务:bot-activity上线 + task7板块slug修复 + cron强化",
"维护: 运行 npm run knowledge:update 刷新本记录"
]
}
],
"relations": [
{
"type": "relation",
"relationType": "HAS_INFRASTRUCTURE",
"from": "追光AI",
"to": "追光AI:服务器与基础设施"
},
{
"type": "relation",
"relationType": "HAS_DEPLOYMENT",
"from": "追光AI",
"to": "追光AI:容器化部署"
},
{
"type": "relation",
"relationType": "HAS_SCHEDULED_TASKS",
"from": "追光AI",
"to": "追光AI:定时任务体系"
},
{
"type": "relation",
"relationType": "HAS_SUBSYSTEM",
"from": "追光AI",
"to": "追光AI:数字人Bot系统"
},
{
"type": "relation",
"relationType": "HAS_DATAMODEL",
"from": "追光AI",
"to": "追光AI:数据模型"
},
{
"type": "relation",
"relationType": "HAS_REPOSITORY",
"from": "追光AI",
"to": "追光AI:代码仓库与备份"
},
{
"type": "relation",
"relationType": "HAS_ISSUE",
"from": "追光AI",
"to": "追光AI:巡检问题与处理(2026-10-01)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(01-数据库)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(02-API)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(03-页面)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(04-组件与样式)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(05-AI发现)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(06-认证)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(07-库与脚本)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(08-社区)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(09-运维与部署)"
},
{
"type": "relation",
"relationType": "HAS_MODULE",
"from": "追光AI",
"to": "追光AI:模块(10-Bot系统)"
},
{
"type": "relation",
"relationType": "HAS_CONVENTION",
"from": "追光AI",
"to": "追光AI:开发约定(导航与图片)"
},
{
"type": "relation",
"relationType": "HAS_CONVENTION",
"from": "追光AI",
"to": "追光AI:开发约定(数据获取与性能)"
},
{
"type": "relation",
"relationType": "HAS_CONVENTION",
"from": "追光AI",
"to": "追光AI:开发约定(安全与校验)"
},
{
"type": "relation",
"relationType": "DEPLOYED_ON",
"from": "追光AI:容器化部署",
"to": "追光AI:服务器与基础设施"
},
{
"type": "relation",
"relationType": "BACKED_UP_TO",
"from": "追光AI:代码仓库与备份",
"to": "K:\\git-repos\\zhuiguang-ai.git"
},
{
"type": "relation",
"relationType": "RUNS_ON",
"from": "追光AI:定时任务体系",
"to": "追光AI:容器化部署"
},
{
"type": "relation",
"relationType": "HAS_CAPABILITY",
"from": "追光AI",
"to": "追光AI:能力快照"
},
{
"type": "relation",
"relationType": "HAS_DEV_RECORD",
"from": "追光AI",
"to": "开发记录:2026-10-01"
},
{
"type": "relation",
"relationType": "HAS_CONVENTION",
"from": "追光AI",
"to": "追光AI:AI协作知识体系"
}
]
}
+50
View File
@@ -0,0 +1,50 @@
{"type":"entity","entityType":"Project","name":"追光AI","observations":["项目名称:追光AI (zhuiguang-ai),AI 工具与技能发现/评测/社区平台","技术栈:Next.js 14 App Router + TypeScript(strict) + Tailwind CSS + shadcn/ui + Prisma 7.8(MariaDB adapter) + MySQL(阿里云RDS) + NextAuth v4 + Redis(ioredis)","版本:V2.1.2(package.json;Docker 镜像 tag zhuiguang-ai:2.1.3)","生产域名:https://www.zhuig.com(备用 ai.zhuig.com)","端口锁定:8301-8310,默认 8301","代码规模:154 个 API 路由(route.ts)、61 个页面(page.tsx)、90 个组件(tsx)、69 个脚本(mjs)、56 个 Prisma 模型、22 个迁移","本地工作副本:E:\\KF_7\\ZhuiGuangAI\\zhuiguang-ai(master)","2026-10-01 巡检:本地源码与服务器 /home/ubuntu/zhuiguang-ai 源码逐文件一致(493 个文件 MD5 相同)"]}
{"type":"entity","entityType":"Infrastructure","name":"追光AI:服务器与基础设施","observations":["服务器:119.45.242.239(登录用户 ubuntu)","SSH 私钥:E:\\0.Center-S2\\S890-V3\\pem\\zg.pem(规则旧路径 C:\\SGP_KF\\ZhuiGuangAI\\zhuiguang-ai\\Pem\\zg.pem 已失效)","部署目录:/home/ubuntu/zhuiguang-ai","数据库:阿里云 RDS MySQL rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306/zhuiguang_ai","共享服务(禁动):redis-cache:6379、nacos:8848、minio:9000、系统 nginx:80/443","nginx 站点配置:/etc/nginx/sites-enabled/zhuiguang-ai → 127.0.0.1:8301","SSL 证书:/etc/letsencrypt/live/www.zhuig.com(自动续期)","邻居项目(只读):zhuiguang-quant、zhenfang、snowy-fgt、agent-system、nacos、gpsj2-webhook"]}
{"type":"entity","entityType":"Deployment","name":"追光AI:容器化部署","observations":["镜像:zhuiguang-ai:2.1.3(多阶段构建 Dockerfile)","容器1:zhuiguang-ai-app(Next.js,端口 8301,healthcheck /api/health)","容器2:zhuiguang-ai-cron(supercronic 调度,healthcheck pgrep supercronic)","网络:network_mode: host(直接访问宿主机 Redis/Nacos/MinIO)","命名卷:zhuiguang-ai_bot-data / zhuiguang-ai_bot-public / zhuiguang-ai_bot-logs / zhuiguang-ai_bot-prisma","生产环境变量:/home/ubuntu/zhuiguang-ai/env/.env.production(chmod 600)","部署脚本:scripts/deploy-build.sh(docker build --build-arg DATABASE_URL)+ scripts/deploy-run.sh(docker stop/rm + docker run)","部署方式:非 git 拉取式,服务器仓库无 git remote","2026-10-01 状态:两容器均 Up 21h (healthy),/api/health 返回 database ok"]}
{"type":"entity","entityType":"ScheduledTask","name":"追光AI:定时任务体系","observations":["调度器:zhuiguang-ai-cron 容器内 supercronic,配置文件 crontab.txt(容器内按 UTC 执行 = 北京时间 -8h)","启用任务数:19 个","启用清单:task1-discover-tools、task3-check-tools、task4-discover-skills(daily-discover)、task5-update-stars、task6-review-hot、daily-news、task7-news-to-community、cleanup-logs、cleanup-task-logs、reconcile-like-counts、health-check、weekly-recommend-email、grant-freeze-cards、notification-digest、newsletter-generate、competitor-monitor、trend-alert、enrich-tool-data、industry-thinktank(collect)","已停用:bot-activity、bot-skill-crystallize、bot-affinity-update、bot-weekly-review、bot-feedback-loop、bot-adversarial-learning-run、bot-persona-experiment-run、bot-roundtable、industry-thinktank(weekly/respond)、industry-daily(daily/respond)","停用原因:2026-09-05 旧『模拟真人互刷』Bot 体系退役;2026-09-26 按要求关闭全部 AI 生成功能(行业情报改为需登录访问)","四方同步要求:新增任务须同步 crontab.txt + seed-task-configs.mjs + health-check.mjs","已知漂移(2026-10-01):seed-task-configs.mjs 仍 enabled:true、health-check.mjs 仍列为期望键,会产生误告警"]}
{"type":"entity","entityType":"Subsystem","name":"追光AI:数字人Bot系统","observations":["角色定义:data/bot-characters.json,当前仅 6 个『行业情报官』角色(cb_intel、fi_ins_intel、fi_stock_intel、brand_intel、ai_intel、ct_intel)","历史:曾为 112 个角色体系,2026-09-05 退役,改由行业情报官引擎承担","头像资源:public/bot-avatars 178 个 SVG;会员头像库 public/avatars 530 个 SVG","Bot 用户标记:User.isBot = true","Bot 专用表 12 张:BotConfig/BotMemory/BotDailyStat/BotSkill/BotWeeklyReview/BotPersona/BotPersonaVariant/BotPersonaExperiment/BotPersonaAssignment/BotPersonaMetric/BotAdversarialLearning/BotCrossForumAffinity"]}
{"type":"entity","entityType":"DataModel","name":"追光AI:数据模型","observations":["ORM:Prisma 7.8 + PrismaMariaDb adapter(禁止直接 new PrismaClient()),datasource.url 位于 prisma.config.ts","模型总数:56 个(prisma/schema.prisma)","迁移:prisma/migrations 共 22 个","命名约定:模型 PascalCase + @@map(snake_case);字段 camelCase + @map(snake_case)","库:阿里云 RDS MySQL zhuiguang_ai(严禁 DROP/TRUNCATE/删迁移,走 migrate deploy 前向变更)"]}
{"type":"entity","entityType":"Repository","name":"追光AI:代码仓库与备份","observations":["当前工作副本:E:\\KF_7\\ZhuiGuangAI\\zhuiguang-ai(master,18 个提交,~500 项未提交改动)","本地裸仓镜像:K:\\git-repos\\zhuiguang-ai.git(2026-10-01 新建,bare,HEAD=master)","裸仓校验:HEAD b44601b 与源一致、18 提交、1504 对象、git fsck 无错","远程现状:E:\\KF_7 的 remote『local』仍指向已不存在的 c:\\gitbf\\zhuiguang-ai.git","副本 E:\\ZG_Dev\\ZhuiGuangAI\\zhuiguang-ai:停在 6c78c32(2026-05-23),无 remote,历史已被 E:\\KF_7 完整包含","旧副本 K:\\git-repos\\zhuiguang-ai(非裸仓):停在 2026-05-23,origin 指向 E:\\ZG_Dev","云端 Gitea(119.45.242.239:3000,v1.27.1):本项目无仓库,无法推送"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(01-数据库)","observations":["规格文档:.trae/specs/modules/01-database.md","覆盖 Prisma Schema、56 个数据模型、MariaDB/RDS"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(02-API)","observations":["规格文档:.trae/specs/modules/02-api.md","覆盖 REST API 路由(154 个 route.ts)、认证、缓存、v1 公开 API","响应约定:列表 {data,total,page,pageSize};错误 {success:false,error}"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(03-页面)","observations":["规格文档:.trae/specs/modules/03-pages.md","Next.js 14 App Router 页面体系(61 个 page.tsx)"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(04-组件与样式)","observations":["规格文档:.trae/specs/modules/04-components-and-styles.md","UI 组件库(90 个 tsx)、Tailwind CSS、shadcn/ui"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(05-AI发现)","observations":["规格文档:.trae/specs/modules/05-ai-discovery.md","提示词+JSON 粘贴导入模式,不调用外部 AI API"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(06-认证)","observations":["规格文档:.trae/specs/modules/06-auth.md","NextAuth v4 Credentials、RBAC、API Key 认证、middleware 保护 /admin 与 /api/admin"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(07-库与脚本)","observations":["规格文档:.trae/specs/modules/07-lib-and-scripts.md","src/lib 纯函数库 + scripts/ 定时任务与运维脚本(69 个 mjs)"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(08-社区)","observations":["规格文档:.trae/specs/modules/08-community.md","论坛、帖子、评论、签到、排行榜、举报、通知"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(09-运维与部署)","observations":["规格文档:.trae/specs/modules/09-devops.md","容器化部署、crontab、备份、监控;注意该文档仍写『本地Git同步目录 C:\\gitbf』,与工作区规则 K:\\git-repos 不一致"]}
{"type":"entity","entityType":"Module","name":"追光AI:模块(10-Bot系统)","observations":["规格文档:.trae/specs/modules/10-bot-system.md","数字人 Bot 引擎;旧体系已于 2026-09-05 退役,现为行业情报官引擎"]}
{"type":"entity","entityType":"Convention","name":"追光AI:开发约定(导航与图片)","observations":["页面内导航必须用 next/link <Link>,禁止 button+router.push;仅外链用 <a target=_blank rel=noopener noreferrer>","图片强制使用 src/components/ui/LazyImage.tsx,禁止原生 <img>","首屏 LCP 图片加 priority,每页最多 1-2 张","长列表(>50) 用 VirtualList 虚拟滚动"]}
{"type":"entity","entityType":"Convention","name":"追光AI:开发约定(数据获取与性能)","observations":["含 generateStaticParams 的页面必须声明 export const revalidate 与 dynamicParams","高频读接口使用 cachedResponse 缓存,写入后 invalidateCache 失效","详情页用 include/_count 一次取关联计数,禁止 N+1","互不依赖的 Prisma 查询必须 Promise.all 并行","非首屏组件用 next/dynamic 懒加载"]}
{"type":"entity","entityType":"Convention","name":"追光AI:开发约定(安全与校验)","observations":["API 路由必须用 Zod 校验请求体;批量上限:工具/技能导入 100、新闻 50、发布 500","后台写操作 API 必须调用 requireAdmin()","禁止硬编码密钥,全部走环境变量且 .env.example 同步","CSP script-src 必须含 'unsafe-inline'(Next.js App Router 水合依赖),禁止 'unsafe-eval'","外键回退值禁止硬编码魔法数字,须查库取有效 ID"]}
{"type":"entity","entityType":"Issue","name":"追光AI:巡检问题与处理(2026-10-01)","observations":["规则文件 SSH 密钥路径失效:写 C:\\SGP_KF\\...\\Pem\\zg.pem,实际 E:\\0.Center-S2\\S890-V3\\pem\\zg.pem","双份 project_rules.md 并存且冲突:工作区根 908 行 / 项目内 357 行","规格文档不自洽:根 modules.md 索引 10 个模块但实际仅 5 个文件,且与项目内 17 个文件命名体系不同","定时任务四方同步漂移:crontab 已停用 vs seed-task-configs 仍 enabled:true vs health-check 仍期望","版本号不一致:package.json V2.1.2 vs 镜像 tag zhuiguang-ai:2.1.3","规则中『112 个 Bot 角色』已过期,实际 6 个行业情报官","本地约 500 项未提交改动未进入任何备份","Gitea 无本项目仓库,且本地 remote 指向不存在的 c:\\gitbf","✅ 2026-10-01 已处理(规则侧):SSH 密钥路径、工作区/裸仓镜像/远端现状、Bot 角色数(112→6)、头像数、seed 任务数(6→29)、定时任务表(改为 UTC 并标注停用)、KG 同步方式,均已同步进两份 project_rules.md","✅ 2026-10-01 已处理(备份侧):新建裸仓 K:\\git-repos\\zhuiguang-ai.git 并推入 master(18 提交/1504 对象/fsck 无错)","✅ 2026-10-01 已处理(图谱侧):建立 .trae/knowledge_graph.jsonl(21 实体/23 关系) 与渲染版 .trae/knowledge_graph.json","⏳ 待处理:.trae/mcp.json 为受保护文件,需人工写入 Knowledge Graph Memory 配置","⏳ 待处理:本地 remote『local』仍指向不存在的 c:\\gitbf;约 500 项未提交改动未进备份","⏳ 待处理:规格文档不自洽(根 modules.md 索引 10 个模块但仅存 5 个文件;根/项目两套命名体系不同,05 一边 auth 一边 ai-discovery)","⏳ 待处理:定时任务三方漂移(crontab 停用 vs seed-task-configs enabled:true vs health-check 期望键)","⏳ 待处理:版本号不一致(package.json V2.1.2 vs 镜像 tag zhuiguang-ai:2.1.3)","⏳ 待处理:双份 project_rules.md 仍并存未合并(工作区根 908 行 / 项目内约 380 行)","✅ 2026-10-01 已处理(知识库侧):建立 AI 编程效率知识库 .trae/ai_coding_knowledge.json、规则体系四层索引 .trae/rules/README.md + ai_agent_rules.md + ai_collaboration.md、自动化记录 scripts/update-knowledge.mjs(能力特征/开发过程/模型成果)+ npm run knowledge:update + git post-commit 钩子"]}
{"type":"entity","entityType":"Convention","name":"追光AI:AI协作知识体系","observations":["四层结构:L0 方法层 .trae/ai_coding_knowledge.json(AI 编程方法/工具/工作流/原则/反模式,机器可解析)|L1 协作层 .trae/rules/README.md + ai_agent_rules.md + ai_collaboration.md|L2 工程层 .trae/rules/project_rules.md|L3 事实层 .trae/knowledge_graph.jsonl/.json + .trae/knowledge/*","自动化记录:node scripts/update-knowledge.mjs(npm run knowledge:update)— 捕获能力特征→.trae/knowledge/capabilities.json、开发过程→dev_process.jsonl、模型成果→artifacts.jsonl,并增量 upsert 知识图谱 json+jsonl","触发方式:git post-commit 钩子(后台静默,不阻断提交)+ 任务收尾手动执行;换机需按 ai_collaboration.md §6 重装钩子","幂等保证:能力快照按实体名整条替换;开发过程按提交 hash 去重;模型成果按(日期+head提交)去重;旧实体永不删除","单一事实源:同一事实只存一处,其余位置只写引用(禁止复制,见反模式 divergent-copies)","入口文档:.trae/rules/README.md(§4 自动化闭环 / §5 维护规则)"]}
{"type":"entity","entityType":"Capability","name":"追光AI:能力快照","observations":["版本: V2.1.2","代码规模: API 路由 154 / 页面 61 / 组件 90 / hooks 1 / lib 38","数据层: Prisma 模型 56 个、迁移 22 个(最新 20260605010000_forum_phase5、20260607000000_bot_persona_ab_test、20260607010000_bot_adversarial_learning、20260824170000_add_public_share_and_screenshots、20260905180000_add_thinktank)","脚本: 79 个(含定时任务与运维脚本)","定时任务: 启用 19 个 —— task1-discover-tools、task3-check-tools、daily-discover、task5-update-stars、task6-review-hot、daily-news、task7-news-to-community、cleanup-logs、cleanup-task-logs、reconcile-like-counts、health-check、weekly-recommend-email、grant-freeze-cards、notification-digest、newsletter-generate、competitor-monitor、trend-alert、enrich-tool-data、industry-thinktank","数字人角色: 6 个(cb_intel、fi_ins_intel、fi_stock_intel、brand_intel、ai_intel、ct_intel)","依赖: dependencies 26 / devDependencies 15(next 14.2.35、prisma ^7.8.0、react ^18)","快照时间: 2026-10-01T09:04:53(由 scripts/update-knowledge.mjs 自动生成,勿手工编辑)"]}
{"type":"entity","entityType":"fact","name":"开发记录:2026-10-01","observations":["记录时间: 2026-10-01T09:04:53","本次交付: b44601bb feat: 全面优化系统 - 修复task1语法错误 + Bot超时保护 + 熔断重试 + Redis缓存 + SEO + 日志清理 + 内容质量评估(改动文件 12 个、12 files changed, 399 insertions(+), 12 deletions(-))","能力基线: API 154 / 页面 61 / 组件 90 / 模型 56 / 迁移 22 / 脚本 79 / 定时任务 19","b44601bb 2026-06-13 feat: 全面优化系统 - 修复task1语法错误 + Bot超时保护 + 熔断重试 + Redis缓存 + SEO + 日志清理 + 内容质量评估","a0a60ee9 2026-06-12 fix: 修复6个use client页面metadata冲突,创建layout.tsx承载metadata","4c325c86 2026-06-12 全项目扫描修复: Docker数据卷修复+安全requireAdmin+12页SEO+API白名单+脚本超时+常量提取+假数据删除","464924ae 2026-06-01 fix: 修复bot-activity两个致命BUG + 添加3个passerby bot补充板块覆盖 + 减少GoogleNews超时","a9145c4d 2026-06-01 修复自动化任务:bot-activity上线 + task7板块slug修复 + cron强化","维护: 运行 npm run knowledge:update 刷新本记录"]}
{"type":"relation","relationType":"HAS_INFRASTRUCTURE","from":"追光AI","to":"追光AI:服务器与基础设施"}
{"type":"relation","relationType":"HAS_DEPLOYMENT","from":"追光AI","to":"追光AI:容器化部署"}
{"type":"relation","relationType":"HAS_SCHEDULED_TASKS","from":"追光AI","to":"追光AI:定时任务体系"}
{"type":"relation","relationType":"HAS_SUBSYSTEM","from":"追光AI","to":"追光AI:数字人Bot系统"}
{"type":"relation","relationType":"HAS_DATAMODEL","from":"追光AI","to":"追光AI:数据模型"}
{"type":"relation","relationType":"HAS_REPOSITORY","from":"追光AI","to":"追光AI:代码仓库与备份"}
{"type":"relation","relationType":"HAS_ISSUE","from":"追光AI","to":"追光AI:巡检问题与处理(2026-10-01)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(01-数据库)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(02-API)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(03-页面)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(04-组件与样式)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(05-AI发现)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(06-认证)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(07-库与脚本)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(08-社区)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(09-运维与部署)"}
{"type":"relation","relationType":"HAS_MODULE","from":"追光AI","to":"追光AI:模块(10-Bot系统)"}
{"type":"relation","relationType":"HAS_CONVENTION","from":"追光AI","to":"追光AI:开发约定(导航与图片)"}
{"type":"relation","relationType":"HAS_CONVENTION","from":"追光AI","to":"追光AI:开发约定(数据获取与性能)"}
{"type":"relation","relationType":"HAS_CONVENTION","from":"追光AI","to":"追光AI:开发约定(安全与校验)"}
{"type":"relation","relationType":"DEPLOYED_ON","from":"追光AI:容器化部署","to":"追光AI:服务器与基础设施"}
{"type":"relation","relationType":"BACKED_UP_TO","from":"追光AI:代码仓库与备份","to":"K:\\git-repos\\zhuiguang-ai.git"}
{"type":"relation","relationType":"RUNS_ON","from":"追光AI:定时任务体系","to":"追光AI:容器化部署"}
{"type":"relation","relationType":"HAS_CAPABILITY","from":"追光AI","to":"追光AI:能力快照"}
{"type":"relation","relationType":"HAS_DEV_RECORD","from":"追光AI","to":"开发记录:2026-10-01"}
{"type":"relation","relationType":"HAS_CONVENTION","from":"追光AI","to":"追光AI:AI协作知识体系"}
+61
View File
@@ -0,0 +1,61 @@
# 追光AI — 规则体系索引
> 版本: v1.0 | 更新: 2026-10-01 | 所有 `.trae` 内容均在代码仓库 `zhuiguang-ai/.trae/` 内,跟着 git 走
## 一、这套规则解决什么问题
AI 代理**每次会话都是无状态的**(不记得上次讨论)。规则与知识库的唯一作用,就是把跨会话的持久上下文注入进来。
没有它们,AI 每次都会重新问、重新猜、重复踩坑 —— 迭代效率无法复利。
## 二、四层结构(单一事实源,互不复制)
| 层 | 载体 | 内容 | 何时加载 |
|----|------|------|---------|
| **L0 方法论** | `.trae/ai_coding_knowledge.json` | AI 编程效率的方法/工具/工作流/原则/反模式(**机器可解析**,不写业务) | 每次任务开始前 |
| **L1 协作规则** | `.trae/rules/ai_agent_rules.md`<br>`.trae/rules/ai_collaboration.md` | 启动加载顺序、小任务豁免、Plan-first 流程、验收标准、完成自动记录 | 每次任务开始前 / 收尾时 |
| **L2 工程标准** | `.trae/rules/project_rules.md` | 技术栈、数据获取、导航图片、无障碍、安全、定时任务、部署等硬规范 | 写代码时按需 |
| **L3 项目事实** | `.trae/knowledge_graph.jsonl`(MCP 源)<br>`.trae/knowledge_graph.json`(渲染版)<br>`.trae/knowledge/*`(能力/过程/成果自动记录) | 模块、部署、定时任务、仓库、约定、巡检问题等**事实** | 需要项目具体知识时 |
> ⚠️ **禁止跨层复制**:同一事实只允许有一个权威载体,其余位置只写引用。复制必然漂移(见反模式 `divergent-copies`)。
## 三、快速导航
| 我要… | 读哪个 |
|-------|--------|
| 开始一个开发任务 | [ai_agent_rules.md](ai_agent_rules.md) §0 |
| 知道怎么和 AI 协作、怎么算做完 | [ai_collaboration.md](ai_collaboration.md) |
| 查 AI 编程方法论 / 选工具 | `.trae/ai_coding_knowledge.json` |
| 查项目某个模块/部署/定时任务的事实 | `.trae/knowledge_graph.jsonl` |
| 查编码硬规范(安全/API/图片/无障碍) | [project_rules.md](project_rules.md) |
| 部署、运维、容器 | [project_rules.md](project_rules.md) §14–15(工程侧见 [specs/modules/09-devops.md](../specs/modules/09-devops.md)) |
## 四、自动化闭环
```
开发完成
│
├─ npm run knowledge:update ← 手动/收尾执行
└─ git post-commit hook ← 本机自动触发
│
▼
scripts/update-knowledge.mjs
│
├─ .trae/knowledge/capabilities.json 能力特征(API/页面/组件/模型/脚本/任务/依赖)
├─ .trae/knowledge/dev_process.jsonl 开发过程(按提交追加)
├─ .trae/knowledge/artifacts.jsonl 模型成果(迁移/脚本/提交统计)
└─ .trae/knowledge_graph.json / .jsonl 事实层增量 upsert
```
## 五、维护规则
1. **方法/工具层变化** → 改 `.trae/ai_coding_knowledge.json`,同步 [ai_collaboration.md](ai_collaboration.md)
2. **协作流程变化** → 改 [ai_collaboration.md](ai_collaboration.md),同步更新 `ai_coding_knowledge.json` 中对应 `workflow:*`
3. **项目事实变化** → 跑 `npm run knowledge:update`,或直接维护 `.trae/knowledge_graph.jsonl`
4. **工程标准变化** → 改 [project_rules.md](project_rules.md)
5. **废弃而非删除**:用 `> **已废弃 (date)**` 标注并写明替代方案,保留至少一个大版本周期
## 六、版本历史
| 日期 | 版本 | 变更 |
|------|------|------|
| 2026-10-01 | v1.0 | 初始版本:建立四层结构;新增 AI 编程知识库、AI 代理规则、AI 协作流程、自动记录脚本 |
+88
View File
@@ -0,0 +1,88 @@
# AI Agent 规则(启动与查询)
> 版本: v1.0 | 更新: 2026-10-01
> 相关: [README.md](README.md) · [ai_collaboration.md](ai_collaboration.md) · `../ai_coding_knowledge.json`
目的:让 AI 在本项目中**每次任务开始前按固定顺序加载上下文**,开工前就知道方法、规范与既有事实,避免重复交代与重复踩坑。
---
## §0 启动加载顺序(最高优先级)
```
1. .trae/rules/README.md ← 索引:先看有哪些层
2. .trae/ai_coding_knowledge.json ← 方法层:按 entity.type 过滤(principle/method/workflow/anti_pattern/tool)
3. .trae/rules/ai_collaboration.md ← 流程层:Plan-first / 验收标准 / 完成自动记录
4. .trae/knowledge_graph.jsonl ← 事实层:按 name / observations 关键词检索相关实体(按需,不要全量读)
5. .trae/rules/project_rules.md ← 工程标准:与本次改动相关的章节
6. 具体源码(只读相关文件)
```
**原则**:上下文是预算(见 `principle:context-budget`)。第 4、5 步**按需**加载,不要一次性灌入全部历史。
## §1 小任务豁免清单
以下任务**无需**走完整启动流程,可直接执行:
| 豁免类型 | 示例 |
|---------|------|
| 拼写修正 | 修改变量名拼写 |
| 注释/文案 | 增删注释、改中文提示文案 |
| 格式调整 | 缩进、空行、引号风格 |
| 单值配置 | 改端口号、超时时间等单个常量 |
| 纯查询 | 「XX 在哪」「XX 是什么意思」(不改代码) |
| 已给完整方案 | 用户已提供明确的文件+改法 |
## §2 非小任务:强制先查知识库
不属于 §1 的任务,开工前**必须**先检索事实层:
```
步骤1 读 .trae/rules/README.md 确定应查哪一层
步骤2 在 .trae/knowledge_graph.jsonl 中按关键词检索(name / observations)
优先匹配:实体名 > observations 关键词 > entityType 批量
步骤3 命中则读取该实体 observations,确认规范/部署/踩坑/约束
步骤4 再开始 Explore → Plan → Implement → Verify(见 ai_collaboration.md)
```
### 检索优先级
| 优先级 | 方式 | 场景 |
|--------|------|------|
| 1 | 实体名精确匹配(如 `追光AI:容器化部署`) | 已知目标实体 |
| 2 | observations 关键词(如 `cron`、`8301`、`Prisma`) | 知道主题 |
| 3 | entityType 批量(如全部 `Issue`、全部 `ScheduledTask`) | 需要某类全貌 |
### 若 Knowledge Graph Memory MCP 不可用
直接读/写 `.trae/knowledge_graph.jsonl`(JSONL,一行一实体或关系)。改动后用 `npm run knowledge:update` 同步渲染版 `.json`。
## §3 查询后输出格式
命中实体时,在回复中简述(便于复核):
```
[KG] 命中 N 条:
- Module: 追光AI:模块(09-运维与部署)
- Issue : 追光AI:巡检问题与处理(2026-10-01)
```
## §4 行为约束
### 必须
1. 非豁免任务**必须先检索知识库**再动手
2. 发现代码与已记录规范/事实冲突时**必须指出**,不得静默按代码走
3. 遇到新踩坑/新约束,**必须**在收尾时写入知识库(见 ai_collaboration.md §4)
4. 改动涉及部署/定时任务/数据模型时,**必须**同步更新对应实体
### 禁止
1. 禁止跳过检索直接改业务代码(豁免项除外)
2. 禁止把同一事实**复制**到多个文件(只写一处 + 其余引用)
3. 禁止忽略知识库中已记录的 `Issue` 实体而重复踩同一坑
4. 禁止在未跑门禁(`npx tsc --noEmit` + `npx vitest run`)的情况下声称完成
## §5 版本历史
| 日期 | 版本 | 变更 |
|------|------|------|
| 2026-10-01 | v1.0 | 初始版本:§0 启动加载顺序 + §1 豁免 + §2 强制检索 + §4 行为约束 |
+110
View File
@@ -0,0 +1,110 @@
# AI 协作流程规范
> 版本: v1.0 | 更新: 2026-10-01
> 相关: [README.md](README.md) · [ai_agent_rules.md](ai_agent_rules.md) · `../ai_coding_knowledge.json`
定义 AI 在本项目中的**协作流程**:怎么规划、怎么验证、怎么收尾、怎么沉淀。目标 —— 产出可验证、经验可复利。
---
## §0 三条不可协商的原则
1. **不一次性生成大量代码** —— 任务必须可拆解(`anti_pattern:one-shot-big`)
2. **没有验收标准不开工** —— 否则无法判断何时完成(`method:acceptance-criteria`)
3. **不审查 diff 不算完成** —— AI 的自我描述可信度低于实际改动(`method:review-diff`)
## §1 主工作流:Explore → Plan → Implement → Verify
| 阶段 | 做什么 | 产出 |
|------|--------|------|
| **Explore** | 读相关源码 / 规格 / 知识图谱实体,确认现状与约束 | 现状结论 + 涉及文件清单 |
| **Plan** | 输出结构化步骤(步骤 + 依赖 + 验收标准);复杂任务**先给计划再动手** | 计划(TodoWrite 跟踪) |
| **Implement** | 按计划分步实现,**一步一验证**,不攒到最后一起验 | 可增量验证的改动 |
| **Verify** | 跑门禁;失败回到 Implement | 门禁通过证据 |
> 复杂任务(跨 3 个以上模块 / 涉及数据模型 / 涉及部署)**必须先出计划并等确认**。
## §2 任务拆解铁律
- **窄范围 > 大而模糊**:`修 /api/tools 分页 total 字段` 优于 `改进工具模块`
- 每步定义**输入**(依赖文件/数据)与**输出**(验收标准)
- 状态机驱动:`Pending → Running → Blocked → Done / Failed`
- 单任务改动建议 ≤15 个文件;超出则重新拆解
## §3 验收标准(每个任务开工前必须明确)
| 任务类型 | 验收标准 |
|---------|---------|
| 功能开发 | `npx tsc --noEmit` 零错误 + `npx vitest run` 全绿 + 新功能可用 + 无回归 |
| Bug 修复 | 复现路径通过 + 相关测试通过 |
| 重构 | 行为不变 + 测试全绿 + 无性能回归 |
| 数据/脚本 | 输出格式正确 + 样例验证通过 + 幂等可重跑 |
| 部署/运维 | 部署后自检清单全通过(见 project_rules.md §15.3) |
本项目**双门禁**:`npx tsc --noEmit` + `npx vitest run`(缺一不可)。
## §4 代码审查:看 diff 不看对话
- 用 `git diff` 逐行审查**实际改动**
- 对照 `§3 验收标准` 核验,而不是对照 AI 的说法
- 重点排查:臆造的 API/字段、被静默删除的逻辑、异常处理缺失、硬编码密钥
## §5 失败分型(先分型,再优化)
| 失败类型 | 表现 | 优化方向 |
|---------|------|---------|
| 规划失败 | 任务拆解不合理、范围失控 | 重新拆解任务 |
| 执行失败 | 命令/环境/依赖报错 | 修工具、环境、依赖 |
| 质量失败 | 结果不满足验收标准 | 收紧验收标准或加门禁 |
> 禁止把所有失败都归因为「模型不够强」(`anti_pattern:model-is-everything`)。
## §6 完成流程:自动记录(关键,不可跳过)
开发完成并通过门禁后,**必须**运行:
```bash
npm run knowledge:update
# 等价于:node scripts/update-knowledge.mjs(可加 --dry-run 预览,不写文件)
```
脚本自动捕获三类信息并沉淀:
| 捕获内容 | 来源 | 落盘位置 |
|---------|------|---------|
| **能力特征** | 扫描 `src/app/api/**/route.ts`、`src/app/**/page.tsx`、`src/components/**`、`prisma/schema.prisma`、`scripts/**`、`crontab.txt`、`package.json`、`data/bot-characters.json` | `.trae/knowledge/capabilities.json` |
| **开发过程** | `git log`(最近提交:hash/时间/主题/改动文件数) | `.trae/knowledge/dev_process.jsonl`(按提交追加) |
| **模型成果** | 本次交付物:新增迁移、新增/变更脚本、提交统计 | `.trae/knowledge/artifacts.jsonl`(追加) |
并**增量 upsert**(按实体名,同名替换 observations,未出现的旧实体原样保留,永不删除):
- `.trae/knowledge_graph.json`(渲染版,人类可读 + 权威)
- `.trae/knowledge_graph.jsonl`(MCP Knowledge Graph Memory 读取的源文件)
**自动化触发**:本机已装 `git post-commit` 钩子(`.git/hooks/post-commit`),每次提交后自动执行同一脚本(静默 + 后台,不阻断、不拖慢提交),无需手动记得。
> **换机 / 新克隆后重装钩子**(`.git/hooks/` 不随 git 传递)——在仓库根执行:
> ```bash
> printf '#!/bin/sh\nROOT=$(git rev-parse --show-toplevel) || exit 0\n[ -f "$ROOT/scripts/update-knowledge.mjs" ] || exit 0\ncommand -v node >/dev/null 2>&1 || exit 0\n( cd "$ROOT" && node scripts/update-knowledge.mjs --quiet >/dev/null 2>&1 & )\nexit 0\n' > .git/hooks/post-commit
> ```
> 卸载:删除 `.git/hooks/post-commit` 即可(脚本本身仍可手动运行)。
> 未运行记录脚本就结束任务,视为**流程未完成**。
## §7 反模式(禁止)
| 反模式 | 替代做法 |
|--------|---------|
| 一次性生成大量代码 | 任务拆解(§2) |
| 无验收标准 | 先定义验收标准(§3) |
| 不审查 AI 代码 | 看 diff + 双门禁(§4) |
| 一次性灌入冗余上下文 | 上下文预算,按需加载 |
| 把「模型更强」当「系统更稳」 | 模型 + 编排 + 门禁三层闭环 |
| 同一知识多份副本 | 单一事实源 + 引用 |
| 知识库只写不更新 | §6 自动记录 + 定期一致性巡检 |
## §8 版本历史
| 日期 | 版本 | 变更 |
|------|------|------|
| 2026-10-01 | v1.0 | 初始版本:Plan-first 四阶段 + 验收标准 + 完成自动记录(§6) |
+134 -26
View File
@@ -1,5 +1,10 @@
# 追光AI 项目开发规则 # 追光AI 项目开发规则
> **本文件是 L2 工程标准层**(四层结构索引见 [README.md](README.md))。其他层:
> - L0 方法论:[../ai_coding_knowledge.json](../ai_coding_knowledge.json)(AI 编程效率方法库,机器可解析)
> - L1 协作规则:[ai_agent_rules.md](ai_agent_rules.md)(启动加载)· [ai_collaboration.md](ai_collaboration.md)(Plan-first + 自动记录)
> - L3 项目事实:[../knowledge_graph.jsonl](../knowledge_graph.jsonl)(知识图谱)· [../knowledge/](../knowledge/)(能力/过程/成果自动记录)
## 端口规范 ## 端口规范
- 应用端口范围: **8301-8310**(预留扩展) - 应用端口范围: **8301-8310**(预留扩展)
- 默认端口: **8301** - 默认端口: **8301**
@@ -9,9 +14,9 @@
## 服务器连接信息(重要!总是需要) ## 服务器连接信息(重要!总是需要)
- **IP地址**: 119.45.242.239 - **IP地址**: 119.45.242.239
- **登录用户**: ubuntu - **登录用户**: ubuntu
- **SSH密钥**: `C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem` - **SSH密钥**: `C:\SSH\zhuiguang-ai\zg.pem`(2026-10-02 校正;旧值 `E:\0.Center-S2\S890-V3\pem\zg.pem` 已迁移)
- **SSH命令**: `ssh -i "C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem" ubuntu@119.45.242.239` - **SSH命令**: `ssh -i "C:\SSH\zhuiguang-ai\zg.pem" ubuntu@119.45.242.239`
- **SCP示例**: `scp -i "C:\SGP_KF\ZhuiGuangAI\zhuiguang-ai\Pem\zg.pem" localfile ubuntu@119.45.242.239:/home/ubuntu/zhuiguang-ai/` - **SCP示例**: `scp -i "C:\SSH\zhuiguang-ai\zg.pem" localfile ubuntu@119.45.242.239:/home/ubuntu/zhuiguang-ai/`
## 服务器与基础设施 ## 服务器与基础设施
- **域名**: www.zhuig.com (HTTPS,SSL证书自动续期) - **域名**: www.zhuig.com (HTTPS,SSL证书自动续期)
@@ -19,8 +24,10 @@
- **访问地址**: https://www.zhuig.com (HTTP自动跳转HTTPS) - **访问地址**: https://www.zhuig.com (HTTP自动跳转HTTPS)
- **数据库**: 阿里云RDS MySQL — rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306 - **数据库**: 阿里云RDS MySQL — rm-0jlbgr2rv6dj3t6jngo.mysql.rds.aliyuncs.com:3306
- **Redis**: 云服务器上已有(Docker容器redis-cache:6379),与其他项目共用,有密码认证 - **Redis**: 云服务器上已有(Docker容器redis-cache:6379),与其他项目共用,有密码认证
- **本地Git同步目录**: K:\git-repos - **工作区/开发目录**: `E:\KF_7\ZhuiGuangAI\zhuiguang-ai`(master,当前唯一活跃工作副本)
- **开发机**: E:\ZG_Dev\ZhuiGuangAI\zhuiguang-ai - **本地裸仓镜像(备份入口)**: `K:\git-repos\zhuiguang-ai.git`(bare,2026-10-01 新建。源仓库 remote 需指向它才能持续备份)
- **已停用副本**: `E:\ZG_Dev\ZhuiGuangAI\zhuiguang-ai`(停在 2026-05-23,无 remote)与 `K:\git-repos\zhuiguang-ai`(非裸仓旧工作副本,同样停在 2026-05-23)
- **远端现状**: 源仓库 remote `local` 仍指向已不存在的 `c:\gitbf\zhuiguang-ai.git`;云端 Gitea(119.45.242.239:3000)无本项目仓库
- **Docker管理**: `docker ps` / `docker logs zhuiguang-ai-app` / `docker restart zhuiguang-ai-app` - **Docker管理**: `docker ps` / `docker logs zhuiguang-ai-app` / `docker restart zhuiguang-ai-app`
### ⚠️ 多项目共存服务器(更新/部署铁律) ### ⚠️ 多项目共存服务器(更新/部署铁律)
@@ -142,9 +149,87 @@ docker logs --tail 50 zhuiguang-ai-cron # Cron 容器日志
## 构建与检查 ## 构建与检查
- 修改代码后必须运行 `npx tsc --noEmit` 确认零错误 - 修改代码后必须运行 `npx tsc --noEmit` 确认零错误
- 修改代码后必须运行 `npx vitest run` 确认所有测试通过
- 本地开发服务器: `npm run dev` (端口8301) - 本地开发服务器: `npm run dev` (端口8301)
- 生产构建: `docker build -t zhuiguang-ai:latest .` - 生产构建: `docker build -t zhuiguang-ai:latest .`
- 部署: `docker stop/rm` 旧容器 → `docker run` 新容器 - 部署: `docker stop/rm` 旧容器 → `docker run` 新容器
- **打包分析**: `ANALYZE=true npm run build` 启用 `@next/bundle-analyzer` 分析包体积
## 测试规范
- 测试框架: Vitest
- 配置文件: `vitest.config.ts` + `vitest.setup.ts`
- 测试目录: `src/__tests__/`,文件名匹配 `*.test.ts`
- 覆盖率目标: **80%+**(行/分支/函数/语句)
- tsconfig 需包含 `"types": ["vitest/globals"]` 以启用全局 test/expect/describe
- 测试类别:
- 工具函数单元测试(lib 目录下的纯函数)
- API 路由集成测试
- 组件渲染测试(配合 @testing-library/react)
- 禁止跳过测试(`.skip`)除非有明确的 TODO 注释说明原因
## 图片优化规范
- **强制使用 LazyImage 组件** (`src/components/ui/LazyImage.tsx`),禁止使用原生 `<img>` 标签
- LazyImage 特性:
- 基于 next/image 的懒加载包装器
- 内置骨架屏加载状态(Skeleton loading state)
- 错误回退占位图(Error fallback)
- 支持 `referrerPolicy` 属性(用于跨域图片场景)
- 支持 `width`/`height` 或 `fill` 模式
- 用法示例:
```tsx
import LazyImage from "@/components/ui/LazyImage";
// 固定尺寸
<LazyImage src={url} alt="描述" width={32} height={32} />
// 响应式
<LazyImage src={url} alt="描述" fill className="object-cover" />
// 跨域图片
<LazyImage src={url} alt="描述" width={48} height={48} referrerPolicy="no-referrer" />
```
- 必须提供有意义的 `alt` 文本
- 装饰性图片使用 `alt=""` 配合 `role="presentation"`
## 导航规范(Link vs router.push)
- 所有静态页面内导航必须使用 Next.js `<Link>` 组件
- 禁止使用 `<button onClick={() => router.push(...)}>` 进行页面跳转
- 适用场景:
- 分页按钮 → `<Link>`
- 侧边栏筛选 → `<Link>`(含查询参数)
- 面包屑 → `<Link>`
- 卡片/列表项 → `<Link>`
- "清除筛选"按钮 → `<Link href="/base-path">`
- 仅外部链接使用 `<a>`(需加 `target="_blank" rel="noopener noreferrer"`)
## API 响应格式规范
- 列表接口统一使用 `total`(不是 `totalCount`)表示总数
- 返回格式: `{ data, total, page, pageSize }` 或 `{ success, error }`
- 前端消费端也同步使用 `total` 字段
- 已统一接口: `/api/notifications`, `/api/forum/topics`, `/api/comments`, `/api/user/points`
## 并行查询规范
- 多个独立的 Prisma 查询必须使用 `Promise.all` 并行执行
- 严禁串行 await 多个无依赖的数据库查询
```typescript
// ✅ 正确:并行查询
const [tool, relatedTools, reviewCount] = await Promise.all([
prisma.tool.findUnique({ where: { slug } }),
prisma.tool.findMany({ where: { categoryId }, take: 6 }),
prisma.review.count({ where: { toolId } }),
]);
```
## 无障碍规范(WCAG 2.1)
- 所有交互元素(div onClick)必须添加 `role` + `aria-*` 属性 + 键盘事件(`onKeyDown`)
- 搜索输入框必须添加 `aria-label` 属性
- 装饰性图片添加 `role="presentation"`(不暴露给屏幕阅读器)
- 根布局必须提供跳过导航链接(Skip-link)
- 主内容区域使用 `<main id="main-content" tabIndex={-1}>`
## TypeScript 类型规范
- 禁止使用 `any` 类型
- 泛型函数使用 `<T>` 约束(参考 `src/lib/cache.ts` 的 `cachedResponse<T>`)
- API 回调函数使用明确的参数类型
- 内联对象类型使用 interface 而非匿名类型字面量
- `src/lib/admin-auth.ts`: 使用 Prisma 严格类型,id 字段统一为 `number`
## 数据库 ## 数据库
- 云数据库: 阿里云RDS MySQL - 云数据库: 阿里云RDS MySQL
@@ -236,16 +321,19 @@ docker logs --tail 50 zhuiguang-ai-cron # Cron 容器日志
- OPENAI_API_KEY: OpenAI API Key(Bot活动引擎使用,生成论坛内容) - OPENAI_API_KEY: OpenAI API Key(Bot活动引擎使用,生成论坛内容)
## 数字人Bot系统 ## 数字人Bot系统
- **角色定义文件**: `data/bot-characters.json` — 112个Bot角色定义,所有Bot脚本和前端工具依赖此文件 - **角色定义文件**: `data/bot-characters.json` — 当前 **6 个**「行业情报官」角色(cb_intel / fi_ins_intel / fi_stock_intel / brand_intel / ai_intel / ct_intel);旧 112 角色「模拟真人互刷」体系已于 2026-09-05 退役
- **Bot头像目录**: `public/bot-avatars/` — 程序化SVG头像(112个,~2.5KB/个) - **Bot头像目录**: `public/bot-avatars/` — SVG 头像 178 个(含历史角色头像)
- **会员头像库**: `public/avatars/` — SVG 头像 530 个,会员可在个人中心选择或自定义上传
- **头像生成脚本**: `scripts/generate-avatar-library.mjs` — 批量生成头像库,支持按性别/主题/数量筛选
- **头像上传目录**: `public/uploads/avatars/` — 用户自定义上传头像存储
- **Bot用户标记**: User表中 `isBot: true` 标识Bot用户,与真人的互动数据分别统计 - **Bot用户标记**: User表中 `isBot: true` 标识Bot用户,与真人的互动数据分别统计
- **Bot活动时段**: 9:00-23:00每小时运行,15轮/天,论坛内容不干扰深夜用户体验 - **Bot活动时段**: 旧「模拟真人互刷」体系已停用(`bot-activity` 等任务在 `crontab.txt` 中已注释,2026-09-05 退役);现由行业情报官引擎承担
- **Bot数据库**: 12张Bot专用表(BotConfig/BotMemory/BotDailyStat/BotSkill/BotWeeklyReview/BotPersona/BotPersonaVariant/BotPersonaExperiment/BotPersonaAssignment/BotPersonaMetric/BotAdversarialLearning/BotCrossForumAffinity) - **Bot数据库**: 12张Bot专用表(BotConfig/BotMemory/BotDailyStat/BotSkill/BotWeeklyReview/BotPersona/BotPersonaVariant/BotPersonaExperiment/BotPersonaAssignment/BotPersonaMetric/BotAdversarialLearning/BotCrossForumAffinity)
## 定时任务调度系统 ## 定时任务调度系统
- **后台管理**: `/admin/tasks`(任务中心)、`/admin/tasks/logs`(执行日志)、`/admin/tasks/[taskKey]`(单任务详情+统计) - **后台管理**: `/admin/tasks`(任务中心)、`/admin/tasks/logs`(执行日志)、`/admin/tasks/[taskKey]`(单任务详情+统计)
- **数据模型**: TaskConfig(任务配置,含enabled/cronExpr/config)、TaskLog(执行历史,含status/duration/result/error) - **数据模型**: TaskConfig(任务配置,含enabled/cronExpr/config)、TaskLog(执行历史,含status/duration/result/error)
- **种子数据**: `scripts/seed-task-configs.mjs` 初始化6个任务配置 - **种子数据**: `scripts/seed-task-configs.mjs` 初始化 29 个任务配置(含已停用任务,与 `crontab.txt` 存在漂移,见下)
- **手动触发**: `/admin/tasks` 页支持每个任务运行,也可通过 POST `/api/admin/tasks/run` 触发 - **手动触发**: `/admin/tasks` 页支持每个任务运行,也可通过 POST `/api/admin/tasks/run` 触发
- **API**: - **API**:
- `GET/PUT /api/admin/tasks/configs` — 任务配置查询/更新 - `GET/PUT /api/admin/tasks/configs` — 任务配置查询/更新
@@ -253,25 +341,42 @@ docker logs --tail 50 zhuiguang-ai-cron # Cron 容器日志
- `POST /api/admin/tasks/run` — 手动触发任务执行 - `POST /api/admin/tasks/run` — 手动触发任务执行
### 定时任务(Supercronic 容器调度) ### 定时任务(Supercronic 容器调度)
所有定时任务由 `zhuiguang-ai-cron` 容器内的 supercronic 调度,配置文件 `crontab.txt`。不再使用主机 crontab + cron-wrapper.sh。 所有定时任务由 `zhuiguang-ai-cron` 容器内的 supercronic 调度,配置文件 `crontab.txt`(**唯一准绳**)。不再使用主机 crontab。
| 时间 | 任务 | 脚本 | TaskKey | 说明 | > ⚠️ supercronic 在容器内按 **UTC** 执行(TZ 环境变量不生效),下表时刻均为 UTC,括号外已换算为北京时间(UTC + 8h)。
|------|------|------|---------|------| > ⚠️ 2026-10-01 巡检确认:`crontab.txt` 已注释停用大量 Bot/行业情报 AI 任务,但 `scripts/seed-task-configs.mjs` 仍写 `enabled: true`、`scripts/health-check.mjs` 仍把它们列入期望键 —— **三方漂移会产生误告警**,新增/停用任务时必须同步这三处。
| 01:00 | Task5 更新星值 | `scripts/task5-update-stars.mjs` | task5-update-stars | 更新所有GitHub技能Stars数+日/周/月变化 |
| 03:00 | Task3 工具巡检 | `scripts/task3-check-tools.mjs` | task3-check-tools | 检查网站/Logo可访问性+自动发布合格待审核工具 | **启用中(19 个)**
| 04:00 | Bot技能结晶 | `scripts/bot-skill-crystallize.mjs` | bot-skill-crystallize | 扫过去14天高互动Bot话题→DeepSeek解构共性→入库BotSkill(usageCount驱动调用);末尾回收7天前成熟技能更新successRate(<30%且使用≥3次自动isActive=false) |
| 05:00 | Bot亲和度与画像 | `scripts/bot-affinity-update.mjs` | bot-affinity-update | 刷BotCrossForumAffinity(affinity=min(1, samples/total/4))和BotPersona(avgReplyLength/questionRatio/stanceKeywords/topHumanUsers/topTopicTypes/lastTopicTitles,bot-activity发帖时注入prompt) | | UTC | 北京时间 | 任务 | 脚本 | TaskKey |
| 04:00 | Task6 评测热门 | `scripts/task6-review-hot.mjs` | task6-review-hot | 评测2个最热未评测GitHub技能(五维评测) | |-----|---------|------|------|---------|
| 05:00 | Task4 发现技能 | `scripts/daily-discover.mjs` | task4-discover-skills | GitHub搜索≥1000 stars项目→DeepSeek预评测→入库PendingSkill | | 0 1 | 09:00 | Task5 更新星值 | `scripts/task5-update-stars.mjs` | task5-update-stars |
| 06:00 | Task1 发现工具 | `scripts/task1-discover-tools.mjs` | task1-discover-tools | 36kr/IT之家/开源中国RSS→DeepSeek提取工具→入库PendingTool | | 0 3 | 11:00 | Task3 工具巡检 | `scripts/task3-check-tools.mjs` | task3-check-tools |
| 07:30 | AI日报 | `scripts/daily-news.mjs` | daily-news | 多源搜索真实新闻+DeepSeek整理格式→发布日报 | | 0 4 | 12:00 | Task6 评测热门 | `scripts/task6-review-hot.mjs` | task6-review-hot |
| 21:30 | Bot反馈闭环 | `scripts/bot-feedback-loop.mjs` | bot-feedback-loop | 扫描Bot自己帖子下的新回复→存反馈记忆→按概率触发二次回复(仅expert bots,配额high=6/medium=3/low=1) | | 0 4 * * 0 | 周日 12:00 | 工具数据补全 | `scripts/enrich-tool-data.mjs` | enrich-tool-data |
| 周日02:00 | Bot周度复盘 | `scripts/bot-weekly-review.mjs` | bot-weekly-review | 算engagementScore→生成REFLECTION记忆→决定下周配额tier(写入BotWeeklyReview.nextWeekQuota,bot-activity下次跑时动态覆盖) | | 30 4 | 12:30 | 日志清理 | `scripts/cleanup-logs.mjs` | cleanup-logs |
| 08:00 | Task7 新闻推社区 | `scripts/task7-news-to-community.mjs` | task7-news-to-community | 将当日日报新闻分发到社区4个板块(AI工具推荐/AI资讯/技术探讨/观点讨论) | | 0 5 | 13:00 | Task4 发现技能 | `scripts/daily-discover.mjs` | task4-discover-skills |
| 0 6 | 14:00 | Task1 发现工具 | `scripts/task1-discover-tools.mjs` | task1-discover-tools |
| 30 6 | 14:30 | task_log 清理 | `scripts/cleanup-task-logs.mjs` | cleanup-task-logs |
| 30 7 | 15:30 | AI 日报 | `scripts/daily-news.mjs` | daily-news |
| 0 8 | 16:00 | Task7 新闻推社区 | `scripts/task7-news-to-community.mjs` | task7-news-to-community |
| 0 8 1 * * | 每月 1 日 16:00 | 断签保护卡发放 | `scripts/grant-freeze-cards.mjs` | grant-freeze-cards |
| 30 8 | 16:30 | 任务健康检查 | `scripts/health-check.mjs` | health-check |
| 0 9 | 17:00 | 通知摘要生成 | `scripts/notification-digest.mjs` | notification-digest |
| 0 9 * * 1 | 周一 17:00 | 竞品工具数监测 | `scripts/competitor-monitor.mjs` | competitor-monitor |
| 0 10 | 18:00 | 行业趋势监测 | `scripts/trend-alert.mjs` | trend-alert |
| 0 10 * * 1 | 周一 18:00 | 周刊生成 | `scripts/newsletter-generate.mjs` | newsletter-generate |
| 30 10 * * 1 | 周一 18:30 | 每周推荐邮件 | `scripts/weekly-recommend-email.mjs` | weekly-recommend-email |
| 15 */6 | 每 6 小时 | 点赞计数对账 | `scripts/reconcile-like-counts.mjs` | reconcile-like-counts |
| 5 23,3,7,11,15 | 每日 7/11/15/19/23 点 | 行业素材采集 | `scripts/industry-thinktank.mjs collect --all` | industry-thinktank |
**已注释停用**:`bot-activity`、`bot-skill-crystallize`、`bot-affinity-update`、`bot-weekly-review`、`bot-feedback-loop`、`bot-adversarial-learning-run`、`bot-persona-experiment-run`、`bot-roundtable`、`industry-thinktank weekly/respond`、`industry-daily daily/respond`
(原因:2026-09-05 旧 Bot「模拟真人互刷」体系退役;2026-09-26 按要求关闭全部 AI 生成功能,行业情报改为需登录访问)
- 调度配置: `crontab.txt`(supercronic 格式),修改后需重建 cron 容器生效 - 调度配置: `crontab.txt`(supercronic 格式),修改后需重建 cron 容器生效
- 日志目录: 容器内 `/app/logs/`(挂载到 Docker volume `zhuiguang_ai_bot_logs`) - 日志目录: 容器内 `/app/logs/`;实际 Docker 卷名为 `zhuiguang-ai_bot-logs`(旧文档写 `zhuiguang_ai_bot_logs`,已过时)
- 定时任务脚本从 `scripts/` 目录直接执行,环境变量由容器 entrypoint 注入 - 定时任务脚本从 `scripts/` 目录直接执行,环境变量由容器 entrypoint 注入
- **新增任务必须同步四处**:`crontab.txt` + `scripts/<task>.mjs` + `scripts/seed-task-configs.mjs` + `scripts/health-check.mjs`
## AI日报新闻源 ## AI日报新闻源
- **36kr** RSS (https://36kr.com/feed) — 国内科技资讯 - **36kr** RSS (https://36kr.com/feed) — 国内科技资讯
@@ -300,6 +405,7 @@ docker logs --tail 50 zhuiguang-ai-cron # Cron 容器日志
- 敏感信息(密钥、密码、Token)必须使用环境变量,禁止硬编码 - 敏感信息(密钥、密码、Token)必须使用环境变量,禁止硬编码
- `.env` 文件已被加入 `.gitignore`,仅 `.env.example` 模板可提交 - `.env` 文件已被加入 `.gitignore`,仅 `.env.example` 模板可提交
- HTTP 响应必须包含安全头:CSP、HSTS、X-Content-Type-Options、X-Frame-Options - HTTP 响应必须包含安全头:CSP、HSTS、X-Content-Type-Options、X-Frame-Options
- CSP 的 `script-src` 必须包含 `'unsafe-inline'`(Next.js App Router 水合依赖内联 `self.__next_f` 脚本),禁止 `'unsafe-eval'`;详见 workspace 规则 7.1
- 文件上传/下载必须限制大小(最大 5MB)和类型,SVG 内容需消毒 script/iframe/on-event 标签 - 文件上传/下载必须限制大小(最大 5MB)和类型,SVG 内容需消毒 script/iframe/on-event 标签
- Logo 文件名使用 SHA256 哈希(不使用 MD5) - Logo 文件名使用 SHA256 哈希(不使用 MD5)
- VIP 用户不能访问管理后台(/admin/* 和 /api/admin/*) - VIP 用户不能访问管理后台(/admin/* 和 /api/admin/*)
@@ -318,8 +424,10 @@ docker logs --tail 50 zhuiguang-ai-cron # Cron 容器日志
- 修改项目架构/技术栈/部署配置 → 更新 `追光AI` 或对应 `Module` 实体 - 修改项目架构/技术栈/部署配置 → 更新 `追光AI` 或对应 `Module` 实体
- 新增/修改实体间关系 → 更新 Knowledge Graph 关系 - 新增/修改实体间关系 → 更新 Knowledge Graph 关系
- **同步时机**:在代码修改完成并通过检查后、提交代码前执行同步 - **同步时机**:在代码修改完成并通过检查后、提交代码前执行同步
- **同步方法**:使用 `mcp_Knowledge_Graph_Memory_create_entities`(新增实体)、`mcp_Knowledge_Graph_Memory_add_observations`(追加观察)、`mcp_Knowledge_Graph_Memory_update_entities`(更新实体)、`mcp_Knowledge_Graph_Memory_create_relations`(新增关系)、`mcp_Knowledge_Graph_Memory_delete_entities`/`delete_relations`/`delete_observations`(删除) - **图谱存储**:`.trae/knowledge_graph.jsonl`(MCP 源文件,JSONL,一行一实体/关系);`.trae/knowledge_graph.json` 为渲染版(`project` / `version` / `updated_at` / `entities` / `relations`)
- **查询优先**:在开始任何开发任务前,先通过 `mcp_Knowledge_Graph_Memory_search_nodes` 或 `mcp_Knowledge_Graph_Memory_open_nodes` 查询图谱,利用已有知识加速开发 - **MCP 配置**:`.trae/mcp.json` 注册 `Knowledge Graph Memory` 服务(`npx -y @itseasy21/mcp-knowledge-graph`,`MEMORY_FILE_PATH=.trae/knowledge_graph.jsonl`)
- **同步方法**:优先使用 `create_entities` / `add_observations` / `update_entities` / `create_relations` / `delete_entities` 等工具;若 MCP 未挂载,直接维护 `.trae/knowledge_graph.jsonl`
- **查询优先**:在开始任何开发任务前,先通过 `search_nodes` / `open_nodes` 查询图谱,利用已有知识加速开发
### 7. 可靠性原则 ### 7. 可靠性原则
- 所有外部 API 调用(GitHub、DeepSeek 等)必须设置超时和重试 - 所有外部 API 调用(GitHub、DeepSeek 等)必须设置超时和重试
+46 -19
View File
@@ -22,28 +22,36 @@
## 项目结构 ## 项目结构
``` ```
zhuiguang-ai/ zhuiguang-ai/
├── data/ # 数据文件 (bot-characters.json)
├── prisma/ # 数据库Schema + 迁移 + 种子 ├── prisma/ # 数据库Schema + 迁移 + 种子
├── public/ # 静态资源 (logo.png, logos/) ├── public/ # 静态资源 (logo.png, logos/, bot-avatars/, avatars/, uploads/avatars/)
├── scripts/ # 独立脚本 (7个定时任务 + 多个工具脚本) ├── scripts/ # 独立脚本 (7个AI发现任务 + 7个Bot任务 + 多个工具脚本)
│ └── lib/ # 脚本共享库 (bot-persona, bot-persona-experiment, bot-adversarial-learning, bot-avatar-generator)
├── src/ ├── src/
│ ├── __tests__/ # 测试文件 (vitest, 5个测试套件, 132个测试)
│ ├── app/ │ ├── app/
│ │ ├── (admin)/admin/ # 后台页面 (18个页面 + 任务调度3个子页) │ │ ├── (admin)/admin/ # 后台页面 (21个页面: 18个原有 + 3个Bot管理)
│ │ ├── api/ # API路由 (50+个端点) │ │ ├── api/ # API路由 (60+个端点, 含bots/、notifications/、recommendations/、points shop等子路由)
│ │ ├── categories/ # 前台分类页 │ │ ├── categories/ # 前台分类页
│ │ ├── community/ # 社区 (bots/列表+排行榜, tools/compare工具对比)
│ │ ├── reviews/ # 前台评测列表+榜单 │ │ ├── reviews/ # 前台评测列表+榜单
│ │ ├── skills/ # 前台技能页 │ │ ├── skills/ # 前台技能页
│ │ ├── tools/ # 前台工具页 │ │ ├── tools/ # 前台工具页 + /tools/compare 对比页
│ │ ├── login/ # 用户登录页 │ │ ├── login/ # 用户登录页
│ │ ├── daily/ # AI日报页 │ │ ├── daily/ # AI日报页
│ │ ├── user/ # 用户中心(个人资料/收藏/收藏夹) │ │ ├── user/ # 用户中心(个人资料/收藏/收藏夹/积分/商店)
│ │ ├── globals.css # 全局样式 │ │ ├── globals.css # 全局样式 (含dark mode CSS变量)
│ │ ├── layout.tsx # 前台布局 │ │ ├── layout.tsx # 前台布局 (含ThemeProvider + SkipLink)
│ │ └── page.tsx # 首页 │ │ └── page.tsx # 首页
│ ├── components/ # 组件 (Header, Footer, UI, Common) │ ├── components/ # 组件 (Header, Footer, UI/LazyImage, UI/ThemeProvider, Common/Compare*, Common/PersonalizedRecommend, Common/HighlightText, forum/Bot*, forum/AvatarPicker, forum/NotificationCenter)
│ ├── lib/ # 工具库 (prisma, auth, admin-auth, github, deepseek, logo-fetcher, logo-storage, utils) │ ├── lib/ # 工具库 (prisma, auth, admin-auth, github, deepseek, logo-fetcher, logo-storage, bot-utils, avatar-library, utils, source-type, constants, streak-freeze, shop-items, level-config, points-reward, formatRelativeTime)
│ └── middleware.ts # 路由保护 │ └── middleware.ts # 路由保护
├── vitest.config.ts # Vitest 测试配置
├── vitest.setup.ts # Vitest 全局 setup
├── Dockerfile # 多阶段构建
├── docker-compose.yml # 容器编排
├── crontab.txt # supercronic 调度表 (25条定时任务)
├── .env # 环境变量 ├── .env # 环境变量
├── restart.ps1 # 服务重启脚本(端口8301-8310)
└── package.json # 依赖配置 └── package.json # 依赖配置
``` ```
@@ -51,15 +59,23 @@ zhuiguang-ai/
| 序号 | 模块 | 文档 | 说明 | | 序号 | 模块 | 文档 | 说明 |
|------|------|------|------| |------|------|------|------|
| 01 | 数据库 | [01-database.md](./modules/01-database.md) | 26张数据模型、8个枚举、完整索引、迁移历史 | | 01 | 数据库 | [01-database.md](./modules/01-database.md) | 38张数据模型、8个枚举、完整索引、迁移历史 |
| 02 | API | [02-api.md](./modules/02-api.md) | 前台/后台/用户API端点、SystemConfig配置键 | | 02 | API | [02-api.md](./modules/02-api.md) | 前台/后台/用户API端点、SystemConfig配置键 |
| 03 | 页面 | [03-pages.md](./modules/03-pages.md) | 前台/后台页面路由、布局、功能说明 | | 03 | 页面 | [03-pages.md](./modules/03-pages.md) | 前台/后台页面路由、布局、功能说明 |
| 04 | 组件与样式 | [04-components-and-styles.md](./modules/04-components-and-styles.md) | UI组件、样式系统、设计规范 | | 04 | 组件与样式 | [04-components-and-styles.md](./modules/04-components-and-styles.md) | UI组件、样式系统、设计规范(含暗色模式、LazyImage) |
| 05 | AI发现 | [05-ai-discovery.md](./modules/05-ai-discovery.md) | 提示词驱动发现流程、Logo抓取引擎、定时任务、评测管线 | | 05 | AI发现 | [05-ai-discovery.md](./modules/05-ai-discovery.md) | 提示词驱动发现流程、Logo抓取引擎、定时任务、评测管线 |
| 06 | 认证 | [06-auth.md](./modules/06-auth.md) | NextAuth配置、路由保护、权限体系、登录流程 | | 06 | 认证 | [06-auth.md](./modules/06-auth.md) | NextAuth配置、路由保护、权限体系、登录流程 |
| 07 | 工具库与脚本 | [07-lib-and-scripts.md](./modules/07-lib-and-scripts.md) | 工具函数、定时任务脚本、运维脚本 | | 07 | 工具库与脚本 | [07-lib-and-scripts.md](./modules/07-lib-and-scripts.md) | 工具函数、定时任务脚本(含Bot脚本)、运维脚本 |
| 08 | 社区功能 | [08-community.md](./modules/08-community.md) | 评论系统、收藏、浏览历史、收藏夹、通知、论坛 | | 08 | 社区功能 | [08-community.md](./modules/08-community.md) | 评论系统、收藏、浏览历史、收藏夹、通知、论坛、Bot社区、会员头像库 |
| 09 | 运维与部署 | [09-devops.md](./modules/09-devops.md) | Docker容器、supercronic调度、Nginx、部署流程、备份 | | 09 | 运维与部署 | [09-devops.md](./modules/09-devops.md) | Docker容器、supercronic调度、Nginx、部署流程、备份 |
| 10 | 数字人Bot系统 | [10-bot-system.md](./modules/10-bot-system.md) | 112个Bot角色、活动引擎、A/B测试、对抗学习、技能结晶、亲和度、头像系统 |
| 11 | 积分体系 | [points.md](./modules/points.md) | 积分规则、等级系统、积分商店、Streak冻结、签到系统 |
| 12 | 通知系统 | [notifications.md](./modules/notifications.md) | 智能摘要、通知偏好、通知中心、通知类型 |
| 13 | 暗色模式 | [theme.md](./modules/theme.md) | ThemeProvider、CSS变量、组件适配、用户偏好持久化 |
| 14 | 工具功能增强 | [tools.md](./modules/tools.md) | 工具对比、工具认证徽章、工具推荐 |
| 15 | 搜索增强 | [search.md](./modules/search.md) | 自动补全、搜索历史、键盘导航、高亮文本 |
| 16 | 测试体系 | [testing.md](./modules/testing.md) | Vitest配置、5个测试套件、覆盖率标准 |
| 17 | 开发规则 | [17-rules.md](./modules/17-rules.md) | 项目开发规则体系(22章:代码质量/数据策略/导航/图片/无障碍/SEO/性能/安全/定时任务/测试/Shell/Docker/部署/数据模型/API设计/Bot系统/环境变量/依赖管理/审查清单) |
## 环境变量 ## 环境变量
``` ```
@@ -68,6 +84,7 @@ NEXTAUTH_URL=http://localhost:8301
NEXTAUTH_SECRET=<secret> NEXTAUTH_SECRET=<secret>
GITHUB_TOKEN=<optional> GITHUB_TOKEN=<optional>
DEEPSEEK_API_KEY=<required-for-review> DEEPSEEK_API_KEY=<required-for-review>
OPENAI_API_KEY=<required-for-bot-activity>
``` ```
## 核心业务流程 ## 核心业务流程
@@ -88,13 +105,23 @@ DEEPSEEK_API_KEY=<required-for-review>
- **GitHub Star同步**: 通过GitHub API定期更新技能Star数,维护90天历史 - **GitHub Star同步**: 通过GitHub API定期更新技能Star数,维护90天历史
- **五维评测系统**: GitHub数据采集 + DeepSeek AI评测 + 雷达图展示,5维度(capability/devExp/costLicense/community/performance)评分 - **五维评测系统**: GitHub数据采集 + DeepSeek AI评测 + 雷达图展示,5维度(capability/devExp/costLicense/community/performance)评分
- **评测榜单**: 前台/reviews页面展示评测分榜TOP10 + Stars涨幅榜TOP10侧边栏 - **评测榜单**: 前台/reviews页面展示评测分榜TOP10 + Stars涨幅榜TOP10侧边栏
- **任务调度系统**: 7个定时任务(Task1-6+Task7新闻推社区)+AI日报,统一TaskConfig配置+TaskLog执行历史+后台可视化管理+手动触发,通过cron-wrapper.sh统一入口执行 - **任务调度系统**: 25个定时任务(AI发现7个+Bot系统+运维),统一TaskConfig配置+TaskLog执行历史+后台可视化管理+手动触发,通过supercronic容器调度
- **数字人Bot社区引擎**: 112个AI驱动的虚拟用户,分专家/观察者两种角色,自动发帖/回复/点赞,含A/B测试、对抗学习、技能结晶、亲和度计算、周度复盘完整闭环
- **权限体系**: 53个精细化权限点,角色管理,细粒度权限控制 - **权限体系**: 53个精细化权限点,角色管理,细粒度权限控制
- **社区互动**: 评论/回复、收藏、浏览历史、收藏夹、通知系统、论坛板块 - **社区互动**: 评论/回复、收藏、浏览历史、收藏夹、通知系统、论坛板块、Bot排行榜
- **分页**: 所有列表页面支持分页(每页20条) - **分页**: 所有列表页面支持分页(每页20条)
- **浅色主题**: 科技感浅色设计系统,毛玻璃效果+渐变文字+精致阴影 - **双主题**: 浅色/暗色双模式,ThemeProvider + CSS变量驱动,用户偏好localStorage持久化
- **层级分类**: 一级分类+二级分类,左侧导航+标签切换 - **层级分类**: 一级分类+二级分类,左侧导航+标签切换
- **用户系统**: 邮箱注册/登录、GitHub OAuth、收藏功能、浏览历史 - **用户系统**: 邮箱注册/登录、GitHub OAuth、收藏功能、浏览历史、**会员头像库(500个预置头像+自定义上传)**
- **积分体系**: 20级成长系统、积分商店、签到连击、Streak冻结保护
- **智能通知**: 通知摘要合并、通知偏好设置、通知中心页面
- **内容推荐**: 个性化推荐引擎、相关话题推荐、工具相似度计算
- **工具对比**: 多工具并排对比、10维度评分表、Context共享状态
- **工具认证**: 管理员认证徽章、蓝V标识
- **搜索增强**: 自动补全API、搜索历史localStorage、键盘导航(上下/回车/ESC)
- **安全加固**: 后台25个写操作API全部添加requireAdmin中间件验证;批量操作ids数组需校验非空且有数量上限(100~500);FK字段回退值禁止硬编码 - **安全加固**: 后台25个写操作API全部添加requireAdmin中间件验证;批量操作ids数组需校验非空且有数量上限(100~500);FK字段回退值禁止硬编码
- **容器化部署**: Docker多阶段构建 + supercronic调度 + 4个命名卷,完全替代PM2+主机crontab
- **测试覆盖**: Vitest + 5个测试套件 + 132个测试 + 80%+覆盖率目标
- **端口锁定**: 8301-8310区间,禁止使用其他端口 - **端口锁定**: 8301-8310区间,禁止使用其他端口
- **频率限制**: 登录尝试限制、IP请求限制 - **频率限制**: 登录尝试限制、IP请求限制
- **开发规则体系**: 22章项目开发规则(代码质量/安全/性能/无障碍/SEO/测试/部署/Docker/Shell/API设计/数据模型/Bot系统/环境变量),🔴🟡🟢三级严重度,覆盖全生命周期
+19
View File
@@ -705,6 +705,15 @@
| ForumPost | [topicId, createdAt] | 联合索引 | | ForumPost | [topicId, createdAt] | 联合索引 |
| Permission | [groupName, sortOrder] | 联合索引 | | Permission | [groupName, sortOrder] | 联合索引 |
| User | [oauthProvider, oauthId] | 联合唯一 | | User | [oauthProvider, oauthId] | 联合唯一 |
| BotConfig | [userId] | 唯一索引 |
| BotDailyStat | [botId, date] | 唯一索引 |
| BotWeeklyReview | [botId, weekKey] | 唯一索引 |
| BotPersona | [botId] | 主键索引 |
| BotPersonaVariant | [botId, variantKey] | 唯一索引 |
| BotPersonaAssignment | [refType, refId] | 唯一索引 |
| BotPersonaMetric | [variantId, date] | 唯一索引 |
| BotAdversarialLearning | [botId, weekKey] | 唯一索引 |
| BotCrossForumAffinity | [botId, forumSlug] | 唯一索引 |
## 迁移历史 ## 迁移历史
@@ -721,3 +730,13 @@
| 9 | add_hot_score | 2026-05-08 | 添加热度评分(DailyReportItem.hotScore) | | 9 | add_hot_score | 2026-05-08 | 添加热度评分(DailyReportItem.hotScore) |
| 10 | add_skill_reviews | 2026-05-20 | 添加技能评测表+Skill扩展字段 | | 10 | add_skill_reviews | 2026-05-20 | 添加技能评测表+Skill扩展字段 |
| 11 | add_task_system | 2026-05-24 | 添加任务调度系统+权限体系+社区功能 | | 11 | add_task_system | 2026-05-24 | 添加任务调度系统+权限体系+社区功能 |
| 12 | add_bot_system | 2026-06-07 | 添加数字人Bot系统(12张表: BotConfig/BotMemory/BotDailyStat/BotSkill/BotWeeklyReview/BotPersona/BotPersonaVariant/BotPersonaExperiment/BotPersonaAssignment/BotPersonaMetric/BotAdversarialLearning/BotCrossForumAffinity) |
---
## 开发规则引用
> 数据模型变更必须遵守 [开发规则第十六章 - 数据模型与迁移规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):
> - Prisma 命名约定:模型PascalCase→`@@map("snake_case")`,字段camelCase→`@map("snake_case")`
> - 连接单例模式:`globalForPrisma` 防止热重载多实例
> - Schema 变更流程:migrate dev → migrate status → migrate deploy(严禁DROP/TRUNCATE)
> - 数据库隔离:仅操作 `zhuiguang_ai` 库
+23
View File
@@ -195,6 +195,18 @@ NextAuth认证端点(登录/回调/CSRF等)。
| /api/admin/permissions | GET | 获取全部权限点列表 | | /api/admin/permissions | GET | 获取全部权限点列表 |
| /api/admin/user-permissions | GET | 获取当前用户权限(roleId/userId/当前用户) | | /api/admin/user-permissions | GET | 获取当前用户权限(roleId/userId/当前用户) |
### Bot管理API
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/admin/bots/stats | GET | Bot表现统计(每日指标+汇总,支持?days和?botUserId) |
| /api/admin/bots/experiments | GET | A/B实验看板数据(变体/实验/每日指标聚合) |
| /api/admin/bots/experiments/run | POST | 手动触发A/B实验(body: {mode, lookback, noSwitch}) |
| /api/admin/bots/experiments/variants | PATCH | 更新变体(权重/启用/重置样本) |
| /api/admin/bots/experiments/variants | DELETE | 删除变体 |
| /api/admin/bots/adversarial-learning | GET | 对抗学习看板(支持?weeks和?botUserId) |
| /api/admin/bots/adversarial-learning/run | POST | 手动触发对抗学习(body: {weekKey, force, lookback, bot}) |
### 日报管理 ### 日报管理
| 路由 | 方法 | 功能 | | 路由 | 方法 | 功能 |
@@ -223,3 +235,14 @@ NextAuth认证端点(登录/回调/CSRF等)。
| discoveryPromptConfig | JSON | AI发现提示词配置(含toolPrompt/skillPrompt/newsPrompt/tagPool等) | | discoveryPromptConfig | JSON | AI发现提示词配置(含toolPrompt/skillPrompt/newsPrompt/tagPool等) |
| logoFetchConfig | JSON | Logo抓取策略配置 | | logoFetchConfig | JSON | Logo抓取策略配置 |
| discoverSkillsScript | string | 技能探索脚本源码 | | discoverSkillsScript | string | 技能探索脚本源码 |
---
## 开发规则引用
> API 开发必须遵守 [开发规则第十七章 - API设计规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):
> - 路由骨架:`export const dynamic = "force-dynamic"` + `Promise.all` 并行 + `Cache-Control` 头
> - 错误处理:使用 [api-error.ts](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/src/lib/api-error.ts) 的 `badRequest`/`notFound`/`serverError`
> - 响应格式:列表 `{ data, total, page, pageSize }`,统一使用 `total`
> - 状态码:200/201/400/401/403/404/409/500
> - 认证分层:middleware + `requireAdmin()` + `getServerSession()`
> - 分页上限:`Math.min(pageSize, 100)`
+43 -3
View File
@@ -11,12 +11,18 @@
### /tools (工具列表) ### /tools (工具列表)
- 文件: `src/app/tools/page.tsx` - 文件: `src/app/tools/page.tsx`
- 组件: ToolsContent - 组件: ToolsContent
- 功能: 分类筛选侧边栏(一级分类)、定价模型筛选、搜索、分页、卡片网格展示 - 功能: 分类筛选侧边栏(一级分类)、定价模型筛选、搜索、分页、卡片网格展示、**工具对比功能(CompareButton)** 🆕
- 数据源: `/api/categories`, `/api/tools` - 数据源: `/api/categories`, `/api/tools`
### /tools/compare (工具对比) 🆕
- 文件: `src/app/tools/compare/page.tsx`
- 组件: ComparePage(CompareProvider + CompareBar)
- 功能: 最多4个工具并排对比,10维度对比表格(名称/分类/定价/特性/标签/评分/评论数/浏览数/官网)
- 数据源: `/api/tools` (通过Context传递)
### /tools/[slug] (工具详情) ### /tools/[slug] (工具详情)
- 文件: `src/app/tools/[slug]/page.tsx` - 文件: `src/app/tools/[slug]/page.tsx`
- 功能: 工具完整信息展示、浏览计数+1、收藏按钮、评论评价(ReviewSection) - 功能: 工具完整信息展示、浏览计数+1、收藏按钮、评论评价(ReviewSection)、**认证蓝V徽章** 🆕、**相关推荐** 🆕
- 数据源: `/api/tools/[slug]` - 数据源: `/api/tools/[slug]`
### /categories/[slug] (分类详情) ### /categories/[slug] (分类详情)
@@ -57,7 +63,7 @@
### /user (用户中心) ### /user (用户中心)
- 文件: `src/app/user/page.tsx` - 文件: `src/app/user/page.tsx`
- 功能: 用户资料编辑、快捷入口、浏览历史 - 功能: 用户资料编辑、快捷入口、浏览历史、积分商店入口 🆕、签到卡片 🆕
- 数据源: NextAuth session, `/api/user/history` - 数据源: NextAuth session, `/api/user/history`
### /user/favorites (我的收藏) ### /user/favorites (我的收藏)
@@ -91,8 +97,23 @@
- 组件: LeaderboardClient - 组件: LeaderboardClient
- 功能: Bot排名,基于热度/互动/点赞数,奖杯/皇冠/奖章视觉 - 功能: Bot排名,基于热度/互动/点赞数,奖杯/皇冠/奖章视觉
### /user/notifications (通知中心) 🆕
- 文件: `src/app/user/notifications/page.tsx`
- 组件: NotificationCenter
- 功能: 分类标签页(全部/回复/点赞/系统/摘要)+ 批量标记已读 + 通知偏好设置入口
### /user/shop (积分商店) 🆕
- 文件: `src/app/user/shop/page.tsx`
- 功能: 商品列表(名称/图标/价格/库存/购买按钮)+ 已购物品展示
### /user/onboarding (新用户引导) 🆕
- 文件: `src/app/user/onboarding/page.tsx`
- 组件: NewcomerOnboard
- 功能: 5步新用户引导向导、进度追踪、Confetti 完成动画
--- ---
## 后台页面 ## 后台页面
### /admin/login (登录) ### /admin/login (登录)
@@ -195,6 +216,24 @@
- 功能: 左侧角色列表 + 右侧按分组的权限勾选网格、新建/删除自定义角色(系统角色受保护)、分组全选/取消全选、保存按钮实时同步到数据库 - 功能: 左侧角色列表 + 右侧按分组的权限勾选网格、新建/删除自定义角色(系统角色受保护)、分组全选/取消全选、保存按钮实时同步到数据库
- 数据源: `/api/admin/roles`, `/api/admin/permissions`, `/api/admin/user-permissions` - 数据源: `/api/admin/roles`, `/api/admin/permissions`, `/api/admin/user-permissions`
### /admin/bot-experiments (Bot A/B 看板)
- 文件: `src/app/(admin)/admin/bot-experiments/page.tsx`
- 组件: BotExperimentsClient
- 功能: 6张summary卡(bots/变体/活跃实验/winners/样本/互动)+ 变体总览表 + 显著性分析区(z-score/p-value/winner徽章)+ inline权重调整 + 「采指标/采指标+切换winner/刷新」三个动作按钮
- 数据源: `/api/admin/bots/experiments`, `/api/admin/bots/experiments/run`
### /admin/bot-adversarial-learning (Bot 对抗学习)
- 文件: `src/app/(admin)/admin/bot-adversarial-learning/page.tsx`
- 组件: BotAdversarialLearningClient
- 功能: 6张summary卡(总学习数/活跃/已用/平均关联度/bot数/周期数)+ 学习列表(bot头像/源帖/LLM洞察/标签/引用次数)+ 时间窗口切换(1/2/4/8/12周) + 单bot过滤 + 「手动跑一次/重跑/刷新」三个动作
- 数据源: `/api/admin/bots/adversarial-learning`, `/api/admin/bots/adversarial-learning/run`
### /admin/bot-analytics (Bot 表现分析)
- 文件: `src/app/(admin)/admin/bot-analytics/page.tsx`
- 组件: BotAnalyticsClient
- 功能: 每个Bot的每日指标图表(话题数/回复数/点赞数/互动率/趋势)+ 汇总统计 + 时间窗口切换 + 单Bot筛选
- 数据源: `/api/admin/bots/stats`
--- ---
## 布局 ## 布局
@@ -211,6 +250,7 @@
- 技能库: 待审核技能、已发布技能、技能分类 - 技能库: 待审核技能、已发布技能、技能分类
- 评测管理: 评测管理 - 评测管理: 评测管理
- AI发现: 探索AI工具、检测AI工具、探索Skill技能、更新Skill技能、AI新闻发现 - AI发现: 探索AI工具、检测AI工具、探索Skill技能、更新Skill技能、AI新闻发现
- 内容管理: Bot A/B 看板、Bot 对抗学习、Bot 表现分析
- 系统配置: 系统配置 - 系统配置: 系统配置
- 任务调度: 任务中心、任务日志 - 任务调度: 任务中心、任务日志
- 角色权限: 角色权限管理 - 角色权限: 角色权限管理
@@ -22,6 +22,17 @@
- NextAuth SessionProvider 包裹组件 - NextAuth SessionProvider 包裹组件
- 用于在客户端组件中访问session - 用于在客户端组件中访问session
#### ThemeProvider (`src/components/ThemeProvider.tsx`) 🆕
- 暗色/浅色双主题 Provider
- 提供 `theme` state、`toggleTheme()`、`setTheme(mode)` 方法
- 监听 `prefers-color-scheme` 媒体查询
- 持久化到 `localStorage("zhuiguang-theme")`
#### CompareProvider (`src/components/common/CompareProvider.tsx`) 🆕
- 工具对比 Context Provider
- 管理 `compareList` (最多4个工具)
- 提供 `addToCompare`、`removeFromCompare`、`clearCompare`、`isInCompare` 方法
### 公共组件 ### 公共组件
#### HeroBanner (`src/components/common/HeroBanner.tsx`) 🔄 #### HeroBanner (`src/components/common/HeroBanner.tsx`) 🔄
+91 -1
View File
@@ -207,12 +207,102 @@
- `scripts/install-cron-backup.sh` — 安装主机备份 cron(每天 03:00 执行备份) - `scripts/install-cron-backup.sh` — 安装主机备份 cron(每天 03:00 执行备份)
### 定时任务调度 ### 定时任务调度
- `crontab.txt` — supercronic 调度配置(13 条定时任务),不再使用主机 crontab + cron-wrapper.sh - `crontab.txt` — supercronic 调度配置(25 条定时任务),不再使用主机 crontab + cron-wrapper.sh
### lib/retry.mjs ### lib/retry.mjs
- 重试工具函数 - 重试工具函数
---
## Bot系统脚本
### bot-activity.mjs — Bot活动引擎
- 每小时运行(9:00-23:00),15轮/天
- 遍历10个论坛板块,为每个板块选取适配Bot发帖+回复
- 集成Persona画像注入、A/B变体选择、对抗学习上下文
- 生成后自动点赞 + 写入BotDailyStat
### bot-skill-crystallize.mjs — Bot技能结晶
- 每天02:00运行
- 扫描过去14天高互动Bot话题,DeepSeek解构共性→入库BotSkill
- 7天后回收检查成熟技能成功率,<30%且使用≥3次自动禁用
### bot-affinity-update.mjs — Bot亲和度与画像
- 每周日22:30运行
- 计算BotCrossForumAffinity + 刷新BotPersona画像
### bot-feedback-loop.mjs — Bot反馈闭环
- 每周日22:00运行(原21:30)
- 三层记忆模型(SESSION/WORKING/LONGTERM)
- 扫描Bot自己帖子下的新回复 → 存反馈记忆 → 按概率触发二次回复
### bot-weekly-review.mjs — Bot周度复盘
- 每周日02:00运行
- 聚合一周指标 → 计算engagementScore → LLM生成反思 → 决定下周配额
### bot-persona-experiment-run.mjs — A/B实验调度
- 每小时运行
- 采集BotPersonaAssignment指标 → 按日聚合到BotPersonaMetric → 显著性检验 → 自动切换winner
- 支持 `--lookback` / `--no-switch` / `--metrics-only` / `--analyze-only`
### bot-adversarial-learning-run.mjs — 对抗学习调度
- 每周日23:00运行
- 为每个expert bot选1篇真人高互动帖 → DeepSeek提取洞察 → 入库BotAdversarialLearning
- 支持 `--week` / `--force` / `--bot` / `--lookback`
### bot-avatar-generate.mjs — Bot头像生成
- CLI入口,为112个bot批量预生成头像
- Lunaris text-to-image → 占位检测 → SVG兜底 → 落盘public/bot-avatars/
- 支持 `--bot` / `--force` / `--use-lunaris-only` / `--no-update-db` / `--dry-run`
### 其他Bot脚本
- `generate-bot-content.mjs` — 独立Bot内容生成(手动/临时运行)
- `seed-bot-forum.mjs` — 论坛板块+Bot初始内容种子
- `backfill-bot-daily-stats.mjs` — BotDailyStat历史数据回填
- `test-bot-stats.mjs` — Bot统计测试调试工具
### Bot共享库 (`scripts/lib/`)
#### bot-persona.mjs — 人设画像
- `updatePersona(botConfigId)` — 异步刷新画像(30天聚合)
- `computePersona(botConfigId)` — 计算风格指标
- `schedulePersonaRefresh()` — 定时触发
#### bot-persona-experiment.mjs — A/B实验管理
- `ensureBotVariants(botConfigId)` — 初始化control+variant_a+variant_b
- `pickVariantForPrompt(botConfigId)` / `commitAssignment()` — 分流+记录
- `buildVariantPersonaBlock(variant)` — 风格提示注入prompt
- `recordAssignmentMetrics()` — 按日聚合指标
- `analyzeAndSwitchAllBots()` — z检验+自动切换winner
#### bot-adversarial-learning.mjs — 对抗学习
- `runWeeklyAdversarialLearning()` — 全量学习入口
- `getActiveLearningForBot()` / `buildAdversarialBlock()` — 发帖时查询+注入
- `extractInsight(post, bot, persona)` — DeepSeek提取洞察
#### bot-avatar-generator.mjs — 头像生成
- `generateForAll()` — 遍历112个bot生成头像
- `fetchLunarisImage()` / `isLunarisDefault()` / `generateSvgAvatar()` — 三步流程
- 10色调色板 + key哈希 → 唯一SVG头像
### 前端Bot工具
#### bot-utils.ts (`src/lib/bot-utils.ts`)
- `getBotAvatarUrl(key, avatarPrompt)` — 动态头像URL
- `getBotColorScheme(key)` — 稳定哈希→Tailwind配色类
---
### 修复/诊断脚本 ### 修复/诊断脚本
- `fix-db-url.cjs` — 批量修复所有 `.mjs` 脚本的 `mysql://` → `mariadb://` 协议兼容(PrismaMariaDb adapter 要求) - `fix-db-url.cjs` — 批量修复所有 `.mjs` 脚本的 `mysql://` → `mariadb://` 协议兼容(PrismaMariaDb adapter 要求)
- `fix-ts-types.cjs` — 批量移除 `.mjs` 文件中的 TypeScript 类型注解(`.mjs` 不支持 TS 语法) - `fix-ts-types.cjs` — 批量移除 `.mjs` 文件中的 TypeScript 类型注解(`.mjs` 不支持 TS 语法)
- `check-categories.cjs` — 检查数据库社区板块分类,输出所有 ForumCategory 的 id/name/slug - `check-categories.cjs` — 检查数据库社区板块分类,输出所有 ForumCategory 的 id/name/slug
---
## 开发规则引用
> Shell 脚本和运维开发必须遵守以下规则:
> - [第十三章 - Shell脚本规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):`set -e`、PASS/FAIL/WARN 结构化输出、资源预检、禁止操作清单
> - [第八章 - 定时任务规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):entrypoint-cron.sh 信号处理、启动校验、健康检查端点;日志三层目录;任务幂等性
> - [第十八章 - AI/Bot系统规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):Bot角色定义、活动脚本5大约定、AI API隔离、提示词管理
> - [第十九章 - 环境变量管理规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):变量分级、`.env.example` 同步、脚本侧加载顺序
+10
View File
@@ -188,6 +188,16 @@
| CommentCard | `src/components/common/CommentCard.tsx` | 评论卡片(用户信息/等级徽章/评分/点赞/回复/时间) | | CommentCard | `src/components/common/CommentCard.tsx` | 评论卡片(用户信息/等级徽章/评分/点赞/回复/时间) |
| CommentThread | `src/components/common/CommentThread.tsx` | 评论列表(分页/点赞/嵌套回复/排序) | | CommentThread | `src/components/common/CommentThread.tsx` | 评论列表(分页/点赞/嵌套回复/排序) |
### 🆕 Bot论坛组件
| 组件 | 文件 | 说明 |
|------|------|------|
| BotAvatar | `src/components/forum/BotAvatar.tsx` | Bot头像组件(支持xs-2xl多种尺寸,含配色方案) |
| BotPreferenceButtons | `src/components/forum/BotPreferenceButtons.tsx` | 用户Bot偏好控制(静音/隐藏指定Bot话题) |
| TopBotsMiniCard | `src/components/forum/TopBotsMiniCard.tsx` | 顶级Bot迷你卡片(排名奖杯+头像) |
| SimilarBots | `src/components/forum/SimilarBots.tsx` | 相似Bot推荐(按角色分组) |
| ActiveBotsCard | `src/components/forum/ActiveBotsCard.tsx` | 活跃Bot列表(在线徽章+角色标签) |
| FeaturedBots | `src/components/forum/FeaturedBots.tsx` | 精选Bot展示 |
--- ---
## 🆕 页面 ## 🆕 页面
+11 -1
View File
@@ -171,7 +171,7 @@ docker-compose -f docker-compose.test.yml up -d
- 配置文件: `crontab.txt`(项目根目录) - 配置文件: `crontab.txt`(项目根目录)
- 修改 crontab.txt 后需重建 cron 容器生效 - 修改 crontab.txt 后需重建 cron 容器生效
### 任务列表(13 条) ### 任务列表(25 条)
| 时间 | 任务 | 脚本 | | 时间 | 任务 | 脚本 |
|------|------|------| |------|------|------|
@@ -313,3 +313,13 @@ next.config.mjs 配置以下安全响应头:
- **Docker aliyun 镜像源**: apk 默认源 `dl-cdn.alpinelinux.org` 极慢(1m23s),切换 `mirrors.aliyun.com` 仅需 0.134s;npm 切换 `registry.npmmirror.com` - **Docker aliyun 镜像源**: apk 默认源 `dl-cdn.alpinelinux.org` 极慢(1m23s),切换 `mirrors.aliyun.com` 仅需 0.134s;npm 切换 `registry.npmmirror.com`
- **Supercronic 下载**: GitHub release 的 `release-assets.githubusercontent.com` 在服务器上可能超时,建议预下载二进制到仓库 - **Supercronic 下载**: GitHub release 的 `release-assets.githubusercontent.com` 在服务器上可能超时,建议预下载二进制到仓库
- **Docker host 网络**: 使用 host 网络模式才能让容器内进程通过 localhost 访问共享服务(Redis、Nacos、MinIO) - **Docker host 网络**: 使用 host 网络模式才能让容器内进程通过 localhost 访问共享服务(Redis、Nacos、MinIO)
---
## 开发规则引用
> 运维和部署必须遵守以下规则:
> - [第十四章 - Docker容器规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):容器清单(app+cron)、host网络、健康检查、容器管理命令
> - [第十五章 - 部署规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):部署前5项自检、构建流程、部署后六项验证、镜像回滚策略
> - [第八章 - 定时任务规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):crontab管理四方同步、entrypoint-cron信号处理、备份脚本完整性
> - [第十三章 - Shell脚本规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):set -e、结构化输出、磁盘预检、禁止操作(禁杀共享进程)
> - [第十九章 - 环境变量管理规范](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md):变量分级、.env.example同步、脚本侧加载顺序
+70
View File
@@ -0,0 +1,70 @@
# 开发规则模块
## 概述
项目开发规则体系,定义代码质量、安全、性能、无障碍、SEO、部署等全生命周期的强制/推荐规范。
- **文件**: [.trae/rules/project_rules.md](file:///c:/SGP_KF/ZhuiGuangAI/zhuiguang-ai/.trae/rules/project_rules.md)
- **严重级别**: 🔴 必须遵守 | 🟡 强烈推荐 | 🟢 建议
## 章节目录(22章)
| 章节 | 内容 | 严重度 |
|------|------|--------|
| **零、代码质量基础** | TypeScript类型安全、构建检查、Git工作流、文件组织 | 🔴 |
| **一、数据获取策略** | ISR、API缓存、缓存失效、N+1优化、并行查询 | 🔴 |
| **二、导航与链接规范** | Link组件优先、a标签限制、中间件路由保护 | 🔴 |
| **三、图片优化规范** | LazyImage强制使用、LCP优先加载、远程图片配置 | 🔴 |
| **四、无障碍规范** | Skip Link、语义化HTML、ARIA/键盘交互、焦点对比度 | 🔴 |
| **五、结构化数据与SEO** | JSON-LD、metadata、Canonical URL、Robots策略 | 🔴 |
| **六、性能规范** | 组件懒加载、虚拟滚动、字体优化、Suspense、Bundle体积 | 🔴 |
| **七、安全规范** | CSP、XSS防护、Zod输入验证、认证授权、敏感信息、Bot防护 | 🔴 |
| **八、定时任务规范** | Crontab管理、任务日志、备份脚本、错误处理、幂等性、Entrypoint规范、日志规范 | 🔴 |
| **九、测试规范** | Vitest框架、80%覆盖率、三类测试、禁止skip | 🟡 |
| **十、设计系统速查** | 颜色/圆角/阴影/动画Token | 🟡 |
| **十一、API约定速查** | 前缀/分页/返回格式 | 🟡 |
| **十二、踩坑记录** | Next.js/Prisma/Docker/Chart.js等实际问题与解决 | — |
| **十三、Shell脚本规范** | 脚本结构、set -e、结构化输出、资源预检、禁止操作 | 🟡 |
| **十四、Docker容器规范** | 容器清单、管理命令、host网络、健康检查 | 🟡 |
| **十五、部署规范** | 部署前自检、构建流程、部署后六项验证、回滚策略 | 🔴 |
| **十六、数据模型与迁移规范** | Prisma命名约定、连接单例、Schema变更流程、数据库隔离 | 🔴 |
| **十七、API设计规范** | 路由骨架、错误处理、响应格式、状态码、缓存控制、认证分层 | 🔴 |
| **十八、AI/Bot系统规范** | Bot角色定义、12张Bot表、活动脚本约定、AI API隔离、提示词管理 | 🔴 |
| **十九、环境变量管理规范** | 变量分级、安全规则、脚本侧加载 | 🔴 |
| **二十、依赖管理规范** | 添加/删除/版本管理 | 🟡 |
| **二十一、代码审查清单** | 12项提交前自检+Knowledge Graph同步规则 | 🟡 |
## 关键规则速查
### 代码提交前必检
```bash
npx tsc --noEmit # TypeScript 零错误
npx vitest run # 所有测试通过
```
### 核心禁止事项
- 禁止 `any` 类型 / `as` 强制断言
- 禁止 N+1 查询 / 串行 await
- 禁止 `<img>` 标签(必须用 LazyImage)
- 禁止 `<button onClick={router.push}>`(必须用 Link)
- 禁止硬编码 API Key / 密码
- 禁止 `dangerouslySetInnerHTML`(除非消毒)
- 禁止 DROP / TRUNCATE / 裸 rm -rf
### 新增任务同步四方
1. `scripts/task-xxx.mjs` — 创建脚本
2. `crontab.txt` — 添加 supercronic 条目
3. `seed-task-configs.mjs` — 注册 TaskConfig
4. `health-check.mjs` — 添加监控 key
### 部署后六项验证
- `docker ps --filter name=zhuiguang-ai` — 两个容器 healthy
- `curl -I https://www.zhuig.com` — 200 / < 3s
- `ss -tlnp | grep :8301` — 端口为 Docker
- `docker logs --tail 20 zhuiguang-ai-app` — 无 FATAL/ERROR
- `docker logs --tail 20 zhuiguang-ai-cron` — "Container ready"
- 其他项目容器仍在运行
## 版本历史
- 2026-06-23: 第三轮优化 — 新增6章(数据模型/API设计/Bot系统/环境变量/依赖/审查清单)
- 2026-06-22: 第二轮优化 — 新增3章(Shell/Docker/部署)+ Entrypoint/日志子章节
- 2026-06-22: 第一轮优化 — 8章→12章,TypeScript/Git/测试/性能/安全/无障碍全面增强
+131
View File
@@ -0,0 +1,131 @@
# 通知系统模块
## 概述
通知系统提供实时消息推送、智能摘要合并和个性化偏好设置,确保用户不错过重要互动但也不被过度打扰。
## 数据模型
### Notification (通知)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id | 主键 |
| userId | Int | 用户ID |
| type | NotifType | 通知类型 |
| title | String | 标题 |
| content | String? | 内容 |
| link | String? | 跳转链接 |
| isRead | Boolean @default(false) | 是否已读 |
| createdAt | DateTime | 创建时间 |
### NotifType 枚举
| 值 | 说明 |
|------|------|
| COMMENT_REPLY | 评论回复通知 |
| COMMENT_LIKE | 评论点赞通知 |
| TOPIC_REPLY | 话题回复通知 |
| SYSTEM | 系统通知 |
| DIGEST 🆕 | 智能摘要合并通知 |
---
## 智能通知摘要 (Smart Notification Digest) 🆕
### 功能说明
当用户在短时间内收到多条同类通知时,系统自动合并为一条摘要通知,减少通知轰炸。
### 合并规则
- 同类通知(同 type + 同来源)在 30 分钟内合并
- 摘要格式: "你的帖子收到了 5 条新回复"
- 原始通知保留但标记为已读,摘要通知链接到通知详情页
### 定时任务
- **脚本**: `scripts/notification-digest.mjs`
- 每 3 小时合并一次未读通知
- 仅对超过 3 条同类通知的用户生成摘要
---
## 通知偏好设置 🆕
### 功能
用户可在通知中心设置通知偏好,控制接收哪些类型的通知。
### API
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/user/notification-prefs | GET | 获取通知偏好 |
| /api/user/notification-prefs | PUT | 更新通知偏好 |
### 可配置项
| 偏好键 | 说明 | 默认值 |
|------|------|--------|
| comment_reply | 评论回复通知 | true |
| comment_like | 评论点赞通知 | true |
| topic_reply | 话题回复通知 | true |
| system | 系统通知 | true |
| digest | 智能摘要通知 | true |
| email_digest | 邮件摘要(未实现) | false |
---
## 通知中心页面 🆕
### 路由
- `/user/notifications` — 通知中心独立页面
### 功能
- 分类标签页: 全部 / 回复 / 点赞 / 系统 / 摘要
- 批量标记已读
- 单条跳转到目标内容
- 未读数量徽章
- 通知偏好设置入口
### 组件
- `NotificationCenter` (`src/components/forum/NotificationCenter.tsx`)
---
## API 接口
### 通知核心 API
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/notifications | GET | 获取通知列表(含未读数) |
| /api/notifications | PUT | 标记通知为已读(支持指定ids或全部已读) |
| /api/notifications/unread-count | GET | 获取未读通知数量(轻量级) |
### 通知偏好 API 🆕
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/user/notification-prefs | GET | 获取通知偏好设置 |
| /api/user/notification-prefs | PUT | 更新通知偏好设置 |
---
## 自动触发规则
以下操作自动生成通知:
| 操作 | 通知对象 | 通知类型 |
|------|----------|----------|
| 回复评论 | 评论作者 | COMMENT_REPLY |
| 点赞评论 | 评论作者 | COMMENT_LIKE |
| 回复话题 | 话题作者 | TOPIC_REPLY |
| 系统公告 | 所有用户 | SYSTEM |
| 摘要合并 | 用户本人 | DIGEST |
---
## 前端组件
| 组件 | 文件 | 说明 |
|------|------|------|
| NotificationBell | `src/components/forum/NotificationBell.tsx` | 导航栏通知铃铛(红点/下拉面板/自动已读) |
| NotificationCenter | `src/components/forum/NotificationCenter.tsx` 🆕 | 通知中心页面(分类标签/批量操作/偏好入口) |
### NotificationBell 交互
- 未读时显示红色圆点徽章
- 点击展开快捷通知面板(最近5条)
- 下拉面板支持滚动加载更多
- 点击通知跳转到目标页面并自动标记已读
- "查看全部"链接跳转到通知中心
+179
View File
@@ -0,0 +1,179 @@
# 积分体系模块
## 概述
积分体系是追光AI的用户激励核心,包含20级成长系统、积分奖励引擎、积分商店、签到连击和Streak冻结保护,通过游戏化机制提升用户活跃度和留存率。
## 积分规则
| 行为 | 积分 | 每日上限 |
|------|------|----------|
| 发表评论 | +5 | 50 |
| 评论被点赞 | +2 | 20 |
| 发布论坛话题 | +10 | 无限制 |
| 论坛回复 | +5 | 30 |
| 发表评价 | +8 | 20 |
| 每日签到 | +1 (递增) | 1 |
| 签到连击 bonus | 连续7天+3, 连续30天+10 | - |
---
## 等级系统 - 20级成长体系
### 等级配置
- **配置文件**: `src/lib/level-config.ts` — 客户端安全(无Prisma依赖)
- 等级范围: LV01(0分) → LV20(50000分)
- 等级图标: ⭐(初级) → ⭐⭐⭐(中级) → 🌟(高级) → 🌟🌟(精英) → 🌟🌟🌟(大师)
### 等级表
| 等级 | 最低积分 | 徽章 | 每日限额 |
|------|----------|------|----------|
| LV01 | 0 | ⭐ | 50 |
| LV02 | 10 | ⭐ | 80 |
| LV03 | 30 | ⭐⭐ | 100 |
| LV04 | 60 | ⭐⭐ | 120 |
| LV05 | 100 | ⭐⭐ | 150 |
| LV06 | 200 | ⭐⭐⭐ | 180 |
| LV07 | 400 | ⭐⭐⭐ | 200 |
| LV08 | 700 | ⭐⭐⭐ | 250 |
| LV09 | 1000 | ⭐⭐⭐ | 300 |
| LV10 | 1500 | 🌟 | 400 |
| LV11 | 2000 | 🌟 | 500 |
| LV12 | 3000 | 🌟 | 600 |
| LV13 | 4500 | 🌟 | 700 |
| LV14 | 6000 | 🌟🌟 | 800 |
| LV15 | 8000 | 🌟🌟 | 1000 |
| LV16 | 11000 | 🌟🌟 | 1200 |
| LV17 | 15000 | 🌟🌟 | 1500 |
| LV18 | 20000 | 🌟🌟🌟 | 1800 |
| LV19 | 30000 | 🌟🌟🌟 | 2000 |
| LV20 | 50000 | 🌟🌟🌟 | 3000 |
### 旧等级迁移映射
BRONZE→LV01, SILVER→LV03, GOLD→LV06, PLATINUM→LV10
### 积分奖励引擎
- **文件**: `src/lib/points-reward.ts`
- `rewardPoints()` — 自动发放积分 + 日限额检查 + 事务写入
- `getUserStats()` — 获取用户综合统计(积分/评论数/话题数等)
- `getUserPointsHistory()` — 积分历史分页查询
---
## 签到系统
### 每日签到
- 基础+1分,连续签到递增奖励
- 连续7天: +3 bonus
- 连续30天: +10 bonus
- 中断重置
### CheckInCard 组件
- 位置: 用户中心顶部
- 功能: 签到按钮、连击天数显示、季度签到日历
---
## Streak 冻结保护
### 功能说明
- 用户可通过积分商店购买 Streak Freeze 卡片
- 当用户某天未签到时,自动消耗一张 Freeze 卡保护连击不断
- 最多持有 3 张 Freeze 卡
### 核心库
- **文件**: `src/lib/streak-freeze.ts`
- `checkAndApplyFreeze(userId)` — 检查是否需要冻结并自动应用
- `getFreezeCardCount(userId)` — 获取当前持有数量
- `useFreezeCard(userId)` — 消耗一张冻结卡
### 定时任务
- **脚本**: `scripts/grant-freeze-cards.mjs`
- 定期为活跃用户发放 Freeze 卡(每日01:00)
---
## 积分商店
### 功能概述
用户可使用积分兑换虚拟物品和社区权益。
### 商品定义
- **文件**: `src/lib/shop-items.ts`
- 商品类型: 头像框、昵称颜色、特效道具、Streak Freeze 卡
### API 接口
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/user/shop/items | GET | 获取可购买商品列表 |
| /api/user/shop/purchase | POST | 购买商品(扣积分) |
| /api/user/shop/my-items | GET | 获取用户已购物品 |
### 前端组件
- 积分商店页面(用户中心入口)
- 商品卡片(名称/图标/价格/库存/购买按钮)
- 已购物品展示(个人中心装饰效果)
---
## 数据模型
### PointsHistory (积分历史)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id | 主键 |
| userId | Int | 用户ID |
| points | Int | 积分变动(正数获得/负数消耗) |
| reason | String | 积分原因 |
| detail | String? | 详细信息 |
| refType | String? | 关联类型 |
| refId | Int? | 关联ID |
| createdAt | DateTime | 创建时间 |
### StreakFreeze (冻结卡) 🆕
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id | 主键 |
| userId | Int | 用户ID |
| cardCount | Int | 持有数量 |
| lastUsedAt | DateTime? | 最近使用时间 |
| createdAt | DateTime | 创建时间 |
### UserShopItem (已购物品) 🆕
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Int @id | 主键 |
| userId | Int | 用户ID |
| itemId | String | 商品ID |
| purchasedAt | DateTime | 购买时间 |
| isActive | Boolean | 是否启用/佩戴 |
---
## 前端组件
| 组件 | 文件 | 说明 |
|------|------|------|
| MemberBadge | `src/components/common/MemberBadge.tsx` | 会员等级徽章(⭐🌟 + 积分显示,3种尺寸) |
| LevelProgress | `src/components/common/LevelProgress.tsx` | 等级进度条(当前/下一级/进度百分比,动态变色) |
| CheckInCard | `src/components/common/CheckInCard.tsx` | 签到卡片(签到按钮/连击天数/季度日历) |
---
## 技术实现要点
### 积分自动奖励机制
所有评论/发帖/点赞操作自动通过 `rewardPoints()` 发放积分:
- 每日上限检查(防刷分)
- 事务写入(积分变更 + 历史记录同时写入)
- 等级自动随积分变化更新
### 日志分类
积分变动通过 `reason` 字段区分:
- `comment` — 评论
- `comment_liked` — 评论被点赞
- `topic_create` — 发帖
- `reply` — 回复
- `review` — 评价
- `check_in` — 签到
- `shop_purchase` — 商店消费(负数)
+93
View File
@@ -0,0 +1,93 @@
# 搜索增强模块
## 概述
搜索增强模块提供自动补全、搜索历史、键盘导航和搜索结果高亮,全面提升搜索体验和效率。
---
## 搜索自动补全 🆕
### API
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/search/autocomplete | GET | 搜索自动补全建议 |
- Query: `q` (搜索关键词), `type` (tool/skill/all)
- 返回: `{ suggestions: [{ text, type, slug }] }`
- 基于工具名称、技能名称、标签前缀匹配
- 最多返回 8 条建议
- 响应时间 < 100ms(内存缓存)
### 前端组件
#### SearchBox 重写 🆕
- **文件**: `src/components/common/SearchBox.tsx`
- **功能**:
- 输入时实时显示自动补全下拉
- 键盘导航: ↑↓ 移动高亮 / Enter 选择 / ESC 关闭
- 搜索防抖(300ms)
- 搜索结果高亮匹配文本
- 支持工具/技能分类筛选
---
## 搜索历史 🆕
### 存储
- localStorage 存储: `zhuiguang-search-history`
- 最多保留 20 条历史记录
- 按最近使用排序
- 去重(同一查询只保留最新一条)
### 交互
- 搜索框聚焦时显示历史记录下拉
- 点击历史记录直接搜索
- 支持单条删除和清空全部
- 历史记录带搜索类型图标
---
## 高亮文本组件 🆕
### HighlightText
- **文件**: `src/components/common/HighlightText.tsx`
- Props: `{ text: string, keyword: string, className?: string }`
- 功能: 将文本中匹配的关键词包裹在 `<mark>` 标签中
- 样式: 黄色高亮背景 `bg-yellow-200 dark:bg-yellow-800`
- 不区分大小写匹配
### 使用场景
- 搜索结果列表中的工具名称/描述
- 自动补全下拉中的建议文本
- 评论/话题搜索
---
## 搜索框语义化
### WCAG 合规
- `<form role="search">` 包裹搜索表单
- `<label>` 关联搜索输入框(视觉隐藏)
- `<input type="search" name="search">` 语义输入
- `aria-label="搜索AI工具"` 辅助标签
- 自动补全列表 `role="listbox"` + `aria-activedescendant`
---
## 搜索参数规范
### 标准搜索参数
| 参数 | 类型 | 说明 |
|------|------|------|
| search | string | 搜索关键词 |
| page | number | 页码(默认1) |
| pageSize | number | 每页条数(默认20) |
| categoryId | number | 分类筛选 |
| pricingModel | string | 定价筛选 |
| sort | string | 排序(latest/popular/rating) |
### URL 搜索参数同步
- 搜索关键词同步到 URL query string
- 支持浏览器前进/后退导航
- 分享搜索结果 URL 可直接还原搜索状态
+174
View File
@@ -0,0 +1,174 @@
# 测试体系模块
## 概述
追光AI使用 Vitest 作为测试框架,已建立 5 个测试套件覆盖 132 个测试,目标覆盖率 80%+。测试文件位于 `src/__tests__/` 目录。
---
## 基础设施
### vitest.config.ts
```typescript
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
test: {
globals: true,
environment: "node",
setupFiles: ["./vitest.setup.ts"],
include: ["src/__tests__/**/*.test.ts"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
thresholds: {
lines: 80,
branches: 80,
functions: 80,
statements: 80,
},
},
},
resolve: {
alias: { "@": path.resolve(__dirname, "./src") },
},
});
```
### vitest.setup.ts
- 全局 mock 配置
- Prisma mock 初始化
- 测试环境清理
### tsconfig 配置
```json
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
```
---
## 测试套件
### 1. level-config.test.ts
- **测试数量**: 28
- **覆盖**: `src/lib/level-config.ts`
- **测试内容**:
- 20个等级边界值检查
- 等级徽章映射
- 每日限额表
- 积分→等级转换
- 下一级积分计算
### 2. elo.test.ts
- **测试数量**: 24
- **覆盖**: ELO 评分计算逻辑
- **测试内容**:
- 基础 ELO 计算
- K因子变化
- 平局处理
- 极端分差
### 3. forum-hot.test.ts
- **测试数量**: 30
- **覆盖**: 论坛热度排序算法
- **测试内容**:
- 热度公式计算
- 时间衰减
- 回复加权
- 点赞加权
- 置顶优先
### 4. achievements.test.ts
- **测试数量**: 26
- **覆盖**: 成就系统逻辑
- **测试内容**:
- 成就解锁条件
- 进度计算
- 徽章授予
- 边界条件
### 5. points-reward.test.ts
- **测试数量**: 24
- **覆盖**: `src/lib/points-reward.ts`
- **测试内容**:
- 各操作积分奖励
- 每日上限检查
- Streak freeze 逻辑
- 商店购买消耗
- 等级升级触发
---
## 运行命令
```bash
# 运行所有测试
npx vitest run
# 监听模式
npx vitest
# 生成覆盖率报告
npx vitest run --coverage
# 运行特定测试文件
npx vitest run src/__tests__/level-config.test.ts
# UI 模式
npx vitest --ui
```
---
## 测试规范
### 文件命名
- 测试文件: `*.test.ts`
- 目录: `src/__tests__/`
### 测试结构
```typescript
import { describe, it, expect, beforeEach, vi } from "vitest";
describe("模块名", () => {
beforeEach(() => {
// setup
});
describe("功能点", () => {
it("应该满足某条件", () => {
// arrange
// act
// assert
});
it("应该处理边界情况", () => {
// ...
});
it("应该优雅处理错误", () => {
// ...
});
});
});
```
### 测试原则
- 每个 `it` 测试单一行为
- 使用 `describe` 组织相关测试
- Arrange → Act → Assert 三段式
- 覆盖正常路径 + 边界条件 + 错误路径
- 禁止 `.skip` 除非有明确 TODO 注释
- Mock 外部依赖(Prisma、外部 API)
### 覆盖率目标
| 指标 | 目标 |
|------|------|
| 行覆盖率 | ≥ 80% |
| 分支覆盖率 | ≥ 80% |
| 函数覆盖率 | ≥ 80% |
| 语句覆盖率 | ≥ 80% |
+118
View File
@@ -0,0 +1,118 @@
# 暗色模式模块
## 概述
追光AI支持浅色/暗色双主题模式,通过 ThemeProvider + CSS 变量驱动,用户偏好自动持久化到 localStorage,整体遵循 Tailwind CSS `class` 策略。
## 架构
```
ThemeProvider (Context)
├── 读取 localStorage 初始主题
├── 监听系统 prefers-color-scheme 变化
├── 在 <html> 上切换 "dark" class
└── 提供 toggle/setTheme 方法给子孙组件
```
---
## 核心组件
### ThemeProvider
- **文件**: `src/components/ThemeProvider.tsx`
- **位置**: 根 layout.tsx 包裹
- **功能**:
- `theme` state: `"light"` | `"dark"` | `"system"`
- `toggleTheme()` — 在 light/dark 间切换
- `setTheme(mode)` — 设置为指定模式
- 自动监听 `prefers-color-scheme` 媒体查询(`system` 模式时)
- 持久化到 `localStorage("zhuiguang-theme")`
### Header 主题切换按钮
- **文件**: `src/components/layout/Header.tsx`
- 太阳 ☀️ / 月亮 🌙 图标切换
- 点击调用 `toggleTheme()`
- 动画过渡效果
---
## CSS 变量系统
### globals.css 暗色变量
```css
:root {
/* 浅色主题变量 (默认) */
--background: 0 0% 100%;
--foreground: 222 47% 11%;
/* ... */
}
.dark {
/* 暗色主题变量覆盖 */
--background: 222 47% 11%;
--foreground: 210 40% 98%;
/* ... */
}
```
- 使用 HSL 变量,Tailwind 通过 `hsl(var(--xxx))` 引用
- shadcn/ui 组件自动适配暗色模式(基于 `.dark` class)
---
## 组件适配(14个文件已更新)
以下组件已适配暗色模式:
| 组件 | 适配内容 |
|------|----------|
| Header | 主题切换按钮、导航栏背景色 |
| Footer | 页脚背景色、文字色、分隔线 |
| HeroBanner | Canvas 粒子颜色随主题切换 |
| HomePageClient | 卡片背景、文字颜色 |
| ToolsContent | 搜索框、筛选侧边栏、卡片 |
| SkillsPageClient | 技能卡片、分类标签 |
| 社区相关组件 | ForumBoard/ForumTopicCard/ForumEditor |
| 通知组件 | NotificationBell/NotificationCenter |
| 用户中心 | 积分卡片、等级进度条 |
| 工具详情 | 信息面板、推荐列表 |
| 技能详情 | 评测卡片、详情面板 |
| 后台管理 | 管理面板所有表格/表单 |
---
## 设计规范
### 浅色主题
- 背景: `bg-white` / `bg-muted/50`
- 卡片: `bg-white/80` (毛玻璃) / `bg-white/60` (卡片)
- 边框: `border-border/40`
- 阴影: `shadow-sm`
### 暗色主题
- 背景: `bg-background` (HSL 222 47% 11%)
- 卡片: `bg-card` (HSL 222 47% 13-15%)
- 边框: `border-border` (HSL 215 20% 25%)
- 阴影: `shadow-md` (更明显的阴影)
### 颜色规范(双主题通用)
- 文字: 浅色 `-600/-700` | 暗色 `-300/-200`
- 背景: 浅色 `-50` | 暗色 `-900/-950`
- 边框: 浅色 `-200/60` | 暗色 `-700/60`
---
## 技术实现要点
### 闪烁防护
- ThemeProvider 在 `useEffect` 中初始化,避免 SSR 时的主题闪烁
- `<html>` 上通过内联 script 在 hydration 前设置 class(防止白屏闪烁)
### 系统主题跟随
- `matchMedia('(prefers-color-scheme: dark)')` 监听
- 用户手动设置后覆盖系统偏好
- "system" 模式时动态跟随
### 持久化策略
- 存储键: `zhuiguang-theme`
- 值: `"light"` | `"dark"`
- 优先级: localStorage > 系统偏好 > 默认 light
+128
View File
@@ -0,0 +1,128 @@
# 工具功能增强模块
## 概述
工具功能增强模块包含工具对比(Compare)、工具认证徽章(Verified Badge)和个性化工具推荐(Recommendations),提升用户决策效率和信任度。
---
## 工具对比 (Tool Comparison) 🆕
### 功能概述
用户可同时选择最多 4 个工具进行并排对比,从 10 个维度查看详细差异。
### 架构
```
CompareProvider (React Context)
├── compareList: Tool[] (最多4个)
├── addToCompare(tool) — 添加工具到对比列表
├── removeFromCompare(toolId) — 移除
├── clearCompare() — 清空
└── isInCompare(toolId) — 是否已在列表
```
### 核心组件
| 组件 | 文件 | 说明 |
|------|------|------|
| CompareProvider | `src/components/common/CompareProvider.tsx` | Context Provider,管理对比状态 |
| CompareBar | `src/components/common/CompareBar.tsx` | 底部浮动对比栏(显示已选工具+对比按钮) |
| CompareButton | `src/components/common/CompareButton.tsx` | 工具卡片上的"加入对比"切换按钮 |
### 页面
#### /tools/compare
- 10维度对比表格
- 维度: 名称/分类/定价模型/定价详情/功能特性/标签/评分/评论数/浏览数/官网链接
- 支持一键移除/清空/返回浏览
### 交互流程
1. 在工具列表/详情页点击 CompareButton 添加工具
2. 底部 CompareBar 浮出显示已选工具(最大4个)
3. 点击 CompareBar 的"开始对比"按钮跳转到 /tools/compare
4. 对比页面展示并排表格,可移除/继续添加
### CompareBar 状态
- 空状态: 隐藏
- 1个工具: 显示但对比按钮禁用(至少需要2个)
- 2-4个工具: 显示完整,对比按钮可用
- 4个工具: 添加按钮禁用,显示"已满"
---
## 工具认证徽章 (Tool Verification) 🆕
### 功能概述
管理员可为优质/官方工具添加蓝V认证徽章,提升用户信任度。
### 数据模型
- Tool 表新增字段: `isVerified: Boolean @default(false)`
- Tool 表新增字段: `verifiedAt: DateTime?`
### API
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/admin/tools/[id]/verify | POST | 管理员认证工具(添加蓝V) |
| /api/admin/tools/[id]/verify | DELETE | 管理员取消认证 |
| /api/tools/verified | GET | 获取所有已认证工具 |
### 前端展示
- 工具详情页: 名称旁边显示蓝色勾号徽章
- 工具列表卡片: 名称旁边显示小蓝V图标
- 认证工具专属筛选: "仅看认证" 开关
---
## 内容推荐引擎 🆕
### 功能概述
基于用户浏览历史和收藏偏好,推荐相关工具和话题。
### 核心 API
| 路由 | 方法 | 功能 |
|------|------|------|
| /api/recommendations | GET | 获取个性化推荐(工具+技能) |
### 推荐算法
- **协同过滤**: 基于相似用户的收藏/浏览模式
- **内容相似度**: 基于工具分类/标签/定价模型
- **热度加权**: 综合浏览量、评论数、评分
### 前端组件
| 组件 | 文件 | 说明 |
|------|------|------|
| PersonalizedRecommend | `src/components/common/PersonalizedRecommend.tsx` 🆕 | 首页/用户中心的个性化推荐区 |
| RelatedTopics | `src/components/forum/RelatedTopics.tsx` | 话题详情页的相关话题推荐 |
### 展示位置
- 首页 "为你推荐" 板块
- 工具详情页 "相关推荐"
- 话题详情页 "相关话题"
- 用户中心 "猜你喜欢"
### PersonalizedRecommend 组件
- Props: `{ type: "tool" | "skill" | "topic", limit?: number }`
- 瀑布流/网格布局
- 骨架屏加载状态
- 空状态友好提示
- 支持刷新获取新推荐
---
## 共享类型定义
集中管理来源类型标签和颜色:
### src/lib/source-type.ts 🆕
```typescript
export function sourceTypeLabel(type: string): string
export function sourceTypeColor(type: string): string
```
- 支持: github / gitlab / gitee / huggingface / 官网
- 统一管理标签中文名和 Tailwind 配色
### src/lib/constants.ts 🆕
- `tagColors` — 工具/技能标签配色映射
- 统一管理所有标签的颜色方案
+7 -2
View File
@@ -37,15 +37,17 @@ COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
# 跑 next build 生成 .next/ # 跑 next build 生成 .next/
# DATABASE_URL 在 build 时不需要(next 是 SSG/SSR-on-demand),但 prisma generate 需要 # DATABASE_URL 在 build 时需要(next 预渲染静态页面时连接数据库)
# 用 .env.example 当 placeholder 生成 prisma client # 用 .env.example 当 placeholder 生成 prisma client
# 同时注入 OPENAI_API_KEY / DEEPSEEK_API_KEY 占位值,避免 openai SDK v6+ 构造时抛错 # 同时注入 OPENAI_API_KEY / DEEPSEEK_API_KEY 占位值,避免 openai SDK v6+ 构造时抛错
# (build 期不会真正调用 AI,仅为通过模块求值) # (build 期不会真正调用 AI,仅为通过模块求值)
ARG DATABASE_URL
RUN if [ ! -f .env ]; then cp .env.example .env 2>/dev/null || echo "DATABASE_URL=placeholder" > .env; fi \ RUN if [ ! -f .env ]; then cp .env.example .env 2>/dev/null || echo "DATABASE_URL=placeholder" > .env; fi \
&& export DATABASE_URL="${DATABASE_URL:-placeholder}" \
&& export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-sk-build-placeholder}" \ && export DEEPSEEK_API_KEY="${DEEPSEEK_API_KEY:-sk-build-placeholder}" \
&& export OPENAI_API_KEY="${OPENAI_API_KEY:-sk-build-placeholder}" \ && export OPENAI_API_KEY="${OPENAI_API_KEY:-sk-build-placeholder}" \
&& npx prisma generate \ && npx prisma generate \
&& DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" OPENAI_API_KEY="$OPENAI_API_KEY" npm run build && DATABASE_URL="$DATABASE_URL" DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" OPENAI_API_KEY="$OPENAI_API_KEY" npm run build
# ---- 阶段 3: 运行 ---- # ---- 阶段 3: 运行 ----
FROM node:20-alpine AS runner FROM node:20-alpine AS runner
@@ -73,8 +75,11 @@ RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
COPY --from=builder /app/public ./public COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/node_modules ./node_modules
# .prisma 目录交给 nextjs 用户(entrypoint 启动时会重新 prisma generate 同步 schema)
RUN chown -R nextjs:nodejs /app/node_modules/.prisma
COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/prisma ./prisma COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
COPY --from=builder /app/scripts ./scripts COPY --from=builder /app/scripts ./scripts
# seed data files for volume auto-fill # seed data files for volume auto-fill
COPY --from=builder --chown=nextjs:nodejs /app/data ./data-seed COPY --from=builder --chown=nextjs:nodejs /app/data ./data-seed
-30
View File
@@ -1,30 +0,0 @@
#!/bin/bash
echo "=== CRONTAB ==="
crontab -l 2>&1
echo "=== NODE ==="
which node
node -v
echo "=== NPM ==="
which npm
npm -v
echo "=== LOGS DIR ==="
ls -la /home/ubuntu/zhuiguang-ai/logs/ 2>&1
echo "=== TASK LOGS (last 5 each) ==="
for f in /home/ubuntu/zhuiguang-ai/logs/*.log; do
echo "--- $(basename $f) ---"
tail -5 "$f" 2>&1
done
echo "=== ENV CHECK ==="
grep -E "DEEPSEEK|DATABASE_URL|GITHUB_TOKEN|NODE_TLS" /home/ubuntu/zhuiguang-ai/.env 2>&1
echo "=== SCRIPT TEST ==="
cd /home/ubuntu/zhuiguang-ai && node -e "
const fs = require('fs');
const path = require('path');
const scripts = fs.readdirSync('scripts').filter(f => f.endsWith('.mjs'));
for (const s of scripts) {
const stat = fs.statSync(path.join('scripts', s));
console.log(s, '-', stat.mtime.toISOString());
}
"
echo "=== PM2 LOGS ==="
pm2 logs zhuiguang-ai --lines 5 --nostream 2>&1
+65 -15
View File
@@ -5,6 +5,8 @@
# - supercronic 自动重启失败的 job # - supercronic 自动重启失败的 job
# #
# 时间字段: minute hour day month weekday # 时间字段: minute hour day month weekday
# ⚠️ supercronic 容器内按 UTC 执行(探针实测确认,TZ 环境不生效),
# 下面的时刻均为 UTC(= 北京时间 - 8h),注释标出对应国内时间
# ============================================================================= # =============================================================================
# 项目内部统一前缀 # 项目内部统一前缀
@@ -35,27 +37,75 @@ ENV_LOADER='set -a; . /app/.env; set +a'
30 7 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/daily-news.mjs >> /app/logs/daily-news.log 2>&1" >> /app/logs/cron.log 2>&1 30 7 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/daily-news.mjs >> /app/logs/daily-news.log 2>&1" >> /app/logs/cron.log 2>&1
0 8 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/task7-news-to-community.mjs >> /app/logs/task7.log 2>&1" >> /app/logs/cron.log 2>&1 0 8 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/task7-news-to-community.mjs >> /app/logs/task7.log 2>&1" >> /app/logs/cron.log 2>&1
# 7) 数字人 bot 活动引擎 - 每小时 9:00-23:00 (共 15 轮/天, 5+5 板块, 翻倍) # 7) 数字人 bot 活动引擎 - 已停用(2026-09-05):旧"模拟真人互刷"体系退役,由行业智库引擎替代
# 2026-06-11 起翻倍:5+5=10 板块 + 4 回复/板块 + 5 点赞 #0 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-activity.mjs >> /app/logs/bot-activity.log 2>&1" >> /app/logs/cron.log 2>&1
0 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-activity.mjs >> /app/logs/bot-activity.log 2>&1" >> /app/logs/cron.log 2>&1
# 8) Bot 技能结晶 - 每天 02:00 # 8) Bot 技能结晶 - 已停用:并入行业智库引擎
0 2 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-skill-crystallize.mjs >> /app/logs/bot-skill-crystallize.log 2>&1" >> /app/logs/cron.log 2>&1 #0 2 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-skill-crystallize.mjs >> /app/logs/bot-skill-crystallize.log 2>&1" >> /app/logs/cron.log 2>&1
# 9) Bot 跨板块亲和度更新 - 每周日 22:30 # ⚠️ 周日高负载窗口 22:30-23:30 — 三个Bot重任务间隔30分钟,如需调整请保持间隔≥20分钟
30 22 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-affinity-update.mjs >> /app/logs/bot-affinity.log 2>&1" >> /app/logs/cron.log 2>&1 # 9) Bot 跨板块亲和度更新 - 已停用
#30 22 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-affinity-update.mjs >> /app/logs/bot-affinity.log 2>&1" >> /app/logs/cron.log 2>&1
# 10) Bot 周复盘 - 每周日 23:00 # 10) Bot 周复盘 - 已停用
0 23 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-weekly-review.mjs >> /app/logs/bot-weekly-review.log 2>&1" >> /app/logs/cron.log 2>&1 #0 23 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-weekly-review.mjs >> /app/logs/bot-weekly-review.log 2>&1" >> /app/logs/cron.log 2>&1
# 11) Bot 反馈环 - 每周日 22:00 # 11) Bot 反馈环 - 已停用(旧模拟互动体系)
0 22 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-feedback-loop.mjs >> /app/logs/bot-feedback.log 2>&1" >> /app/logs/cron.log 2>&1 #0 */3 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-feedback-loop.mjs >> /app/logs/bot-feedback.log 2>&1" >> /app/logs/cron.log 2>&1
# 12) Bot 数字人 vs 真人对抗学习 - 每周日 23:30 # 12) Bot 数字人 vs 真人对抗学习 - 已停用
30 23 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-adversarial-learning-run.mjs >> /app/logs/bot-adversarial.log 2>&1" >> /app/logs/cron.log 2>&1 #30 23 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-adversarial-learning-run.mjs >> /app/logs/bot-adversarial.log 2>&1" >> /app/logs/cron.log 2>&1
# 13) Bot 人设 A/B 实验数据采集 - 每小时 # 13) Bot 人设 A/B 实验数据采集 - 已停用
0 * * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-persona-experiment-run.mjs >> /app/logs/bot-experiment.log 2>&1" >> /app/logs/cron.log 2>&1 #0 * * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-persona-experiment-run.mjs >> /app/logs/bot-experiment.log 2>&1" >> /app/logs/cron.log 2>&1
# 14) 日志清理 - 每天 04:30 # 14) 日志清理 - 每天 04:30
30 4 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/cleanup-logs.mjs >> /app/logs/cleanup.log 2>&1" >> /app/logs/cron.log 2>&1 30 4 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/cleanup-logs.mjs >> /app/logs/cleanup.log 2>&1" >> /app/logs/cron.log 2>&1
# 15) 数据库 task_log 清理 - 每天 06:30(30天保留期,防止 DB 日志表膨胀)
30 6 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/cleanup-task-logs.mjs >> /app/logs/cleanup-task-logs.log 2>&1" >> /app/logs/cron.log 2>&1
# 16) 点赞计数对账 - 每6小时15分钟(修正异步并发导致的计数不一致)
15 */6 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/reconcile-like-counts.mjs --fix --limit=2000 >> /app/logs/reconcile.log 2>&1" >> /app/logs/cron.log 2>&1
# 17) 任务健康检查 - 每天 08:30(审计所有定时任务最近一次成功率)
30 8 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/health-check.mjs >> /app/logs/health-check.log 2>&1" >> /app/logs/cron.log 2>&1
# 18) Bot 圆桌讨论 - 已停用(旧体系);行业智库引擎将承担多角色研讨
#0 20 * * 3,6 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/bot-roundtable.mjs >> /app/logs/bot-roundtable.log 2>&1" >> /app/logs/cron.log 2>&1
# 19) 每周推荐邮件 - 每周一 10:30(分析用户兴趣,生成个性化AI工具推荐邮件)
30 10 * * 1 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/weekly-recommend-email.mjs >> /app/logs/weekly-recommend-email.log 2>&1" >> /app/logs/cron.log 2>&1
# 20) 断签保护卡发放 - 每月1号 08:00(给近30天活跃用户发放2张断签保护卡)
0 8 1 * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/grant-freeze-cards.mjs >> /app/logs/grant-freeze-cards.log 2>&1" >> /app/logs/cron.log 2>&1
# 21) 通知摘要生成 - 每天 09:00(为启用摘要模式的用户生成每日通知摘要)
0 9 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/notification-digest.mjs >> /app/logs/notification-digest.log 2>&1" >> /app/logs/cron.log 2>&1
# 22) 周刊生成 - 每周一 10:00(生成追光AI周刊 JSON + HTML)
0 10 * * 1 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/newsletter-generate.mjs >> /app/logs/newsletter-generate.log 2>&1" >> /app/logs/cron.log 2>&1
# 23) 竞品工具数监测 - 每周一 09:00
0 9 * * 1 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/competitor-monitor.mjs >> /app/logs/competitor-monitor.log 2>&1" >> /app/logs/cron.log 2>&1
# 24) 行业趋势监测 - 每日 10:00
0 10 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/trend-alert.mjs >> /app/logs/trend-alert.log 2>&1" >> /app/logs/cron.log 2>&1
# 25) 工具数据补全 - 每周日 04:00(自动补全缺失的Logo/描述)
0 4 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/enrich-tool-data.mjs >> /app/logs/enrich.log 2>&1" >> /app/logs/cron.log 2>&1
# 26) 【已停用】行业智库周更(多专家互聊模式已废弃,改用 industry-daily 每日情报)
#0 22 * * 0 bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/industry-thinktank.mjs weekly --all >> /app/logs/industry-thinktank.log 2>&1" >> /app/logs/cron.log 2>&1
# 27) 【已停用】行业智库真人应答(已迁移到 industry-daily respond,单情报官+边界标注)
#*/30 0-15 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/industry-thinktank.mjs respond --all >> /app/logs/industry-thinktank.log 2>&1" >> /app/logs/cron.log 2>&1
# 29) 【已停用 2026-09-26】行业情报日更(AI 生成)—— 按要求先关闭全部 AI 功能,行业情报整站改为需登录访问
#30 0 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/industry-daily.mjs daily --all >> /app/logs/industry-daily.log 2>&1" >> /app/logs/cron.log 2>&1
# 30) 【已停用 2026-09-26】行业情报真人问答(AI 应答)—— 随 AI 功能一并关闭
#*/30 0-15 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/industry-daily.mjs respond --all >> /app/logs/industry-daily.log 2>&1" >> /app/logs/cron.log 2>&1
# 28) 行业素材采集 - 北京时间每日 7/11/15/19/23 点 = 23/03/07/11/15 时 UTC(RSS+AI日报命中标题 → 10 天素材库,供每日情报的价值闸门与生成使用)
5 23,3,7,11,15 * * * bash -c "set -a; . /app/.env; set +a; cd /app && node scripts/industry-thinktank.mjs collect --all >> /app/logs/industry-thinktank.log 2>&1" >> /app/logs/cron.log 2>&1
+164 -5057
View File
File diff suppressed because it is too large Load Diff
+8 -10
View File
@@ -1,19 +1,21 @@
# ============================================================================= # =============================================================================
# 追光AI Docker Compose (生产部署) # 追光AI Docker Compose (生产部署)
# - 1 个 APP 容器 (Next.js, 端口 8301) # - 1 个 APP 容器 (Next.js, 端口 8301)
# - 1 个 CRON 容器 (supercronic 调度 13 个任务) # - 1 个 CRON 容器 (supercronic 调度 25 个任务)
# - 共用同一镜像,通过 override command 区分 # - 共用同一镜像,通过 override command 区分
# - 用 network_mode: host 让容器能访问宿主机共享服务 (Redis/Nacos/MinIO) # - 用 network_mode: host 让容器能访问宿主机共享服务 (Redis/Nacos/MinIO)
# - 卷挂载:data、public、logs 持久化 # - 卷挂载:data、public、logs 持久化
# ============================================================================= # =============================================================================
version: '3.7'
services: services:
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# 1) APP 服务 # 1) APP 服务
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
zhuiguang-ai-app: zhuiguang-ai-app:
image: zhuiguang-ai:2.1.2 image: zhuiguang-ai:2.1.3
container_name: zhuiguang-ai-app container_name: zhuiguang-ai-app
build: build:
context: . context: .
@@ -44,11 +46,11 @@ services:
# 不依赖 DB 健康检查(DB 是外网阿里云 RDS,检测不到会拖累启动) # 不依赖 DB 健康检查(DB 是外网阿里云 RDS,检测不到会拖累启动)
healthcheck: healthcheck:
test: ["CMD", "curl", "-fs", "http://localhost:8301/api/health"] test: ["CMD", "curl", "-f", "http://localhost:8301/api/health"]
interval: 30s interval: 30s
timeout: 5s timeout: 10s
retries: 3 retries: 3
start_period: 30s start_period: 40s
logging: logging:
driver: json-file driver: json-file
@@ -63,7 +65,7 @@ services:
# 2) CRON 服务 # 2) CRON 服务
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
zhuiguang-ai-cron: zhuiguang-ai-cron:
image: zhuiguang-ai:2.1.2 image: zhuiguang-ai:2.1.3
container_name: zhuiguang-ai-cron container_name: zhuiguang-ai-cron
build: build:
context: . context: .
@@ -112,10 +114,6 @@ services:
# ============================================================================= # =============================================================================
volumes: volumes:
bot-data: bot-data:
name: zhuiguang_ai_bot_data
bot-public: bot-public:
name: zhuiguang_ai_bot_public
bot-logs: bot-logs:
name: zhuiguang_ai_bot_logs
bot-prisma: bot-prisma:
name: zhuiguang_ai_bot_prisma
-1
View File
@@ -71,7 +71,6 @@
│ ├── entrypoint-cron.sh # CRON 启动入口 │ ├── entrypoint-cron.sh # CRON 启动入口
│ ├── backup-volumes.sh # 卷备份 (host 跑) │ ├── backup-volumes.sh # 卷备份 (host 跑)
│ ├── install-cron-backup.sh # 备份 cron 安装 (host 跑) │ ├── install-cron-backup.sh # 备份 cron 安装 (host 跑)
│ ├── install-cron.sh # 旧 host cron 安装 (停用)
│ └── ... (业务脚本) │ └── ... (业务脚本)
├── public/ # 静态资源 (volume) ├── public/ # 静态资源 (volume)
├── data/ # bot-characters.json (volume) ├── data/ # bot-characters.json (volume)
+19 -5
View File
@@ -1,4 +1,10 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
import bundleAnalyzer from "@next/bundle-analyzer";
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === "true",
});
const nextConfig = { const nextConfig = {
images: { images: {
remotePatterns: [ remotePatterns: [
@@ -9,6 +15,10 @@ const nextConfig = {
], ],
minimumCacheTTL: 3600, minimumCacheTTL: 3600,
formats: ["image/avif", "image/webp"], formats: ["image/avif", "image/webp"],
// 预设头像库使用本地 SVG(/avatars/*.svg),需放行 SVG 走优化器,否则 next/image 返回 400
dangerouslyAllowSVG: true,
// SVG 以 sandbox 方式渲染,禁用脚本,防止恶意 SVG 执行
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
}, },
compress: true, compress: true,
poweredByHeader: false, poweredByHeader: false,
@@ -32,7 +42,7 @@ const nextConfig = {
pagesBufferLength: 5, pagesBufferLength: 5,
}, },
experimental: { experimental: {
optimizeCss: true, optimizeCss: false,
}, },
headers: async () => { headers: async () => {
const isDev = process.env.NODE_ENV === "development"; const isDev = process.env.NODE_ENV === "development";
@@ -50,9 +60,13 @@ const nextConfig = {
{ {
source: "/:path*", source: "/:path*",
headers: [ headers: [
{ key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" }, { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains; preload" },
{ key: "Content-Security-Policy", value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; font-src 'self'; connect-src 'self' https:; frame-ancestors 'none'" }, { key: "Content-Security-Policy", value: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data: blob:; font-src 'self' data:; connect-src 'self' https: wss:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" },
{ key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" }, { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=()" },
{ key: "X-Content-Type-Options", value: "nosniff" },
{ key: "X-Frame-Options", value: "DENY" },
{ key: "X-XSS-Protection", value: "1; mode=block" },
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
], ],
}, },
{ {
@@ -83,4 +97,4 @@ const nextConfig = {
}, },
}; };
export default nextConfig; export default withBundleAnalyzer(nextConfig);
+2080 -11
View File
File diff suppressed because it is too large Load Diff
+12 -2
View File
@@ -7,7 +7,11 @@
"build": "next build", "build": "next build",
"start": "next start --port 8301", "start": "next start --port 8301",
"lint": "next lint", "lint": "next lint",
"version:bump": "node scripts/version-manager.mjs" "test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"version:bump": "node scripts/version-manager.mjs",
"knowledge:update": "node scripts/update-knowledge.mjs"
}, },
"dependencies": { "dependencies": {
"@base-ui/react": "^1.4.1", "@base-ui/react": "^1.4.1",
@@ -38,15 +42,21 @@
"zod": "^4.4.3" "zod": "^4.4.3"
}, },
"devDependencies": { "devDependencies": {
"@next/bundle-analyzer": "^16.2.9",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"@vitejs/plugin-react": "^6.0.2",
"dotenv": "^17.4.2", "dotenv": "^17.4.2",
"jsdom": "^29.1.1",
"mysql2": "^3.22.3", "mysql2": "^3.22.3",
"postcss": "^8", "postcss": "^8",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5" "typescript": "^5",
"vitest": "^4.1.9"
}, },
"prisma": { "prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts" "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
@@ -0,0 +1,162 @@
-- DropForeignKey
ALTER TABLE `comments` DROP FOREIGN KEY `comments_user_id_fkey`;
-- DropForeignKey
ALTER TABLE `forum_posts` DROP FOREIGN KEY `forum_posts_user_id_fkey`;
-- DropForeignKey
ALTER TABLE `forum_topics` DROP FOREIGN KEY `forum_topics_user_id_fkey`;
-- AlterTable
ALTER TABLE `bot_adversarial_learnings` ALTER COLUMN `updated_at` DROP DEFAULT;
-- AlterTable
ALTER TABLE `bot_persona_assignments` ALTER COLUMN `updated_at` DROP DEFAULT;
-- AlterTable
ALTER TABLE `bot_persona_experiments` MODIFY `significance_level` DOUBLE NOT NULL DEFAULT 0.1,
ALTER COLUMN `updated_at` DROP DEFAULT;
-- AlterTable
ALTER TABLE `bot_persona_metrics` MODIFY `avg_replies` DOUBLE NOT NULL DEFAULT 0,
MODIFY `avg_likes` DOUBLE NOT NULL DEFAULT 0,
MODIFY `computed_score` DOUBLE NOT NULL DEFAULT 0,
ALTER COLUMN `updated_at` DROP DEFAULT;
-- AlterTable
ALTER TABLE `bot_persona_variants` MODIFY `weight` DOUBLE NOT NULL DEFAULT 0.5,
MODIFY `engagement_score` DOUBLE NOT NULL DEFAULT 0,
ALTER COLUMN `updated_at` DROP DEFAULT;
-- AlterTable
ALTER TABLE `collections` ADD COLUMN `is_public` BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN `public_slug` VARCHAR(100) NULL,
ADD COLUMN `view_count` INTEGER NOT NULL DEFAULT 0;
-- AlterTable
ALTER TABLE `notifications` MODIFY `type` ENUM('COMMENT_REPLY', 'COMMENT_LIKE', 'TOPIC_REPLY', 'MENTION', 'BEST_ANSWER', 'POST_LIKE', 'TOPIC_LIKE', 'SYSTEM', 'DIGEST') NOT NULL;
-- AlterTable
ALTER TABLE `tools` ADD COLUMN `screenshots` JSON NULL;
-- AlterTable
ALTER TABLE `user_achievements` ALTER COLUMN `updated_at` DROP DEFAULT;
-- CreateTable
CREATE TABLE `feedbacks` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`type` VARCHAR(20) NOT NULL DEFAULT 'suggestion',
`content` TEXT NOT NULL,
`contact` VARCHAR(255) NULL,
`page_url` VARCHAR(500) NULL,
`user_id` VARCHAR(50) NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'pending',
`admin_note` TEXT NULL,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` DATETIME(3) NOT NULL,
INDEX `feedbacks_status_created_at_idx`(`status`, `created_at`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `api_keys` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`user_id` INTEGER NOT NULL,
`name` VARCHAR(100) NOT NULL,
`key_hash` VARCHAR(64) NOT NULL,
`key_prefix` VARCHAR(8) NOT NULL,
`tier` VARCHAR(20) NOT NULL DEFAULT 'free',
`rate_limit` INTEGER NOT NULL DEFAULT 100,
`last_used_at` DATETIME(3) NULL,
`request_count` INTEGER NOT NULL DEFAULT 0,
`is_active` BOOLEAN NOT NULL DEFAULT true,
`created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`expires_at` DATETIME(3) NULL,
UNIQUE INDEX `api_keys_key_hash_key`(`key_hash`),
INDEX `api_keys_user_id_idx`(`user_id`),
INDEX `api_keys_key_prefix_idx`(`key_prefix`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateIndex
CREATE INDEX `bot_daily_stats_bot_id_date_idx` ON `bot_daily_stats`(`bot_id`, `date`);
-- CreateIndex
CREATE INDEX `bot_memories_bot_id_created_at_idx` ON `bot_memories`(`bot_id`, `created_at`);
-- CreateIndex
CREATE UNIQUE INDEX `collections_public_slug_key` ON `collections`(`public_slug`);
-- CreateIndex
CREATE INDEX `collections_is_public_view_count_idx` ON `collections`(`is_public`, `view_count`);
-- CreateIndex
CREATE INDEX `forum_posts_topic_id_is_hidden_created_at_idx` ON `forum_posts`(`topic_id`, `is_hidden`, `created_at`);
-- CreateIndex
CREATE INDEX `forum_topics_category_id_updated_at_idx` ON `forum_topics`(`category_id`, `updated_at`);
-- CreateIndex
CREATE INDEX `forum_topics_created_at_idx` ON `forum_topics`(`created_at`);
-- CreateIndex
CREATE INDEX `skills_status_view_count_idx` ON `skills`(`status`, `view_count`);
-- CreateIndex
CREATE INDEX `skills_status_created_at_idx` ON `skills`(`status`, `created_at`);
-- CreateIndex
CREATE INDEX `tools_status_view_count_idx` ON `tools`(`status`, `view_count`);
-- CreateIndex
CREATE INDEX `tools_status_created_at_idx` ON `tools`(`status`, `created_at`);
-- CreateIndex
CREATE INDEX `users_is_bot_idx` ON `users`(`is_bot`);
-- CreateIndex
CREATE INDEX `users_role_idx` ON `users`(`role`);
-- AddForeignKey
ALTER TABLE `comments` ADD CONSTRAINT `comments_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `forum_topics` ADD CONSTRAINT `forum_topics_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `forum_posts` ADD CONSTRAINT `forum_posts_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `bot_persona_variants` ADD CONSTRAINT `bot_persona_variants_bot_id_fkey` FOREIGN KEY (`bot_id`) REFERENCES `bot_configs`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `bot_persona_variants` ADD CONSTRAINT `bot_persona_variants_experiment_id_fkey` FOREIGN KEY (`experiment_id`) REFERENCES `bot_persona_experiments`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `bot_persona_assignments` ADD CONSTRAINT `bot_persona_assignments_variant_id_fkey` FOREIGN KEY (`variant_id`) REFERENCES `bot_persona_variants`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `bot_persona_metrics` ADD CONSTRAINT `bot_persona_metrics_variant_id_fkey` FOREIGN KEY (`variant_id`) REFERENCES `bot_persona_variants`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `bot_adversarial_learnings` ADD CONSTRAINT `bot_adversarial_learnings_bot_id_fkey` FOREIGN KEY (`bot_id`) REFERENCES `bot_configs`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `bot_cross_forum_affinities` ADD CONSTRAINT `bot_cross_forum_affinities_bot_id_fkey` FOREIGN KEY (`bot_id`) REFERENCES `bot_configs`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `user_achievements` ADD CONSTRAINT `user_achievements_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `user_achievements` ADD CONSTRAINT `user_achievements_achievement_key_fkey` FOREIGN KEY (`achievement_key`) REFERENCES `achievements`(`key`) ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `forum_topic_subscriptions` ADD CONSTRAINT `forum_topic_subscriptions_topic_id_fkey` FOREIGN KEY (`topic_id`) REFERENCES `forum_topics`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `forum_topic_subscriptions` ADD CONSTRAINT `forum_topic_subscriptions_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `api_keys` ADD CONSTRAINT `api_keys_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,2 @@
-- 行业智库元数据列:主题帖标记为智库帖,存版本/议题等
ALTER TABLE `forum_topics` ADD COLUMN `thinktank` JSON NULL;
+66 -3
View File
@@ -53,6 +53,7 @@ model Tool {
pricingModel PricingModel @default(freemium) @map("pricing_model") pricingModel PricingModel @default(freemium) @map("pricing_model")
pricingDetail String? @map("pricing_detail") @db.VarChar(200) pricingDetail String? @map("pricing_detail") @db.VarChar(200)
features Json? features Json?
screenshots Json? // Array of { url: string, alt?: string } - max 5 screenshots
tags Json? tags Json?
viewCount Int @default(0) @map("view_count") viewCount Int @default(0) @map("view_count")
commentCount Int @default(0) @map("comment_count") commentCount Int @default(0) @map("comment_count")
@@ -71,6 +72,8 @@ model Tool {
collectionItems CollectionItem[] collectionItems CollectionItem[]
comments Comment[] comments Comment[]
@@index([status, viewCount]) // for published tools sorted by views
@@index([status, createdAt]) // for recent published tools
@@index([categoryId, status]) @@index([categoryId, status])
@@fulltext([name, description]) @@fulltext([name, description])
@@map("tools") @@map("tools")
@@ -137,6 +140,8 @@ model Skill {
commentCount Int @default(0) @map("comment_count") commentCount Int @default(0) @map("comment_count")
avgRating Float @default(0) @map("avg_rating") avgRating Float @default(0) @map("avg_rating")
@@index([status, viewCount]) // for published skills sorted by views
@@index([status, createdAt]) // for recent published skills
@@index([categoryId, status]) @@index([categoryId, status])
@@map("skills") @@map("skills")
} }
@@ -229,7 +234,12 @@ model User {
achievements UserAchievement[] achievements UserAchievement[]
topicSubscriptions ForumTopicSubscription[] topicSubscriptions ForumTopicSubscription[]
apiKeys ApiKey[]
@@unique([oauthProvider, oauthId]) @@unique([oauthProvider, oauthId])
@@index([isBot]) // for filtering bot users
@@index([role]) // for role-based queries
@@map("users") @@map("users")
} }
@@ -371,6 +381,9 @@ model Collection {
name String @db.VarChar(50) name String @db.VarChar(50)
description String? @db.Text description String? @db.Text
sortOrder Int @default(0) @map("sort_order") sortOrder Int @default(0) @map("sort_order")
isPublic Boolean @default(false) @map("is_public")
publicSlug String? @unique @map("public_slug") @db.VarChar(100)
viewCount Int @default(0) @map("view_count")
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
@@ -378,6 +391,7 @@ model Collection {
items CollectionItem[] items CollectionItem[]
@@index([userId]) @@index([userId])
@@index([isPublic, viewCount])
@@map("collections") @@map("collections")
} }
@@ -526,7 +540,7 @@ model Comment {
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
tool Tool? @relation(fields: [toolId], references: [id], onDelete: Cascade) tool Tool? @relation(fields: [toolId], references: [id], onDelete: Cascade)
skill Skill? @relation(fields: [skillId], references: [id], onDelete: Cascade) skill Skill? @relation(fields: [skillId], references: [id], onDelete: Cascade)
parent Comment? @relation("CommentReplies", fields: [parentId], references: [id]) parent Comment? @relation("CommentReplies", fields: [parentId], references: [id])
@@ -583,6 +597,7 @@ enum NotifType {
POST_LIKE POST_LIKE
TOPIC_LIKE TOPIC_LIKE
SYSTEM SYSTEM
DIGEST
} }
model ForumCategory { model ForumCategory {
@@ -622,9 +637,12 @@ model ForumTopic {
lastReplyAt DateTime? @map("last_reply_at") lastReplyAt DateTime? @map("last_reply_at")
createdAt DateTime @default(now()) @map("created_at") createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
// 行业智库元数据:{ kind:"thinktank", industry:<板块slug>, version: n, lastTopic, lastRoundAt }
// 主题帖 topic.content = 最新"打法"(1 楼),楼层 post = 每周研讨对话原文/真人问答
thinktank Json? @map("thinktank")
category ForumCategory @relation(fields: [categoryId], references: [id]) category ForumCategory @relation(fields: [categoryId], references: [id])
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
posts ForumPost[] posts ForumPost[]
likes ForumTopicLike[] likes ForumTopicLike[]
tags ForumTopicTag[] tags ForumTopicTag[]
@@ -634,6 +652,8 @@ model ForumTopic {
@@index([categoryId, lastReplyAt]) @@index([categoryId, lastReplyAt])
@@index([categoryId, isPinned, createdAt]) @@index([categoryId, isPinned, createdAt])
@@index([categoryId, updatedAt]) // for recent topics by category
@@index([createdAt]) // for chronological queries
@@index([userId]) @@index([userId])
@@index([isFeatured, lastReplyAt]) @@index([isFeatured, lastReplyAt])
@@index([isHidden]) @@index([isHidden])
@@ -656,12 +676,13 @@ model ForumPost {
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade) topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
likes ForumPostLike[] likes ForumPostLike[]
replyTo ForumPost? @relation("PostReplies", fields: [replyToId], references: [id], onDelete: SetNull) replyTo ForumPost? @relation("PostReplies", fields: [replyToId], references: [id], onDelete: SetNull)
replies ForumPost[] @relation("PostReplies") replies ForumPost[] @relation("PostReplies")
@@index([topicId, createdAt]) @@index([topicId, createdAt])
@@index([topicId, isHidden, createdAt]) // for filtered post queries
@@index([userId]) @@index([userId])
@@index([replyToId]) @@index([replyToId])
@@index([isHidden]) @@index([isHidden])
@@ -836,6 +857,8 @@ model BotConfig {
botPersonaVariants BotPersonaVariant[] @relation("BotVariants") botPersonaVariants BotPersonaVariant[] @relation("BotVariants")
adversarialLearnings BotAdversarialLearning[] @relation("BotAdversarialLearnings") adversarialLearnings BotAdversarialLearning[] @relation("BotAdversarialLearnings")
crossForumAffinities BotCrossForumAffinity[]
@@map("bot_configs") @@map("bot_configs")
} }
@@ -873,6 +896,7 @@ model BotDailyStat {
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
@@unique([botId, date]) @@unique([botId, date])
@@index([botId, date]) // for bot daily stats queries
@@index([date]) @@index([date])
@@map("bot_daily_stats") @@map("bot_daily_stats")
} }
@@ -1076,6 +1100,8 @@ model BotCrossForumAffinity {
reason String? @db.VarChar(100) reason String? @db.VarChar(100)
updatedAt DateTime @updatedAt @map("updated_at") updatedAt DateTime @updatedAt @map("updated_at")
bot BotConfig @relation(fields: [botId], references: [id], onDelete: Cascade)
@@unique([botId, forumSlug]) @@unique([botId, forumSlug])
@@index([botId, affinity]) @@index([botId, affinity])
@@map("bot_cross_forum_affinities") @@map("bot_cross_forum_affinities")
@@ -1132,3 +1158,40 @@ model ForumTopicSubscription {
@@index([userId, createdAt]) @@index([userId, createdAt])
@@map("forum_topic_subscriptions") @@map("forum_topic_subscriptions")
} }
model Feedback {
id Int @id @default(autoincrement())
type String @default("suggestion") @db.VarChar(20)
content String @db.Text
contact String? @db.VarChar(255)
pageUrl String? @map("page_url") @db.VarChar(500)
userId String? @map("user_id") @db.VarChar(50)
status String @default("pending") @db.VarChar(20)
adminNote String? @map("admin_note") @db.Text
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([status, createdAt])
@@map("feedbacks")
}
model ApiKey {
id Int @id @default(autoincrement())
userId Int @map("user_id")
name String @db.VarChar(100)
keyHash String @unique @map("key_hash") @db.VarChar(64)
keyPrefix String @map("key_prefix") @db.VarChar(8)
tier String @default("free") @db.VarChar(20)
rateLimit Int @default(100) @map("rate_limit")
lastUsedAt DateTime? @map("last_used_at")
requestCount Int @default(0) @map("request_count")
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
expiresAt DateTime? @map("expires_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([keyPrefix])
@@map("api_keys")
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g5" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#166534"/><stop offset="100%" stop-color="#4ade80"/>
</linearGradient>
<clipPath id="c5"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g5)" clip-path="url(#c5)"/>
<g transform="translate(20.00 20.00) scale(0.7143)"><path d="M140 36a56 56 0 0 0-56 56v6.17A12 12 0 0 0 74 110v14a12 12 0 0 0 10.3 11.88A56 56 0 0 0 116 180.6V199h-4a72 72 0 0 0-72 72v9h200v-9a72 72 0 0 0-72-72h-4v-18.38a56 56 0 0 0 31.7-44.73A12 12 0 0 0 206 124v-14a12 12 0 0 0-10-11.83V92a56 56 0 0 0-56-56" fill="#f8d25c"/><path d="M116 180.61v8a56 56 0 0 0 24 5.39 56 56 0 0 0 24-5.39v-8a56 56 0 0 1-24 5.39 56 56 0 0 1-24-5.39" fill="black" fill-opacity=".1"/><g transform="translate(40 184.7)"><path d="M100 42.36c14.91 0 27-11.2 27-25q0-1.53-.2-3h1.2a72 72 0 0 1 72 72v8.95H0v-8.95a72 72 0 0 1 72-72h1.2q-.2 1.47-.2 3c0 13.8 12.09 25 27 25" fill="#E6E6E6"/><path d="M68.78 14.43 69 13.3c-2.96.06-6 1-6 1l-.42.67A72 72 0 0 0 0 86.36v8.95h74s-10.7-51.56-5.24-80.8zM126 95.3s11-53 5-82c2.96.06 6 1 6 1l.42.67A72 72 0 0 1 200 86.36v8.95z" fill="#ffffff"/><path fill-rule="evenodd" clip-rule="evenodd" d="M69 13.3c-6 29 5 82 5 82H58l-14-36 6-9-6-6 19-30s3.04-.94 6-1m62 0c6 29-5 82-5 82h16l14-36-6-9 6-6-19-30s-3.04-.94-6-1" fill="black" fill-opacity=".15"/><path d="M76 6.84c-6.77 4.6-11 11.12-11 18.35 0 7.4 4.43 14.06 11.48 18.67l5.94-4.68 4.58.33-1-3.15.08-.06C79.98 33.16 76 28 76 22.18zm48 15.34c0 5.83-3.98 10.98-10.08 14.12l.08.06-1 3.15 4.58-.33 5.94 4.68C130.57 39.26 135 32.6 135 25.2c0-7.23-4.23-13.75-11-18.35z" fill="#F2F2F2"/><path d="m151.42 71.07.87-2.24 6.27-4.7a4 4 0 0 1 4.85.05l6.6 5.13z" fill="#E6E6E6"/></g><g transform="translate(94 140)"><path fill-rule="evenodd" clip-rule="evenodd" d="M26 24.4C27.14 13.9 30.24 5 46 5s18.92 8.96 20 19.5c.08.84-.83 1.5-1.96 1.5-6.69 0-9.37-1.5-18.05-1.5-8.7 0-13.25 1.5-17.9 1.5-1.16 0-2.2-.55-2.1-1.6" fill="black" fill-opacity=".7"/><path d="M59.86 9.1c-.8.57-1.8.9-2.86.9H36c-1.3 0-2.49-.5-3.38-1.31C35.56 6.39 39.8 5 46 5c6.54 0 10.9 1.54 13.86 4.1" fill="white"/><path d="M34 19a6 6 0 0 0-6 6v7a6 6 0 0 0 12 0v-2h.08a6 6 0 0 1 11.84 0H52a6 6 0 0 0 12 0v-5a6 6 0 0 0-6-6z" fill="#7BB24B"/><path d="M64 25a6 6 0 0 0-6-6H34a6 6 0 0 0-6 6v6a6 6 0 0 0 12 0v-2h.08a6 6 0 0 1 11.84 0H52a6 6 0 0 0 12 0z" fill="#88C553"/></g><g transform="translate(128 130)"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 0c0 4.42 5.37 8 12 8s12-3.58 12-8" fill="black" fill-opacity=".16"/></g><g transform="translate(98 98)"><path fill-rule="evenodd" clip-rule="evenodd" d="M2.16 19.55C4.01 23.35 8.16 26 13 26c4.81 0 8.96-2.63 10.82-6.4.55-1.13-.24-2.05-1.03-1.37a15 15 0 0 1-9.8 3.43c-3.73 0-7.12-1.24-9.55-3.23-.9-.73-1.82.01-1.28 1.12m58 0c1.85 3.8 6 6.45 10.84 6.45 4.81 0 8.96-2.63 10.82-6.4.55-1.13-.24-2.05-1.03-1.37a15 15 0 0 1-9.8 3.43c-3.76 0-7.15-1.24-9.58-3.23-.9-.73-1.82.01-1.28 1.12" fill="black" fill-opacity=".6"/></g><g transform="translate(91.86 82)"><path d="M8.11 17.13C9.61 7.6 22.2 1.1 31.31 5.3a2 2 0 0 0 1.66-3.63C21.5-3.63 6.07 4.33 4.17 16.5a2 2 0 1 0 3.94.63m80.05 0C86.66 7.6 74.08 1.1 64.97 5.3a2 2 0 0 1-1.67-3.63c11.5-5.3 26.9 2.66 28.81 14.83a2 2 0 0 1-3.95.63" fill="black" fill-opacity=".6"/></g><g transform="translate(8)"><path d="M196 168h-2v56.06a9 9 0 1 0 2 0zm-126 8h-2v56.06a9 9 0 1 0 2 0z" fill="#F4F4F4"/><circle cx="132" cy="20" r="20" fill="#F4F4F4"/><path d="M92.45 77.53h79.1c6.08 0 9.82 2.93 9.82 9V166c0 30.46 22.63 30.41 22.63 10.92v-73.86C204 68.8 186.77 21 132 21s-72 47.8-72 82.05v73.86c0 19.5 22.63 19.54 22.63-10.92V86.53c0-6.07 3.73-9 9.82-9" fill="#a7ffc4"/><path d="M197.67 67H66.33c9.1-24.5 28.93-46 65.67-46s56.58 21.5 65.67 46" fill="black" fill-opacity=".2"/><path d="M90.2 33.73 101.5 50 114 32H92.66q-1.25.84-2.46 1.73M171.34 32H151l12.5 18 10.95-15.77q-1.5-1.15-3.11-2.23M132.5 50 120 32h25z" fill="white" fill-opacity=".5"/><path d="M98 59 85.5 41 73 59zm31 0-12.5-18L104 59zm18.5-18L160 59h-25zM191 59l-12.5-18L166 59z" fill="black" fill-opacity=".5"/></g><g transform="translate(81 98)"><path d="M60 43.3c6.8-3.59 16.42-4.15 21.61-2.17a32 32 0 0 1 4.84 2.55c4.13 2.47 8.55 5.12 14.91 3.15.37-.12.73.2.62.58-1.37 4.5-9 7.6-11.6 7.7-6.2.24-11.75-2.26-17.13-4.7-4.44-2-8.77-3.95-13.25-4.25-4.48.3-8.8 2.26-13.25 4.26-5.38 2.43-10.92 4.93-17.13 4.69-2.6-.1-10.23-3.2-11.6-7.7-.1-.37.25-.7.62-.58 6.36 1.97 10.78-.68 14.9-3.15a32 32 0 0 1 4.85-2.55c5.2-1.98 14.82-1.42 21.6 2.17" fill="#2c1b18"/></g><g transform="translate(75.77 46)"><path fill-rule="evenodd" clip-rule="evenodd" d="M88.63.78c-3.08-3.09-6.28 3.86-7.77 5.65-3.61 4.32-7.09 8.75-10.76 13.02-7.25 8.43-14.43 16.92-21.63 25.4-1.1 1.28-.96 1.4-2.4 1.54-.95.08-2.27-.4-3.26-.46a29 29 0 0 0-8.14.9c-5.35 1.17-11.01 3.1-15.65 6.07-1.22.78-2 1.7-3.32 1.94-1.15.2-2.67-.21-3.84-.32-2.09-.2-5.09-1.05-7.13-.6-2.6.55-3.58 3.7-.94 5.08 2 1.06 6 .48 8.21.64 2.57.2 1.8.06 1.42 2.52-.52 3.54.35 7.49 1.84 10.72 3.46 7.5 13.04 15.46 21.77 14.72 7.28-.6 13.67-7.19 16.66-13.5a31 31 0 0 0 2.74-10.47c.2-2.27.1-4.67-.56-6.87a17 17 0 0 0-1.38-3.2c-.44-.8-2.4-2.64-2.52-3.44-.23-1.56 4.19-5.73 5.03-6.78Q63 36 68.92 28.6q5.82-7.33 11.77-14.52C82.5 11.9 91.54 3.7 88.6.78" fill="#ffafb9"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g8" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#166534"/><stop offset="100%" stop-color="#4ade80"/>
</linearGradient>
<clipPath id="c8"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g8)" clip-path="url(#c8)"/>
<g transform="translate(20.00 20.00) scale(0.7143)"><path d="M140 36a56 56 0 0 0-56 56v6.17A12 12 0 0 0 74 110v14a12 12 0 0 0 10.3 11.88A56 56 0 0 0 116 180.6V199h-4a72 72 0 0 0-72 72v9h200v-9a72 72 0 0 0-72-72h-4v-18.38a56 56 0 0 0 31.7-44.73A12 12 0 0 0 206 124v-14a12 12 0 0 0-10-11.83V92a56 56 0 0 0-56-56" fill="#f8d25c"/><path d="M116 180.61v8a56 56 0 0 0 24 5.39 56 56 0 0 0 24-5.39v-8a56 56 0 0 1-24 5.39 56 56 0 0 1-24-5.39" fill="black" fill-opacity=".1"/><g transform="translate(40 184.7)"><path d="M60.68 15.24A72 72 0 0 0 0 86.36v8.95h200v-8.95a72 72 0 0 0-60.68-71.12 24 24 0 0 1-7.56 13.6l-29.08 26.23a4 4 0 0 1-5.36 0L68.24 28.84a24 24 0 0 1-7.56-13.6" fill="#b1e2ff"/></g><g transform="translate(94 140)"><path fill-rule="evenodd" clip-rule="evenodd" d="M27.12 23.87a19 19 0 0 1 37.77.09c.08.77-.77 2.04-1.85 2.04H29.1c-1.1 0-2.1-1.18-1.98-2.13" fill="black" fill-opacity=".7"/><path d="M61.59 26H30.4A11 11 0 0 1 46 19.2 11 11 0 0 1 61.6 26" fill="#FF4F6D"/><path d="M58.57 11.75A5 5 0 0 1 57 12H36q-1.22-.02-2.24-.53A19 19 0 0 1 46 7c4.82 0 9.22 1.8 12.57 4.75" fill="white"/></g><g transform="translate(128 130)"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 0c0 4.42 5.37 8 12 8s12-3.58 12-8" fill="black" fill-opacity=".16"/></g><g transform="translate(98 98)"><path d="M30 12.73c0 4.26-6.27 7.72-14 7.72S2 17 2 12.73 8.27 5 16 5s14 3.46 14 7.73m52 0c0 4.26-6.27 7.72-14 7.72S54 17 54 12.73 60.27 5 68 5s14 3.46 14 7.73" fill="white"/><path d="M18.82 20.3a25 25 0 0 1-5.64 0 6 6 0 1 1 5.64 0m52 0a25 25 0 0 1-5.64 0 6 6 0 1 1 5.64 0" fill="black" fill-opacity=".7"/></g><g transform="translate(91.86 82)"><path d="M7.73 14.16c4.5-6.32 14.01-9.5 23.75-6.36a2 2 0 1 0 1.23-3.81c-11.4-3.7-22.74.1-28.24 7.85a2 2 0 1 0 3.26 2.32m80.78 7c-3.91-5.51-14.64-8.6-23.89-6.33a2 2 0 0 1-.95-3.88c10.73-2.64 23.16.94 28.1 7.9a2 2 0 0 1-3.26 2.3" fill="black" fill-opacity=".6"/></g><g transform="translate(8)"><path d="M132 18a74 74 0 0 0-74 74v96c0 8.56 1.45 16.78 4.13 24.42A71.7 71.7 0 0 1 104 199h4v-18.39a56 56 0 0 1-31.8-45.74A12 12 0 0 1 66 123v-13a12 12 0 0 1 .46-3.3c17.13-6.02 33.75-21.94 43.59-44.04l1.18-2.76q6.87 18.34 25.89 26.64a87 87 0 0 1-12.45-20.79c7.98 8.5 18.35 16.74 30.48 23.75 14.33 8.27 28.91 13.56 41.87 15.75q.96 2.2.98 4.75v13a12 12 0 0 1-10.2 11.87A56 56 0 0 1 156 180.6V199h4a71.7 71.7 0 0 1 41.88 13.42A74 74 0 0 0 206 188V92a74 74 0 0 0-74-74" fill="#a55728"/><path d="M110.05 62.66C98.59 88.39 77.95 105.75 58 108.84v4c19.95-3.1 40.59-20.45 52.05-46.18l1.18-2.76q6.87 18.35 25.89 26.63a78 78 0 0 1-4.62-6.26q-15.27-8.35-21.27-24.37a99 99 0 0 1-1.18 2.76m18.45 10.98a137 137 0 0 0 26.65 19.86c17.75 10.25 35.9 15.9 50.85 16.78v-4c-14.95-.87-33.1-6.54-50.85-16.78-12.13-7-22.5-15.24-30.48-23.75a98 98 0 0 0 3.83 7.89" fill="black" fill-opacity=".16"/></g><g transform="translate(81 98)"><path d="M60 43.3c6.8-3.59 16.42-4.15 21.61-2.17a32 32 0 0 1 4.84 2.55c4.13 2.47 8.55 5.12 14.91 3.15.37-.12.73.2.62.58-1.37 4.5-9 7.6-11.6 7.7-6.2.24-11.75-2.26-17.13-4.7-4.44-2-8.77-3.95-13.25-4.25-4.48.3-8.8 2.26-13.25 4.26-5.38 2.43-10.92 4.93-17.13 4.69-2.6-.1-10.23-3.2-11.6-7.7-.1-.37.25-.7.62-.58 6.36 1.97 10.78-.68 14.9-3.15a32 32 0 0 1 4.85-2.55c5.2-1.98 14.82-1.42 21.6 2.17" fill="#ecdcbf"/></g><g transform="translate(75.77 46)"><path d="M65.23 54.11c-11.03 0-12.63-9.08-35.33-10.37C7.22 42.72.05 49.44 0 54.11c.05 4.3-1.12 15.45 13.6 28.52 14.77 15.51 29.9 10.25 35.33 5.18 5.44-2.34 11.64-23.35 16.3-23.33s10.87 21 16.3 23.33c5.43 5.07 20.57 10.33 35.34-5.18 14.72-13.07 13.55-24.23 13.59-28.52-.04-4.66-7.21-11.4-29.9-10.37-22.7 1.29-24.29 10.37-35.33 10.37" fill="black" fill-opacity=".1"/><path d="M65.23 52.11c-11.03 0-12.63-9.08-35.33-10.37C7.22 40.72.05 47.44 0 52.11c.05 4.3-1.12 15.45 13.6 28.52 14.77 15.51 29.9 10.25 35.33 5.18 5.44-2.34 11.64-23.35 16.3-23.33s10.87 21 16.3 23.33c5.43 5.07 20.57 10.33 35.34-5.18 14.72-13.07 13.55-24.23 13.59-28.52-.04-4.66-7.21-11.4-29.9-10.37-22.7 1.29-24.29 10.37-35.33 10.37" fill="#5199e4"/><path d="M27.19 46.93c14.26-.29 27.55 7.9 27.17 15.55-.22 5.05-2.93 22.83-19.02 23.33-16.1.5-24.8-17.79-24.46-25.92.2-3.51 2.05-12.67 16.3-12.96m76.1 0c-14.26-.29-27.56 7.9-27.18 15.55.22 5.05 2.94 22.83 19.03 23.33s24.8-17.79 24.46-25.92c-.2-3.51-2.06-12.67-16.31-12.96" fill="#2F383B"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.7 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c1"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g1)" clip-path="url(#c1)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#e2ba87"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#e2ba87"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M155.7 51c3 1.7 24.1-24.8 38.2-16.7 4.7 2.7 8.3 7.3 11 12.3 6 11 8 24.4 5.6 36.8a67 67 0 0 1-29 41.2c-37.3 25.5-67 20.7-106.7 6C43.4 118.9.8 95 9.6 51.9c15.5-76 146-1 146.1-1" fill="#873C41"/><path d="M47.7 46c10.2 6.4 22.5 7 33.6 4.5 2-.5 3.8 1.3 4.3 3.4 5.4 21 36 32 51.5 18.5 1.2-1 3.3-1.3 4.5-.1 8.3 7.8 21.6 10 31.4 4.6 13.8-7.5 13.7-28.6 10-43.3-12.2 3.7-25 18.7-27.3 17.3 0 0-80.1-46-122.3-29.4a31 31 0 0 0 14.3 24.6" fill="white"/><path d="M162.7 127.8c-22-20.1-48.9-31.4-76.2-39.3C63 81.6 36.8 77.2 13 84c11 23.5 39.2 38.2 61.7 46.6 34.8 12.9 61.8 18.2 93 2.2z" fill="#EE7E8B"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M16.5 89.6c8.9-.2 17.7 3 25.5 7.7a24 24 0 0 0 5.2-21.5c-2.5-12-11.8-20-20.9-18S12 71.3 14.3 83.3q.8 3.4 2.2 6.3M132.3 91q2-4.4 2-10.2c-.2-9.5-5.3-17.2-11.4-17.2s-11 7.8-10.8 17.4c.1 5.2 1.7 9.9 4.2 13q7.6-3 16-3" fill="#2A1200"/><path d="M49.7 19a44 44 0 0 0-38.2 13.3c-3.2 3.4-4 9.3-.8 13 3 3.6 8.7 4.6 12 1l3.3-3c.3-.3 1.3-.8 1.6-1.2l.5-.4a38 38 0 0 1 6-3l1.7-.5a35 35 0 0 1 6.3-1.2h2l4.3.5c4.4.7 9-3.6 9.1-8.4.2-5.3-3-9.3-7.8-10m91.8 15.7a31 31 0 0 0-20.1-12.6c-2.1-.4-4.6.4-6.3 1.8a10 10 0 0 0-3.1 6.2c-.2 2.4.3 5 1.8 6.8a9 9 0 0 0 5.7 3.5q.6 0 1.1.3l3.3 1.4q1.7 1.2 3.3 2.7l1 1.3a9 9 0 0 0 5.7 3.5c2.1.2 4.7-.4 6.3-2 1.7-1.5 3-3.6 3.1-6q.4-3.9-1.8-7" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M431.7 162.7c20.2-6 26.5 20 27.4 37.2v3.3c.3 20.2-6.6 42.2-24.4 51.4a78 78 0 0 1-8-8.4q-11.2-10.5-24-18.6a142 142 0 0 0-86.2-22.6c-36.2 2.3-75.8 11.9-103 36.8-26.9 24.5-43.4 66.5-42.5 106.7l.2 4.8c.6 9.9 1.7 28.9-2.2 31.7l-.6.3-2.4 1.8c-3 2.3-6.6-3.8-11.3-12.1-5.6-10-13-23-23.7-28.5A41 41 0 0 0 82 357c-2-9.4-3-16-3.5-21.6v.1l-.3-8v.6-5.6a225 225 0 0 1 1.6-27.5c3.8-38 15.1-75.6 37.1-105.9 22.4-30.8 56.8-52.3 90.9-65l10.8-3.8a173 173 0 0 1 105-.3 221 221 0 0 1 64.6 36.3q2-2.8 4.9-4.8c8.3-5.8 21-2.1 25.7 7.3q2.4 5 4 10.3 3.5-4.6 9-6.4" fill="#3a1a00"/><path d="M323.7 120c23.3 7.7 44.6 21 64.5 36.3q1.6-2.1 3.7-3.8a41 41 0 0 0-6.7 11c-8-6.8-18.2-10.6-28.3-7.5a35 35 0 0 0-20.7 20.9c-23.2-14-48-19.3-76.4-13.9-34 6.4-63.8 30-83.6 60.4-26.6 41-27 118.3-6.9 161.2L166 387c-6.8 5-15.5-30.6-35-40.6A41 41 0 0 0 82 357c-3.9-17.5-3.9-25.5-3.9-34.5 0-10.9.6-16.4 1.6-27v-.5c4-38 15.3-75.6 37.3-106 22.4-30.8 56.8-52.3 90.9-65q5.4-2 10.8-3.7a173 173 0 0 1 105-.4" fill="black" fill-opacity=".1"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path fill-rule="evenodd" clip-rule="evenodd" d="M75 207a359 359 0 0 1 59.6 32l-2.8 4a366 366 0 0 0-58.6-31.3c-11.2-4.5-22.1-8-31.9-9.5a40 40 0 0 0-24 2l-2.2-4.4a45 45 0 0 1 27-2.5c10.3 1.6 21.6 5.2 33 9.8m57.1 122.4 9-2.3 1.2 4.8-8.5 2.2q-10.8 3.2-20.7 4.3c-9.5.8-19.3-.7-32.4-6.6l2-4.6c12.5 5.7 21.5 7 30 6.2 6.2-.5 12.2-2.1 19.4-4" fill="white"/><path d="M302.7 214a665 665 0 0 0-169.5 23.5c14 24 19 59.5 5 95.5 46 26 153.8 66.6 218.4 21a123 123 0 0 0 31.1-57c9.6-39.6-23-68.8-40.5-78.5-9.6-1.6-28.5-4.4-44.5-4.5" fill="#F7F7F7"/><path d="M127.7 241.5c2.4-4 13-7.2 19-7.5 3 3 9.2 17.1 10 49.5 1 40.5-7 52-8 54.5-.7 1.9-14-5-17-6.5s-3-4.5 0-11c6.5-14 3-54.5 0-63.5-2.4-7.2-3-12.8-4-15.5m197.5 5.5c-35.2-4.8-106 18-137 30 114.4-28.8 181.7-12 201 0-6.6-8-28.8-25.2-64-30m-97 59c26 12.3 91.4 31.2 145 8-25 20-89 46.4-145-8" fill="#EDEDED"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g2" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c2"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g2)" clip-path="url(#c2)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#8c5a2b"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#8c5a2b"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M147.7 64.4c9.3.2 18.3-2.5 26-10.5 4-4.2 8.2-6.5 14-3 13.9 8.7 17.1 30.2 13.4 44.6a63 63 0 0 1-30.7 36.8c-38.2 22.4-67.6 17.4-106.8 3-30.8-11.2-72.5-33.9-62-72.9 13.2-48.3 69.6-25 101.4-12 13.5 5.5 29.6 13.5 44.7 14" fill="white"/><path d="m109.5 147.2-6-.8L126 59.7l5.7 1.9zm79.7-30.2-5 5.3-13-66q1.3-1 2.5-2.4l2.7-2.4zm-130-81.6L26.8 117l-5-3.7 31.4-79z" fill="#D1D3D4"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M118 65.2c4 1.8 9.3 3.9 14.6 5.1 1.6 3.1 2.5 6.9 2.5 10.3 0 8-3.7 16.5-13 15.5-7.8-.8-9.7-11-9.7-16 0-6 2.2-12 5.7-15m-78.9-2.4a26 26 0 0 1 7.4 18.2c0 11.8-6.3 21.5-19.3 19.5C18 98 11.7 91.4 11.8 79q0-3 .6-6c12.4-1.5 21.2-6 26.8-10.2" fill="#2A1200"/><path d="M41 18.1A44 44 0 0 0 8.6 42.3c-2 4.2-1 10 3.3 12.7 4 2.4 9.6 1.7 11.7-2.7q1-2.1 2.2-3.9c.2-.4 1-1.2 1.1-1.7l.4-.5a38 38 0 0 1 4.7-4.6l1.6-1a35 35 0 0 1 5.2-2.9l.5-.2 1.9-.6 4.2-.8c4.4-.7 7.5-6.2 6.1-10.8-1.4-5-5.7-7.9-10.5-7.2m100.4 22.7c-3.2-7.8-9.1-14-16.5-17.2-2-.8-4.5-.6-6.5.3-2 1-3.8 3-4.6 5.2a10 10 0 0 0 .1 7 9 9 0 0 0 4.7 4.9l1 .5q1.5 1 2.8 2.2 1.4 1.6 2.6 3.4l.7 1.5a8 8 0 0 0 11.2 4.5q3.2-1.7 4.6-5.2 1.2-3.6-.1-7" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M370 205c-32.5 45-106 137.5-207 170.5a44 44 0 0 0-41-23.5c-27.5.5-37.5 20.5-37.5 20.5s-32 53.5 42 87.5c20.5 4 27 4 43.5-3 11.2 8.7 46.6 38.2 59 43-3.2 16.8-8.8 58.8-1.2 78-14.5 1.3-49.8 8.7-77.8-6.5-2.8-17.1-5.3-39.5-.9-48-5.3 8.5-14.8 28.2-14.8 39.5-16-5.5-51.6-20.8-66-38C46 420 38 165.5 264 144c19.8-2.3 70.4-.3 104 22.5C410 195 431 233 439 271c-17.2-8-55-32.4-69-66m82.5 358c-6.8-9.6-2.8-53.2 0-68-28 23.5-44.5 35-102 36.5 10 15 49 20.5 53.5 22.5l8-5.5v8a215 215 0 0 0 40.5 6.5" fill="#d56c0c"/><path d="M331.6 191.6c18-1.6 26.3 8.2 26.3 8.2-13.9 1.6-42.8 23.7-42.9 23.7 20.7-2.8 46.7-9.8 46.7-9.8q-.6 3.5-4 8.4 8-9.4 12.3-17.1a94 94 0 0 0 12.7 21.3 59 59 0 0 0-5.6-17.9s13.6 7 15.4 12.7c0 0-4.5-18-18.4-32 0 0 8.7-1.5 15.8 3.4 0 0-11.3-16.8-26-25.8h3.3l-1.7-1.2c-13-8.9-28.3-14.4-43.2-17.8a37 37 0 0 0-15.5 10.9c12.4-2 32 6.5 32 6.5-18.5-2-41.8 14.3-41.8 14.3a64 64 0 0 1 55.7 6.1c-8.3-.8-21 6.1-21 6.1M300 249c-14 17.7-59.6 60.6-130 91 22.3-14 79.6-51.8 130-91m-186 91c40.8-86.4 123.7-135.3 160-149-93.6 16-156 108.3-175.5 152.5A77 77 0 0 1 114 340M93 444.5c2 2.5 7.8 6.4 11 8a1048 1048 0 0 0-2.2 83.3 596 596 0 0 1-3.8-68.3c-3.7 13.3-9.9 17.8-5.5 73-4.7-35.7-11.9-74.4.5-96m334.5 70c2.8-1.2 7.5-4.5 9.5-6-3.6 6-2.2 36.2-1 50.5-2.4-2-4-9.5-4.5-13-1.6 5.2-4.7 5.5-4 13-4.4-10-.5-28.5 0-44.5m-225-8c-4.8 43.6-2.5 55.5 1.5 74h-20c-7.2-35.2 11.2-52 18.5-74" fill="black" fill-opacity=".2"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path fill-rule="evenodd" clip-rule="evenodd" d="m395.3 186.3-49-1.8-.6-10.5 49.8 2.3z" fill="#896307"/><path fill-rule="evenodd" clip-rule="evenodd" d="M220.3 123.7c-27.2 4.3-46.1 32.2-41.3 63 4.9 30.9 31.4 51.6 58.6 47.3s46.1-32.2 41.3-63-31.4-51.6-58.6-47.3m-52 52.2c.6-30.6 21-57.4 50.4-62 33.4-5.3 64.4 20 70 55.6l3.3-1.2a27 27 0 0 1 13-.7c.6-12.5 4.3-24 10.2-33a41 41 0 0 1 31.6-19.8 42 42 0 0 1 34 16.3 71 71 0 0 1 15.7 40.8 69 69 0 0 1-10 42.3 41 41 0 0 1-31.6 19.7 42 42 0 0 1-34-16.2 71 71 0 0 1-15.7-39.6q-5.2-1.9-10.4-.2-3.2 1-5.3 2.2c.3 31.3-20.3 59-50.3 63.8-33.4 5.2-64.4-20.1-70-55.6l-.3-2.2-86.4-14a102 102 0 0 0-44.4 2.6c-12 4-18.3 18.8-18.5 28l-9.9-.2c.3-15.3 11.4-32.6 25.3-37.3s31.7-5.3 48.9-3v.1zm155.2-35.8a59 59 0 0 0-8.5 36c1 14.1 6.1 26.5 13.4 35 7.4 8.7 16.6 13.3 26 12.8s17.8-6 23.8-15.2a59 59 0 0 0 8.5-36.1c-1-14.1-6.2-26.5-13.5-35s-16.5-13.3-25.9-12.8-17.8 6-23.8 15.3" fill="#C5900F"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g3" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c3"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g3)" clip-path="url(#c3)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#e2ba87"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#e2ba87"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M114.1 41.8c35.7 2.3 57.8 0 65 0 29.9-4.5 42.7 48.4 5 76.2-11.2 8.3-16.7 7-40.7 7s-50 9-64.5 4.9C37.8 118.3 30.5 88.5 34.2 72c5-22.1 15.2-34.4 80-30.3" fill="white"/><path d="m132.5 125.5-6 .7 3.2-83.6 6 .1zm64-19.6a58 58 0 0 1-5.1 5.9l-11.1-70.2q3.2-.2 6.1.3zM69.2 42.3 56.6 120q-3-2-5.5-4L63 43.5q3-.7 6.3-1.2" fill="#D1D3D4"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M46.3 80.9c0 11.9-6.2 21.5-19 19.5C18 98 11.7 91.4 11.7 79c0-13 7.6-21.5 15.5-21.5 10.6 0 19 11.6 19 23.5m85-11.5c2 3.3 3 7.4 3 11.2 0 8-3.6 16.5-12.9 15.5-7.7-.8-9.7-11-9.7-16q0-3.6 1-6.8c5.6.3 13.4-.3 18.6-3.9" fill="#2A1200"/><path d="M48.2 18.5a44 44 0 0 0-37.7 14.6c-3.1 3.4-3.8 9.4-.4 13 3.2 3.4 8.8 4.3 12.1.6q1.5-1.7 3.1-3.1c.4-.4 1.3-1 1.6-1.4q.3 0 .5-.3a38 38 0 0 1 5.8-3.2L35 38a35 35 0 0 1 6.2-1.4h2q2.3 0 4.3.3c4.5.6 9-4 8.9-8.7 0-5.3-3.5-9.2-8.2-9.8m87.5 38.1c-7.5 4-16 5-23.7 2.7a8 8 0 0 1-4.8-4.4c-.9-2-1-4.8-.2-7a10 10 0 0 1 4.6-5.3 9 9 0 0 1 6.6-.7l1.2.3h3.5q2.1-.2 4.2-1l1.4-.6c2-1.1 4.6-1.4 6.7-.7 2 .6 4 2.3 4.8 4.4.9 2 1.2 4.6.2 6.9q-1.4 3.5-4.5 5.4" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M385.8 208.9c-8 7.2-16.8 17.6-26.5 29.1-16.3 19.4-35.3 41.8-57.3 57.3a227 227 0 0 1-86.5 35.3 86 86 0 0 0-16.4 32c-3.2 9.9-5.5 17-13 17.4-9-20.6-25.4-54.2-71.1-39-38.5 12.8-40.4 56.3-28 78.5 7 12.4 35.3 40 81 34l29.5 25.5c-2.9 5.4-8.2 13.1-25.8 20.7-21.9 9.5-123.8 1-133.3-60.8-23.8-20.6-61.1-78.2-20-143.6a114 114 0 0 1 51.3-43.7q-.3-12.3.1-28.6c2-60 58.1-108.4 102-118.9 42.7-10.2 132.8-6.8 169.8 26.2C462 85.5 492.8 231.9 488.5 287.5c22.4 44 7 106.7-1 131.5-4-33.6-31.3-56-44-61.5 5.6-30.4-5-82-11-104a139 139 0 0 0-46.7-44.6" fill="#605de4"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path d="M64.2 63c101.9-11.2 203 37.8 229.5 60.5 9.6-11.2 25.4-24.7 36.4-26 2.7 6 7 21 8.1 24.5-16 0-33.5 19.8-42.1 33.5-66.9-58-160-58-254.9-58a96 96 0 0 1 23-34.5" fill="#FFC00C"/><path d="M200 108.8a308 308 0 0 0-18.4-3.6L204 79.7q8.1 2.6 15.8 5.4zm46.7 15.7-5.4-2.4L257 101l5.2 2.7zm19.1 9.9-11-6 14.7-21 9.4 5.6zm64.6-11a55 55 0 0 0-19 13l19.2-37.7c1.6 4 3.7 10.7 5.4 16z" fill="#FFD45B"/><ellipse cx="294.4" cy="135.5" rx="8.8" ry="13.5" fill="#FF3C15"/><path d="M294.4 149c4.8 0 8.8-6 8.8-13.5l-.2-2.5a26 26 0 0 1-14 13q2.2 3 5.4 3" fill="#CB1414"/><path d="M290.7 133.5c1.5.6 3.5-1 4.3-3.7.8-2.5.2-5-1.3-5.6s-3.5 1.1-4.3 3.7c-.8 2.5-.2 5 1.3 5.6" fill="#FF7256"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g4" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c4"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g4)" clip-path="url(#c4)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#efcc9f"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#efcc9f"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M155.7 51c3 1.7 24.1-24.8 38.2-16.7 4.7 2.7 8.3 7.3 11 12.3 6 11 8 24.4 5.6 36.8a67 67 0 0 1-29 41.2c-37.3 25.5-67 20.7-106.7 6C43.4 118.9.8 95 9.6 51.9c15.5-76 146-1 146.1-1" fill="#873C41"/><path d="M47.7 46c10.2 6.4 22.5 7 33.6 4.5 2-.5 3.8 1.3 4.3 3.4 5.4 21 36 32 51.5 18.5 1.2-1 3.3-1.3 4.5-.1 8.3 7.8 21.6 10 31.4 4.6 13.8-7.5 13.7-28.6 10-43.3-12.2 3.7-25 18.7-27.3 17.3 0 0-80.1-46-122.3-29.4a31 31 0 0 0 14.3 24.6" fill="white"/><path d="M162.7 127.8c-22-20.1-48.9-31.4-76.2-39.3C63 81.6 36.8 77.2 13 84c11 23.5 39.2 38.2 61.7 46.6 34.8 12.9 61.8 18.2 93 2.2z" fill="#EE7E8B"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M118 65.2c4 1.8 9.3 3.9 14.6 5.1 1.6 3.1 2.5 6.9 2.5 10.3 0 8-3.7 16.5-13 15.5-7.8-.8-9.7-11-9.7-16 0-6 2.2-12 5.7-15m-78.9-2.4a26 26 0 0 1 7.4 18.2c0 11.8-6.3 21.5-19.3 19.5C18 98 11.7 91.4 11.8 79q0-3 .6-6c12.4-1.5 21.2-6 26.8-10.2" fill="#2A1200"/><path d="M41 18.1A44 44 0 0 0 8.6 42.3c-2 4.2-1 10 3.3 12.7 4 2.4 9.6 1.7 11.7-2.7q1-2.1 2.2-3.9c.2-.4 1-1.2 1.1-1.7l.4-.5a38 38 0 0 1 4.7-4.6l1.6-1a35 35 0 0 1 5.2-2.9l.5-.2 1.9-.6 4.2-.8c4.4-.7 7.5-6.2 6.1-10.8-1.4-5-5.7-7.9-10.5-7.2m100.4 22.7c-3.2-7.8-9.1-14-16.5-17.2-2-.8-4.5-.6-6.5.3-2 1-3.8 3-4.6 5.2a10 10 0 0 0 .1 7 9 9 0 0 0 4.7 4.9l1 .5q1.5 1 2.8 2.2 1.4 1.6 2.6 3.4l.7 1.5a8 8 0 0 0 11.2 4.5q3.2-1.7 4.6-5.2 1.2-3.6-.1-7" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M459 140.8c-74-50.6-244.4-113.6-334 40l28.2 32a210 210 0 0 0-25.7 43.2c-8.8 22-12 46.3-12.6 70.6q-.1 6.9.2 13.8c38.1 6.5 56.1 47.3 60.4 67 0 0 11.3.6 14-1.6 14-7.3 38.9-37.4 26.5-99-6-28-2-50.3 47-85 20.6-14.5 46.3-19.2 70.3-18.7l10 .5c29.2 2.3 54.4 11.5 62.7 18.2 12.8 10.4 19 14.4 20.4 15q-8.4-18.9-19-32.8c7.8-19 21.9-44.5 40.7-57.4q5.2-3.7 10.9-5.8" fill="#605de4"/><path d="M125 180.8c89.6-153.6 260-90.6 334-40-20-13.7-115.5-40.5-159-18.5l.4.7c-83-18-151.1 30.5-175.4 57.8" fill="white" fill-opacity=".2"/><path d="M459 140.8c-30.4 11.2-51 58-57 78-28.5-14-47-15-77-16 4-32.8-17.7-67.8-25-80.5 43.5-22 139 4.8 159 18.5" fill="black" fill-opacity=".1"/><path d="M325.2 201.2c-.8-12-40.7-41.1-81.2-36.4-54.1 6.4-86.5 45-89 50l-1.8-2a210 210 0 0 0-25.7 43.2c-8.8 22-12 46.3-12.6 70.6q-.1 6.9.2 13.8c38.1 6.5 56.1 47.3 60.4 67 0 0 11.3.6 14-1.6 14-7.3 38.9-37.4 26.5-99-6-28-2-50.3 47-85 20.6-14.5 46.3-19.2 70.3-18.7l-8.3-.3zm18.1 2.4c29.2 2.3 54.4 11.5 62.7 18.2 12.8 10.4 19 14.4 20.4 15q-8.4-18.9-19-32.8-3.6 8.6-5.4 14.7v.1a138 138 0 0 0-58.7-15.2" fill="white" fill-opacity=".7"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path d="M309.7 232.7c9-5.2 30.7-14.3 45.7-9 18 6.3 25.4 29.3 37.6 26.7q.6.2.4.6c-5.8 4.7-26 20-39.5 20.6-17.9 1-25.6-8.6-40.1-22.7-4 10.2-18 23.7-50.5 40-59 29.5-97.6-3-116.3-25-.2-.3.3-.8.6-.7 7.2 4.2 15.3.5 35.8-9 32.5-15 53.5-31.3 78.5-31.5a125 125 0 0 1 47.8 10" fill="#71472D"/><path d="M255.9 240.5c11-4.8 32.5-1 42 1.4-33-1-42.4 11.1-68.9 25.3a54 54 0 0 1-49.6 1c27.8 8.8 62.8-21.6 76.5-27.7m26.9 17.9c-8.2 7.6-28.2 22.8-42.2 23.2 10.4-7.9 33.4-23.5 42.2-23.2m44.7-22.6c4.6-2.2 15.8-5.1 24 .8 9.3 6.5 16.5 12.3 21.2 12.4-4.8.4-14-.2-21.4-5.4a48 48 0 0 0-23.8-7.8" fill="#5E351B"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g5" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c5"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g5)" clip-path="url(#c5)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#643d19"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#643d19"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M147.7 64.4c9.3.2 18.3-2.5 26-10.5 4-4.2 8.2-6.5 14-3 13.9 8.7 17.1 30.2 13.4 44.6a63 63 0 0 1-30.7 36.8c-38.2 22.4-67.6 17.4-106.8 3-30.8-11.2-72.5-33.9-62-72.9 13.2-48.3 69.6-25 101.4-12 13.5 5.5 29.6 13.5 44.7 14" fill="white"/><path d="m109.5 147.2-6-.8L126 59.7l5.7 1.9zm79.7-30.2-5 5.3-13-66q1.3-1 2.5-2.4l2.7-2.4zm-130-81.6L26.8 117l-5-3.7 31.4-79z" fill="#D1D3D4"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M118 65.2c4 1.8 9.3 3.9 14.6 5.1 1.6 3.1 2.5 6.9 2.5 10.3 0 8-3.7 16.5-13 15.5-7.8-.8-9.7-11-9.7-16 0-6 2.2-12 5.7-15m-78.9-2.4a26 26 0 0 1 7.4 18.2c0 11.8-6.3 21.5-19.3 19.5C18 98 11.7 91.4 11.8 79q0-3 .6-6c12.4-1.5 21.2-6 26.8-10.2" fill="#2A1200"/><path d="M41 18.1A44 44 0 0 0 8.6 42.3c-2 4.2-1 10 3.3 12.7 4 2.4 9.6 1.7 11.7-2.7q1-2.1 2.2-3.9c.2-.4 1-1.2 1.1-1.7l.4-.5a38 38 0 0 1 4.7-4.6l1.6-1a35 35 0 0 1 5.2-2.9l.5-.2 1.9-.6 4.2-.8c4.4-.7 7.5-6.2 6.1-10.8-1.4-5-5.7-7.9-10.5-7.2m100.4 22.7c-3.2-7.8-9.1-14-16.5-17.2-2-.8-4.5-.6-6.5.3-2 1-3.8 3-4.6 5.2a10 10 0 0 0 .1 7 9 9 0 0 0 4.7 4.9l1 .5q1.5 1 2.8 2.2 1.4 1.6 2.6 3.4l.7 1.5a8 8 0 0 0 11.2 4.5q3.2-1.7 4.6-5.2 1.2-3.6-.1-7" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M370 205c-32.5 45-106 137.5-207 170.5a44 44 0 0 0-41-23.5c-27.5.5-37.5 20.5-37.5 20.5s-32 53.5 42 87.5c20.5 4 27 4 43.5-3 11.2 8.7 46.6 38.2 59 43-3.2 16.8-8.8 58.8-1.2 78-14.5 1.3-49.8 8.7-77.8-6.5-2.8-17.1-5.3-39.5-.9-48-5.3 8.5-14.8 28.2-14.8 39.5-16-5.5-51.6-20.8-66-38C46 420 38 165.5 264 144c19.8-2.3 70.4-.3 104 22.5C410 195 431 233 439 271c-17.2-8-55-32.4-69-66m82.5 358c-6.8-9.6-2.8-53.2 0-68-28 23.5-44.5 35-102 36.5 10 15 49 20.5 53.5 22.5l8-5.5v8a215 215 0 0 0 40.5 6.5" fill="#238d80"/><path d="M331.6 191.6c18-1.6 26.3 8.2 26.3 8.2-13.9 1.6-42.8 23.7-42.9 23.7 20.7-2.8 46.7-9.8 46.7-9.8q-.6 3.5-4 8.4 8-9.4 12.3-17.1a94 94 0 0 0 12.7 21.3 59 59 0 0 0-5.6-17.9s13.6 7 15.4 12.7c0 0-4.5-18-18.4-32 0 0 8.7-1.5 15.8 3.4 0 0-11.3-16.8-26-25.8h3.3l-1.7-1.2c-13-8.9-28.3-14.4-43.2-17.8a37 37 0 0 0-15.5 10.9c12.4-2 32 6.5 32 6.5-18.5-2-41.8 14.3-41.8 14.3a64 64 0 0 1 55.7 6.1c-8.3-.8-21 6.1-21 6.1M300 249c-14 17.7-59.6 60.6-130 91 22.3-14 79.6-51.8 130-91m-186 91c40.8-86.4 123.7-135.3 160-149-93.6 16-156 108.3-175.5 152.5A77 77 0 0 1 114 340M93 444.5c2 2.5 7.8 6.4 11 8a1048 1048 0 0 0-2.2 83.3 596 596 0 0 1-3.8-68.3c-3.7 13.3-9.9 17.8-5.5 73-4.7-35.7-11.9-74.4.5-96m334.5 70c2.8-1.2 7.5-4.5 9.5-6-3.6 6-2.2 36.2-1 50.5-2.4-2-4-9.5-4.5-13-1.6 5.2-4.7 5.5-4 13-4.4-10-.5-28.5 0-44.5m-225-8c-4.8 43.6-2.5 55.5 1.5 74h-20c-7.2-35.2 11.2-52 18.5-74" fill="black" fill-opacity=".2"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><ellipse cx="303.2" cy="231.5" rx="29" ry="28.5" fill="#FF3C15"/><path d="M331.8 235.6a32 32 0 0 1-25.6 13.9A43 43 0 0 1 276 237a28.4 28.4 0 0 0 56-1.4" fill="#DD2400"/><path d="M291.9 227.3c5 1.2 11.1-2.3 13.8-7.7s.8-10.7-4.1-11.8c-5-1.1-11.2 2.3-13.9 7.7-2.6 5.4-.8 10.7 4.2 11.8" fill="#FF7256"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g6" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c6"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g6)" clip-path="url(#c6)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#a47539"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#a47539"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M155.7 51c3 1.7 24.1-24.8 38.2-16.7 4.7 2.7 8.3 7.3 11 12.3 6 11 8 24.4 5.6 36.8a67 67 0 0 1-29 41.2c-37.3 25.5-67 20.7-106.7 6C43.4 118.9.8 95 9.6 51.9c15.5-76 146-1 146.1-1" fill="#873C41"/><path d="M47.7 46c10.2 6.4 22.5 7 33.6 4.5 2-.5 3.8 1.3 4.3 3.4 5.4 21 36 32 51.5 18.5 1.2-1 3.3-1.3 4.5-.1 8.3 7.8 21.6 10 31.4 4.6 13.8-7.5 13.7-28.6 10-43.3-12.2 3.7-25 18.7-27.3 17.3 0 0-80.1-46-122.3-29.4a31 31 0 0 0 14.3 24.6" fill="white"/><path d="M162.7 127.8c-22-20.1-48.9-31.4-76.2-39.3C63 81.6 36.8 77.2 13 84c11 23.5 39.2 38.2 61.7 46.6 34.8 12.9 61.8 18.2 93 2.2z" fill="#EE7E8B"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M130 66.6c3.2 3.5 5.1 9 5.1 14 0 8-3.6 16.6-12.8 15.6-7.8-.8-9.7-11-9.7-16q0-2.9.6-5.5a80 80 0 0 0 16.9-8.1M17.4 63.5a49 49 0 0 0 29.1 11q.8 3 .8 6.4c0 11.9-6.2 21.5-19.1 19.5-9.3-2.5-15.5-9-15.4-21.5 0-6.4 1.8-11.7 4.6-15.4" fill="#2A1200"/><path d="M48.3 50.7A44 44 0 0 1 9.8 38.1c-3.2-3.3-4.2-9.2-1-13 3-3.6 8.6-4.7 12-1.2l3.4 3c.3.3 1.3.8 1.6 1.2l.5.3a38 38 0 0 0 6 3q.9.1 1.8.5a35 35 0 0 0 6.3 1h2l4.3-.6c4.4-.8 9 3.5 9.3 8.3.2 5.2-3 9.3-7.7 10.1m89.5-8.9c-5.9 6-13.7 9.5-21.7 9.7a8 8 0 0 1-6-2.8 10 10 0 0 1-2.2-6.5 10 10 0 0 1 2.8-6.5 9 9 0 0 1 6.1-2.7h1.2l3.4-1q2-1 3.7-2.2l1.2-1.1c1.6-1.7 4-2.7 6.1-2.7s4.6 1 6 2.8 2.4 4 2.2 6.5q-.2 3.8-2.8 6.5" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><g fill="#605de4"><path fill-rule="evenodd" clip-rule="evenodd" d="m360.7 206 .9 1 .8-.8c20.8 3.8 37.4 10.5 44 15.8a156 156 0 0 0 20.3 15 249 249 0 0 1 15.9 55c4 24.8 3 53.7 2 65q2.2 1.8 4.9 3.5c5 3.5 10.7 7.5 18 14.5a61 61 0 0 0 6.6-47.5c-1.4-6.4-4-12.9-7.1-20.6-6.5-15.7-15-36.9-19.4-74.4-4-33.8-32-84.5-64-95.5-25.6-8.8-45.7-3.7-51 0-55-46-140-14-163 0-10.8 6.5-19.5 16-28.7 26.1a163 163 0 0 1-37.3 32.9c-29 17-35 60-29 89s9 53-6 72-23 47-6 80c13.6 26.4 9.6 49 6 57 17.4 16 55.3 29 92.5 38.4 1.8.5 2.3-4.7 2.7-10 .5-5.1 1-10.3 2.8-9.9l1.6 2.3.7 3.6c1.3 6.6 3.3 16.9 15.2 19.5a990 990 0 0 0 71.5 13.1c13.1-22.4-2.3-53.5-16.1-81.2a173 173 0 0 1-16-38.8c-3.3-18.1 3.5-31.1 12.3-47.7a202 202 0 0 0 19.8-48.3c4.5-18.2 3.3-33 2.1-47.3-1.7-21.2-3.4-41.5 13.6-70.7 27.5-15.4 61.8-15.8 89.4-11m63.5 25.5"/><path d="M461 486c1.2 5.8 2.9 19.2 0 26a129 129 0 0 1-34 2c6.4-3.8 22-14.8 34-28"/></g><path d="M362.4 206.2c20.8 3.8 37.4 10.5 44 15.8a156 156 0 0 0 20.3 15 163 163 0 0 0-5.5-11.4C410.2 205 395 190 375.1 196a22 22 0 0 0-8.1 6zm-84.8.8c29.8-44 67.2-19.6 83-1-27.5-4.8-61.8-4.4-89.3 11q2.8-4.8 6.3-10" fill="black" fill-opacity=".3"/><path d="M235.6 210c23.2-40 72.3-46.7 94-45-117-12-128 68-128 99 0 24.8 3 39-5 53a72 72 0 0 0-13 79c-5-26 5-45 27-70s-4-66 25-116m-34 272c-14.4-14.4-18-28-18-33 46.4 24 37.3 61.3 27 77 3-8.7 5.4-29.6-9-44m-70-165c8.8-20 7-43.7 5-53 0 30-19 62-40 100-16.8 30.4 7 66 21 80 3-16-10-28-11-57s14-45 25-70m331 54c5.2-16.4 1.5-39.5-1-49 0 16.4-3 36.2-4.5 44z" fill="black" fill-opacity=".2"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path d="M108.5 5.9c11.1-17.1 55.7 14.7 77.2 33-29.2-1.8-45.2 37.3-50.4 56.4-9.2 9-24.5 10.7-30.7 10.7-3.4-26.3-7.3-83 3.9-100.1" fill="#A09B9B"/><path d="m101.7 74.4-.3-7 11.6 4zm.9 12.7 1.4 14c7.3-2.7 15.8-5.4 19.4-6.6z" fill="#C4C4C4"/><path d="M185.7 39c-28.5 9.7-45.4 41.2-50.3 55.7C113 66.8 101.7 18 113.3 5.6c9.3-10 52.1 18 72.4 33.3" fill="#FEB3A4"/><path d="M136 93c5.5-15 22.2-44.7 49.7-54l-5.9-4.4c-7.6-2.6-18-5-30.1-5.1C129.4 29 131.8 70 135.9 93" fill="#FFE7E2"/><path d="M304 56.9a25 25 0 0 1 13 10.7c1-19.4 0-57.3-13.3-57.7-8-.3-21.3 7.7-33.3 16.7A164 164 0 0 1 304 56.9" fill="#A09B9B"/><path d="M317.2 74.8c0-18-2.6-60.3-11.4-62.2-7-1.5-18 7.6-26.6 20.4a167 167 0 0 1 38 41.8" fill="#FEB3A4"/><path d="M317.2 74.8q0-6.4-.5-15.3-3-7-8.2-15.2c-4.5-7.1-11.8-6.5-19.2-2.8a168 168 0 0 1 28 33.3" fill="#FFE7E2"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g7" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c7"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g7)" clip-path="url(#c7)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#e2ba87"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#e2ba87"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M155.7 51c3 1.7 24.1-24.8 38.2-16.7 4.7 2.7 8.3 7.3 11 12.3 6 11 8 24.4 5.6 36.8a67 67 0 0 1-29 41.2c-37.3 25.5-67 20.7-106.7 6C43.4 118.9.8 95 9.6 51.9c15.5-76 146-1 146.1-1" fill="#873C41"/><path d="M47.7 46c10.2 6.4 22.5 7 33.6 4.5 2-.5 3.8 1.3 4.3 3.4 5.4 21 36 32 51.5 18.5 1.2-1 3.3-1.3 4.5-.1 8.3 7.8 21.6 10 31.4 4.6 13.8-7.5 13.7-28.6 10-43.3-12.2 3.7-25 18.7-27.3 17.3 0 0-80.1-46-122.3-29.4a31 31 0 0 0 14.3 24.6" fill="white"/><path d="M162.7 127.8c-22-20.1-48.9-31.4-76.2-39.3C63 81.6 36.8 77.2 13 84c11 23.5 39.2 38.2 61.7 46.6 34.8 12.9 61.8 18.2 93 2.2z" fill="#EE7E8B"/><path d="m42 19 7 4 14 7.1a470 470 0 0 0 51.6 20.1l2.2.8c14.4 5 32.2 10.4 51.4 10.4a31 31 0 0 0 15-4.3 1.5 1.5 0 1 1 1.3 2.6 34 34 0 0 1-16.2 4.7c-19.8 0-38-5.6-52.5-10.6l-2.2-.7a487 487 0 0 1-52-20.4l-1.1-.5-13.1-6.7q-5.6-3-9.6-5.6z" fill="#C4C4C4"/><rect x="162.1" y="56" width="12" height="12" rx="2" transform="rotate(3.2 162 56)" fill="#A09E9E"/><rect x="111.5" y="45" width="12" height="12" rx="2" transform="rotate(15 111.5 45)" fill="#A09E9E"/><rect x="59.7" y="24" width="12" height="12" rx="2" transform="rotate(26.4 59.7 24)" fill="#A09E9E"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M47.3 80.9c0 11.9-6.2 21.5-19 19.5C19 98 12.7 91.4 12.7 79c0-13 7.6-21.5 15.5-21.5 10.6 0 19 11.6 19 23.5m88-.3c0 8-3.6 16.5-12.9 15.5-7.7-.8-9.7-11-9.7-16 0-8.7 4.4-17 10.8-17 7 0 11.8 9.5 11.8 17.5" fill="#2A1200"/><path d="M53.1 24.6A44 44 0 0 0 12.7 26c-4 2.3-6.6 7.7-4.5 12.3 1.9 4.2 6.9 6.9 11.2 4.5l4-2c.4-.2 1.4-.4 1.9-.7l.6-.2a38 38 0 0 1 6.5-1.1h1.9a35 35 0 0 1 5.8.6l.5.2 2 .6q2 .7 3.9 1.6c4 2 9.7-.8 11.2-5.3 1.7-5-.3-9.8-4.6-12m86.6 4.5c-7.4-4.1-16-5-23.6-2.8-2 .6-4 2.4-4.9 4.4-.8 2-1 4.8-.2 7a10 10 0 0 0 4.6 5.3 9 9 0 0 0 6.7.7l1.1-.3h3.5q2.1.2 4.2 1l1.5.6c2 1.1 4.6 1.4 6.6.7s4-2.3 4.9-4.4c.8-2 1-4.6.2-6.9q-1.4-3.5-4.6-5.4" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M113.2 325.6q-.2 6.9.1 13.8c38.5 6.4 56.7 47.2 61 66.8 0 0 11.4.6 14-1.5 14.3-7.4 39.5-37.5 27.1-99.1-6-28-1.9-50.4 47.8-85.2s128.5-13.3 144.6-.4c13 10.4 14.3 12 18.2 14.5-34.4-77.5-101.9-87.7-101.9-87.7-136.9-34.2-195.3 108.6-195.3 108.6-9 22-15 45.8-15.6 70.2" fill="#71472d"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path fill-rule="evenodd" clip-rule="evenodd" d="m395.3 186.3-49-1.8-.6-10.5 49.8 2.3z" fill="#896307"/><path fill-rule="evenodd" clip-rule="evenodd" d="M220.3 123.7c-27.2 4.3-46.1 32.2-41.3 63 4.9 30.9 31.4 51.6 58.6 47.3s46.1-32.2 41.3-63-31.4-51.6-58.6-47.3m-52 52.2c.6-30.6 21-57.4 50.4-62 33.4-5.3 64.4 20 70 55.6l3.3-1.2a27 27 0 0 1 13-.7c.6-12.5 4.3-24 10.2-33a41 41 0 0 1 31.6-19.8 42 42 0 0 1 34 16.3 71 71 0 0 1 15.7 40.8 69 69 0 0 1-10 42.3 41 41 0 0 1-31.6 19.7 42 42 0 0 1-34-16.2 71 71 0 0 1-15.7-39.6q-5.2-1.9-10.4-.2-3.2 1-5.3 2.2c.3 31.3-20.3 59-50.3 63.8-33.4 5.2-64.4-20.1-70-55.6l-.3-2.2-86.4-14a102 102 0 0 0-44.4 2.6c-12 4-18.3 18.8-18.5 28l-9.9-.2c.3-15.3 11.4-32.6 25.3-37.3s31.7-5.3 48.9-3v.1zm155.2-35.8a59 59 0 0 0-8.5 36c1 14.1 6.1 26.5 13.4 35 7.4 8.7 16.6 13.3 26 12.8s17.8-6 23.8-15.2a59 59 0 0 0 8.5-36.1c-1-14.1-6.2-26.5-13.5-35s-16.5-13.3-25.9-12.8-17.8 6-23.8 15.3" fill="#C5900F"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g8" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c8"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g8)" clip-path="url(#c8)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#c99c62"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#c99c62"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M114.1 41.8c35.7 2.3 57.8 0 65 0 29.9-4.5 42.7 48.4 5 76.2-11.2 8.3-16.7 7-40.7 7s-50 9-64.5 4.9C37.8 118.3 30.5 88.5 34.2 72c5-22.1 15.2-34.4 80-30.3" fill="white"/><path d="m132.5 125.5-6 .7 3.2-83.6 6 .1zm64-19.6a58 58 0 0 1-5.1 5.9l-11.1-70.2q3.2-.2 6.1.3zM69.2 42.3 56.6 120q-3-2-5.5-4L63 43.5q3-.7 6.3-1.2" fill="#D1D3D4"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M57 89.3a48 48 0 0 0-43-6.7q-.4 0-.3-.4c21.6-24.5 37.9-6.1 43.7 6.8q0 .4-.4.3m78.2-8.6c0 8-3.6 16.5-12.9 15.5-7.7-.8-9.7-11-9.7-16 0-8.7 4.4-17 10.8-17 7 0 11.8 9.5 11.8 17.5" fill="#2A1200"/><path d="M49.7 19a44 44 0 0 0-38.2 13.3c-3.2 3.4-4 9.3-.8 13 3 3.6 8.7 4.6 12 1l3.3-3c.3-.3 1.3-.8 1.6-1.2l.5-.4a38 38 0 0 1 6-3l1.7-.5a35 35 0 0 1 6.3-1.2h2l4.3.5c4.4.7 9-3.6 9.1-8.4.2-5.3-3-9.3-7.8-10m91.8 15.7a31 31 0 0 0-20.1-12.6c-2.1-.4-4.6.4-6.3 1.8a10 10 0 0 0-3.1 6.2c-.2 2.4.3 5 1.8 6.8a9 9 0 0 0 5.7 3.5q.6 0 1.1.3l3.3 1.4q1.7 1.2 3.3 2.7l1 1.3a9 9 0 0 0 5.7 3.5c2.1.2 4.7-.4 6.3-2 1.7-1.5 3-3.6 3.1-6q.4-3.9-1.8-7" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><g fill="#3a1a00"><path fill-rule="evenodd" clip-rule="evenodd" d="m360.7 206 .9 1 .8-.8c20.8 3.8 37.4 10.5 44 15.8a156 156 0 0 0 20.3 15 249 249 0 0 1 15.9 55c4 24.8 3 53.7 2 65q2.2 1.8 4.9 3.5c5 3.5 10.7 7.5 18 14.5a61 61 0 0 0 6.6-47.5c-1.4-6.4-4-12.9-7.1-20.6-6.5-15.7-15-36.9-19.4-74.4-4-33.8-32-84.5-64-95.5-25.6-8.8-45.7-3.7-51 0-55-46-140-14-163 0-10.8 6.5-19.5 16-28.7 26.1a163 163 0 0 1-37.3 32.9c-29 17-35 60-29 89s9 53-6 72-23 47-6 80c13.6 26.4 9.6 49 6 57 17.4 16 55.3 29 92.5 38.4 1.8.5 2.3-4.7 2.7-10 .5-5.1 1-10.3 2.8-9.9l1.6 2.3.7 3.6c1.3 6.6 3.3 16.9 15.2 19.5a990 990 0 0 0 71.5 13.1c13.1-22.4-2.3-53.5-16.1-81.2a173 173 0 0 1-16-38.8c-3.3-18.1 3.5-31.1 12.3-47.7a202 202 0 0 0 19.8-48.3c4.5-18.2 3.3-33 2.1-47.3-1.7-21.2-3.4-41.5 13.6-70.7 27.5-15.4 61.8-15.8 89.4-11m63.5 25.5"/><path d="M461 486c1.2 5.8 2.9 19.2 0 26a129 129 0 0 1-34 2c6.4-3.8 22-14.8 34-28"/></g><path d="M362.4 206.2c20.8 3.8 37.4 10.5 44 15.8a156 156 0 0 0 20.3 15 163 163 0 0 0-5.5-11.4C410.2 205 395 190 375.1 196a22 22 0 0 0-8.1 6zm-84.8.8c29.8-44 67.2-19.6 83-1-27.5-4.8-61.8-4.4-89.3 11q2.8-4.8 6.3-10" fill="black" fill-opacity=".3"/><path d="M235.6 210c23.2-40 72.3-46.7 94-45-117-12-128 68-128 99 0 24.8 3 39-5 53a72 72 0 0 0-13 79c-5-26 5-45 27-70s-4-66 25-116m-34 272c-14.4-14.4-18-28-18-33 46.4 24 37.3 61.3 27 77 3-8.7 5.4-29.6-9-44m-70-165c8.8-20 7-43.7 5-53 0 30-19 62-40 100-16.8 30.4 7 66 21 80 3-16-10-28-11-57s14-45 25-70m331 54c5.2-16.4 1.5-39.5-1-49 0 16.4-3 36.2-4.5 44z" fill="black" fill-opacity=".2"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path d="M175 195.1c3.8 15.9 11.2 30.3 24.3 35.3 29 11 71 3 77-3.5a67 67 0 0 0 7.7-20.4c17.1-9.6 31.9-4.4 36.7-1 1.3 7.7 2.7 14 3.7 15.8 3 5.6 24.3 12.5 39 3 14.8-9.5 15.3-67 12.5-73-3.4-7.3-55.7-3.8-57.9 10-1.5 4.2-.7 17.2.8 30-8-2.6-19.1-3.7-31.4.5 3-14.8 4.3-29.6 1.4-34.4-5-17.4-111.6-24.9-114-11.5-2.5 7-3.7 21.6-1.9 36.9a12627 12627 0 0 0-131-16.3 47 47 0 0 0-25.9 6.2c-4.6 2.7-9 6.5-12.1 11.9Q-1 192.8.3 204.2l11.9-1.4c-.6-5.3.4-9.3 2-12.3q2.9-4.5 7.9-7.4c6.8-4 15-5 18.5-4.6 16 1.7 91.3 11.1 134.5 16.6" fill="#595757" style="mix-blend-mode:multiply"/><path opacity=".2" d="M243.2 141.4c-10.7-1.4-22-2.2-32.2-2.3l-40 55.5 4.2.5a68 68 0 0 0 8.6 21.8zm34.6 7.8a122 122 0 0 0-21.5-5.7L190.6 225q3.9 3.3 8.7 5.3 3.6 1.3 7.3 2.3zm98.8 33.5c1-13 .7-25.2-.3-29.8l-52.9 65.7 1 2.7c1.4 2.6 6.7 5.4 13.4 6.8z" fill="white"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g9" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c9"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g9)" clip-path="url(#c9)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#e2ba87"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#e2ba87"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M73.7 46.3C101.4 30 125 29 141.7 28c33.3-2 61.1 8.8 68.5 42.7 6.8 31.2-16.9 49.4-27.8 49S169 107.5 143 106.9s-63.4 42.9-85.5 26.6c-22.2-16.4-18.4-66.6 16.1-87" fill="#873C41"/><path d="M195.7 114.2a23 23 0 0 1-13.1 5.3c-4.3-.1-7.3-2-10.9-4.4a48 48 0 0 0-28.5-8.5c-11-.2-24.2 7.4-37.2 15.1-17.8 10.4-35.6 20.9-48.4 11.5-11.7-8.6-16.2-26.4-12.8-44.4 20-6 42.3-5.6 62.8-3.3 29.5 3.3 59.2 10 85.4 26.8z" fill="#EE7E8B"/><path d="M95.8 52.5a47 47 0 0 1-38 7.5q6.6-8 16-13.7c27-16 50-17.3 66.7-18.4h1.3c21.6-1.4 40.8 2.7 53.7 14.9l-.5.7a29 29 0 0 1-32.3 7.3c-1.7-.7-3.6.4-4.4 1.9-9.8 18.9-43.8 19.8-57 1.4-1.3-1.7-3.6-2.8-5.5-1.6" fill="white"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M46.3 80.9c0 11.9-6.2 21.5-19 19.5C18 98 11.7 91.4 11.7 79c0-13 7.6-21.5 15.5-21.5 10.6 0 19 11.6 19 23.5m85-11.5c2 3.3 3 7.4 3 11.2 0 8-3.6 16.5-12.9 15.5-7.7-.8-9.7-11-9.7-16q0-3.6 1-6.8c5.6.3 13.4-.3 18.6-3.9" fill="#2A1200"/><path d="M48.2 18.5a44 44 0 0 0-37.7 14.6c-3.1 3.4-3.8 9.4-.4 13 3.2 3.4 8.8 4.3 12.1.6q1.5-1.7 3.1-3.1c.4-.4 1.3-1 1.6-1.4q.3 0 .5-.3a38 38 0 0 1 5.8-3.2L35 38a35 35 0 0 1 6.2-1.4h2q2.3 0 4.3.3c4.5.6 9-4 8.9-8.7 0-5.3-3.5-9.2-8.2-9.8m87.5 38.1c-7.5 4-16 5-23.7 2.7a8 8 0 0 1-4.8-4.4c-.9-2-1-4.8-.2-7a10 10 0 0 1 4.6-5.3 9 9 0 0 1 6.6-.7l1.2.3h3.5q2.1-.2 4.2-1l1.4-.6c2-1.1 4.6-1.4 6.7-.7 2 .6 4 2.3 4.8 4.4.9 2 1.2 4.6.2 6.9q-1.4 3.5-4.5 5.4" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M459 140.8c-74-50.6-244.4-113.6-334 40l28.2 32a210 210 0 0 0-25.7 43.2c-8.8 22-12 46.3-12.6 70.6q-.1 6.9.2 13.8c38.1 6.5 56.1 47.3 60.4 67 0 0 11.3.6 14-1.6 14-7.3 38.9-37.4 26.5-99-6-28-2-50.3 47-85 20.6-14.5 46.3-19.2 70.3-18.7l10 .5c29.2 2.3 54.4 11.5 62.7 18.2 12.8 10.4 19 14.4 20.4 15q-8.4-18.9-19-32.8c7.8-19 21.9-44.5 40.7-57.4q5.2-3.7 10.9-5.8" fill="#220f00"/><path d="M125 180.8c89.6-153.6 260-90.6 334-40-20-13.7-115.5-40.5-159-18.5l.4.7c-83-18-151.1 30.5-175.4 57.8" fill="white" fill-opacity=".2"/><path d="M459 140.8c-30.4 11.2-51 58-57 78-28.5-14-47-15-77-16 4-32.8-17.7-67.8-25-80.5 43.5-22 139 4.8 159 18.5" fill="black" fill-opacity=".1"/><path d="M325.2 201.2c-.8-12-40.7-41.1-81.2-36.4-54.1 6.4-86.5 45-89 50l-1.8-2a210 210 0 0 0-25.7 43.2c-8.8 22-12 46.3-12.6 70.6q-.1 6.9.2 13.8c38.1 6.5 56.1 47.3 60.4 67 0 0 11.3.6 14-1.6 14-7.3 38.9-37.4 26.5-99-6-28-2-50.3 47-85 20.6-14.5 46.3-19.2 70.3-18.7l-8.3-.3zm18.1 2.4c29.2 2.3 54.4 11.5 62.7 18.2 12.8 10.4 19 14.4 20.4 15q-8.4-18.9-19-32.8-3.6 8.6-5.4 14.7v.1a138 138 0 0 0-58.7-15.2" fill="white" fill-opacity=".7"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path d="M64.2 63c101.9-11.2 203 37.8 229.5 60.5 9.6-11.2 25.4-24.7 36.4-26 2.7 6 7 21 8.1 24.5-16 0-33.5 19.8-42.1 33.5-66.9-58-160-58-254.9-58a96 96 0 0 1 23-34.5" fill="#FFC00C"/><path d="M200 108.8a308 308 0 0 0-18.4-3.6L204 79.7q8.1 2.6 15.8 5.4zm46.7 15.7-5.4-2.4L257 101l5.2 2.7zm19.1 9.9-11-6 14.7-21 9.4 5.6zm64.6-11a55 55 0 0 0-19 13l19.2-37.7c1.6 4 3.7 10.7 5.4 16z" fill="#FFD45B"/><ellipse cx="294.4" cy="135.5" rx="8.8" ry="13.5" fill="#FF3C15"/><path d="M294.4 149c4.8 0 8.8-6 8.8-13.5l-.2-2.5a26 26 0 0 1-14 13q2.2 3 5.4 3" fill="#CB1414"/><path d="M290.7 133.5c1.5.6 3.5-1 4.3-3.7.8-2.5.2-5-1.3-5.6s-3.5 1.1-4.3 3.7c-.8 2.5-.2 5 1.3 5.6" fill="#FF7256"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g10" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#b45309"/><stop offset="100%" stop-color="#fde047"/>
</linearGradient>
<clipPath id="c10"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g10)" clip-path="url(#c10)"/>
<g transform="translate(20.00 20.00) scale(0.4167)"><g transform="matrix(.85682 0 0 .85439 52 75.4)"><path d="M264.2 5.4s131.6 20.6 121.6 208c0 .8 15.2 11 16.4 12a77 77 0 0 1 23.5 36.3c13.8 51.4-35.8 104.2-76 118.3q-22.7 7.6-46.7 8.2c-103.2 3-240-62.6-247-190.9q-.3-6.9-.2-13.8a203 203 0 0 1 15.4-70s57.5-142.6 193-108.1" fill="#643d19"/><path d="M389.1 215.5A57 57 0 0 0 386 226a40 40 0 0 0 8.5 30.3 42 42 0 0 0 33 15.7c-3.2-31.7-29-50.7-38.3-56.5m-185.5 83.3c24.5-5.3 39.6-31 33.9-57.5s-30.2-43.8-54.6-38.5-39.6 31-33.9 57.5 30.2 43.7 54.6 38.5" fill="#DC2400" opacity=".1" style="mix-blend-mode:multiply"/><path opacity=".1" d="M323.3 224.6s21-19.8 37-1.2Z" fill="black"/><path d="M128.8 323.6C59.4 358.4-3.7 271.1 16.5 222c12.9-31.3 50.4-33.8 72.2-5.1a185 185 0 0 1 29.1 59" fill="#643d19"/><path opacity=".1" d="M71.6 245.7c9 9 16.1 22.6 25 30-1.7-12.5-4.3-26-10.4-37.3-6.2-11.5-17.5-21-29-20.3-14.3 1-26 17.6-27.5 32.7 1-9 19-15.1 26-14.3 10.8 20.4 15 60.7 15 60.7 9-22.8 5.3-40.7.9-51.5" fill="black"/></g><g transform="matrix(.85472 0 0 .855 222 253.2)"><path d="M155.7 51c3 1.7 24.1-24.8 38.2-16.7 4.7 2.7 8.3 7.3 11 12.3 6 11 8 24.4 5.6 36.8a67 67 0 0 1-29 41.2c-37.3 25.5-67 20.7-106.7 6C43.4 118.9.8 95 9.6 51.9c15.5-76 146-1 146.1-1" fill="#873C41"/><path d="M47.7 46c10.2 6.4 22.5 7 33.6 4.5 2-.5 3.8 1.3 4.3 3.4 5.4 21 36 32 51.5 18.5 1.2-1 3.3-1.3 4.5-.1 8.3 7.8 21.6 10 31.4 4.6 13.8-7.5 13.7-28.6 10-43.3-12.2 3.7-25 18.7-27.3 17.3 0 0-80.1-46-122.3-29.4a31 31 0 0 0 14.3 24.6" fill="white"/><path d="M162.7 127.8c-22-20.1-48.9-31.4-76.2-39.3C63 81.6 36.8 77.2 13 84c11 23.5 39.2 38.2 61.7 46.6 34.8 12.9 61.8 18.2 93 2.2z" fill="#EE7E8B"/><path d="m42 19 7 4 14 7.1a470 470 0 0 0 51.6 20.1l2.2.8c14.4 5 32.2 10.4 51.4 10.4a31 31 0 0 0 15-4.3 1.5 1.5 0 1 1 1.3 2.6 34 34 0 0 1-16.2 4.7c-19.8 0-38-5.6-52.5-10.6l-2.2-.7a487 487 0 0 1-52-20.4l-1.1-.5-13.1-6.7q-5.6-3-9.6-5.6z" fill="#C4C4C4"/><rect x="162.1" y="56" width="12" height="12" rx="2" transform="rotate(3.2 162 56)" fill="#A09E9E"/><rect x="111.5" y="45" width="12" height="12" rx="2" transform="rotate(15 111.5 45)" fill="#A09E9E"/><rect x="59.7" y="24" width="12" height="12" rx="2" transform="rotate(26.4 59.7 24)" fill="#A09E9E"/></g><g transform="matrix(.85472 0 0 .855 261.6 170.8)"><path d="M118 65.2c4 1.8 9.3 3.9 14.6 5.1 1.6 3.1 2.5 6.9 2.5 10.3 0 8-3.7 16.5-13 15.5-7.8-.8-9.7-11-9.7-16 0-6 2.2-12 5.7-15m-78.9-2.4a26 26 0 0 1 7.4 18.2c0 11.8-6.3 21.5-19.3 19.5C18 98 11.7 91.4 11.8 79q0-3 .6-6c12.4-1.5 21.2-6 26.8-10.2" fill="#2A1200"/><path d="M41 18.1A44 44 0 0 0 8.6 42.3c-2 4.2-1 10 3.3 12.7 4 2.4 9.6 1.7 11.7-2.7q1-2.1 2.2-3.9c.2-.4 1-1.2 1.1-1.7l.4-.5a38 38 0 0 1 4.7-4.6l1.6-1a35 35 0 0 1 5.2-2.9l.5-.2 1.9-.6 4.2-.8c4.4-.7 7.5-6.2 6.1-10.8-1.4-5-5.7-7.9-10.5-7.2m100.4 22.7c-3.2-7.8-9.1-14-16.5-17.2-2-.8-4.5-.6-6.5.3-2 1-3.8 3-4.6 5.2a10 10 0 0 0 .1 7 9 9 0 0 0 4.7 4.9l1 .5q1.5 1 2.8 2.2 1.4 1.6 2.6 3.4l.7 1.5a8 8 0 0 0 11.2 4.5q3.2-1.7 4.6-5.2 1.2-3.6-.1-7" fill="#71472D"/></g><g transform="matrix(.85472 0 0 .85667 1.8 -47.6)"><path d="M442 262.5c-25-104-115.5-123-152.5-121.5C125.1 143 88.7 276.2 91 348.5c1-2 3-3.5 3-3.5 13-7 20.5-5 23-5 43.5 2 58.8 53.5 56 75.5 7.9 7.4 18.7.2 23.3-2.9l.2-.1c3.6-2.4 0-13.5 0-20-9-42-7-102-4.5-102 2 0 29.5-2.3 40.5-4-.8-10.4-3.7-40.3 0-45.5-3.2 5.2 10.2 36.8 11.5 43 49.5 5 150.5-13 153.5-14.5 2.4-1.2-1-27.8-3-41 7.6 7.2 13.8 31.7 15 41 9.6-.8 25.7-5 32.5-7" fill="#71472d"/><path d="M302 157c23.3 1.7 77.4 16.2 107 61-11.7-24.3-49.4-70.6-107-61m48 48c7 9 21.2 33.6 22 60 5-10.7 7.6-37.6-22-60m-199 84c4.8-53.6 40-93 57-106-64 20.8-78 89.3-77 121 .8-40 23.7-74 35-86-10.4 18.4-14.3 55-15 71" fill="black" fill-opacity=".2"/></g><g transform="matrix(.85472 0 0 .85667 85.6 71)"><path fill-rule="evenodd" clip-rule="evenodd" d="M75 207a359 359 0 0 1 59.6 32l-2.8 4a366 366 0 0 0-58.6-31.3c-11.2-4.5-22.1-8-31.9-9.5a40 40 0 0 0-24 2l-2.2-4.4a45 45 0 0 1 27-2.5c10.3 1.6 21.6 5.2 33 9.8m57.1 122.4 9-2.3 1.2 4.8-8.5 2.2q-10.8 3.2-20.7 4.3c-9.5.8-19.3-.7-32.4-6.6l2-4.6c12.5 5.7 21.5 7 30 6.2 6.2-.5 12.2-2.1 19.4-4" fill="white"/><path d="M302.7 214a665 665 0 0 0-169.5 23.5c14 24 19 59.5 5 95.5 46 26 153.8 66.6 218.4 21a123 123 0 0 0 31.1-57c9.6-39.6-23-68.8-40.5-78.5-9.6-1.6-28.5-4.4-44.5-4.5" fill="#F7F7F7"/><path d="M127.7 241.5c2.4-4 13-7.2 19-7.5 3 3 9.2 17.1 10 49.5 1 40.5-7 52-8 54.5-.7 1.9-14-5-17-6.5s-3-4.5 0-11c6.5-14 3-54.5 0-63.5-2.4-7.2-3-12.8-4-15.5m197.5 5.5c-35.2-4.8-106 18-137 30 114.4-28.8 181.7-12 201 0-6.6-8-28.8-25.2-64-30m-97 59c26 12.3 91.4 31.2 145 8-25 20-89 46.4-145-8" fill="#EDEDED"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#374151"/><stop offset="100%" stop-color="#9ca3af"/>
</linearGradient>
<clipPath id="c1"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g1)" clip-path="url(#c1)"/>
<g transform="translate(20.00 20.00) scale(1.6667)"><rect width="120" height="120" fill="#b18911"/><mask id="botttsNeutral-a" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h120v120H0z"/></mask><g mask="url(#botttsNeutral-a)"><g transform="translate(-48.5 -9.89)"><path fill-rule="evenodd" clip-rule="evenodd" d="M33.88 128.1c4.36-30.7 13.06-4.57 33.99-9.98 9.24-2.4 1.77-12.82 8.67-17.35 5.82-3.82 13.76-.74 20.43-3.21 9.63-3.57 9.5-10.82 18.48-11.9 8.48-1.03 17.37 2.34 25.93 1.08 16.15-2.4 9.53-34.03 21.64-39.61 12.1-5.6 32.05 4.06 34.66 19.87 2.54 15.42-14.46 72.78-14.46 72.78H33.88" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M78.93 128c37.67 0 41.6-14.75 60.13-21.5s34.79 10.71 41.02-6.27 6.2 35.95 6.2 35.95H78.93z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M41 10.87C52.73 9.75 48.96 22.8 53.6 22.8c1.85 0 4.03-8.46 7.38-8.46 2.46 0 2 5.5 7.33 4.91 5-.54 4.07-4 6.6-4 5.44 0 3.38 20.2 7.73 20.2s5.73-14.8 7.65-20.64c2.43-7.4 16.97-11.56 6.6-14.5H40.71" fill="black" fill-opacity=".4"/></g></g><g transform="translate(26 72)"><rect y="1" width="68" height="22" rx="5" fill="black" fill-opacity=".2"/><rect x="4" y="5" width="60" height="14" rx="2" fill="white"/><path fill="black" fill-opacity=".1" d="M14 5h4v14h-4zm24 0h4v14h-4zM26 5h4v14h-4zm24 0h4v14h-4z"/></g><g transform="translate(8 20)"><rect y="12" width="104" height="32" rx="16" fill="black" fill-opacity=".8"/><rect x="24" y="22" width="10" height="12" rx="2" fill="#F4F4F4"/><rect x="70" y="22" width="10" height="12" rx="2" fill="#F4F4F4"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g3" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#374151"/><stop offset="100%" stop-color="#9ca3af"/>
</linearGradient>
<clipPath id="c3"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g3)" clip-path="url(#c3)"/>
<g transform="translate(20.00 20.00) scale(1.6667)"><rect width="120" height="120" fill="#7681f5"/><mask id="botttsNeutral-a" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h120v120H0z"/></mask><g mask="url(#botttsNeutral-a)"><g transform="translate(-48.5 -9.89)"><path fill-rule="evenodd" clip-rule="evenodd" d="M33.88 128.1c4.36-30.7 13.06-4.57 33.99-9.98 9.24-2.4 1.77-12.82 8.67-17.35 5.82-3.82 13.76-.74 20.43-3.21 9.63-3.57 9.5-10.82 18.48-11.9 8.48-1.03 17.37 2.34 25.93 1.08 16.15-2.4 9.53-34.03 21.64-39.61 12.1-5.6 32.05 4.06 34.66 19.87 2.54 15.42-14.46 72.78-14.46 72.78H33.88" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M78.93 128c37.67 0 41.6-14.75 60.13-21.5s34.79 10.71 41.02-6.27 6.2 35.95 6.2 35.95H78.93z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M41 10.87C52.73 9.75 48.96 22.8 53.6 22.8c1.85 0 4.03-8.46 7.38-8.46 2.46 0 2 5.5 7.33 4.91 5-.54 4.07-4 6.6-4 5.44 0 3.38 20.2 7.73 20.2s5.73-14.8 7.65-20.64c2.43-7.4 16.97-11.56 6.6-14.5H40.71" fill="black" fill-opacity=".4"/></g></g><g transform="translate(26 72)"><rect x="24" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/><rect x="10" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/><rect x="38" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/><rect x="52" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/></g><g transform="translate(8 20)"><circle cx="28" cy="24" r="18" fill="black" fill-opacity=".2"/><circle cx="74" cy="24" r="18" fill="black" fill-opacity=".2"/><circle cx="28" cy="24" r="15" fill="#F1EEDA"/><circle cx="74" cy="24" r="15" fill="#F1EEDA"/><rect x="26" y="15" width="10" height="10" rx="2" fill="black" fill-opacity=".8"/><rect x="74" y="15" width="10" height="10" rx="2" fill="black" fill-opacity=".8"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g4" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#374151"/><stop offset="100%" stop-color="#9ca3af"/>
</linearGradient>
<clipPath id="c4"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g4)" clip-path="url(#c4)"/>
<g transform="translate(20.00 20.00) scale(1.6667)"><rect width="120" height="120" fill="#448ef4"/><mask id="botttsNeutral-a" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h120v120H0z"/></mask><g mask="url(#botttsNeutral-a)"><g transform="translate(-48.5 -9.89)"><path d="M66 36h49.42a5 5 0 1 1 0 4H62V6h4zm-8-12h-4V6h4z" fill="black" fill-opacity=".1"/><path d="M163 31a5 5 0 0 1 2 9.58V66h19v4h-23V40.58a5 5 0 0 1 2-9.58" fill="white" fill-opacity=".2"/><path d="M157 74h27v4h-31V52h4zm-85 26a5 5 0 0 0-2 9.58V135H51v4h23v-29.42a5 5 0 0 0-2-9.58" fill="black" fill-opacity=".2"/></g></g><g transform="translate(26 72)"><rect x="12" y="4" width="44" height="4" rx="2" fill="black" fill-opacity=".8"/></g><g transform="translate(8 20)"><circle cx="24" cy="30" r="6" fill="white"/><circle cx="80" cy="30" r="6" fill="white"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g5" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#374151"/><stop offset="100%" stop-color="#9ca3af"/>
</linearGradient>
<clipPath id="c5"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g5)" clip-path="url(#c5)"/>
<g transform="translate(20.00 20.00) scale(1.6667)"><rect width="120" height="120" fill="#b46bd6"/><mask id="botttsNeutral-a" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h120v120H0z"/></mask><g mask="url(#botttsNeutral-a)"><g transform="translate(-48.5 -9.89)"><path d="M72.47 40.03q-.15-.02-.26-.12c-.1-.07-.15-.12-.14.14.01.5.67-.05.4-.02M52.33 54.92s.2-.56.03-.74q-.26.26-.03.74M50.5 58c.06 0 .57-.28.48-.43-.12-.18-.49.34-.49.42m15.4 7.15c-.12-.11-.52-.45-.27-.5.14-.03.5.36.32.54v-.01h-.01zm3.44 49.32c.13-.02.66-.36.34-.45-.21.01-.6.53-.34.51m26.3-12.14c-.05.05-.2.1-.34.12-.15.03-.26.06-.16.1.19.05.84.15.5-.23M48.1 77.63l.06-.1c1.02 0-.68.19-.07.1m75.45 38.3c.07-.03.08-.03.1-.12q-.1 0-.1.1m-51.37 7.87q.1 0 .1-.11-.1 0-.1.1M52.01 55.75c.08-.02.09-.03.1-.12-.08.02-.09.03-.1.12m56.84-1q-.01.11.1.1c-.02-.09-.02-.1-.1-.1m41.43 28.16c-.08.02-.09.03-.1.12.08-.02.09-.03.1-.12" fill="black" fill-opacity=".2"/></g></g><g transform="translate(26 72)"><rect width="68" height="24" rx="5" fill="black" fill-opacity=".2"/><rect x="4" y="4" width="60" height="16" rx="2" fill="black" fill-opacity=".8"/><path d="M5 13h11l2-4 3 7 4-8 2 9 3-11 3 10 3-3h15l3-4 2 7 3-3h4" stroke="#4EFAC9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(8 20)"><rect y="11" width="104" height="34" rx="17" fill="black" fill-opacity=".8"/><circle cx="29" cy="28" r="13" fill="#F1EEDA"/><circle cx="75" cy="28" r="13" fill="#F1EEDA"/><rect x="24" y="23" width="10" height="10" rx="2" fill="black" fill-opacity=".8"/><rect x="70" y="23" width="10" height="10" rx="2" fill="black" fill-opacity=".8"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g6" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#374151"/><stop offset="100%" stop-color="#9ca3af"/>
</linearGradient>
<clipPath id="c6"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g6)" clip-path="url(#c6)"/>
<g transform="translate(20.00 20.00) scale(1.6667)"><rect width="120" height="120" fill="#1499da"/><mask id="botttsNeutral-a" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h120v120H0z"/></mask><g mask="url(#botttsNeutral-a)"><g transform="translate(-48.5 -9.89)"><path d="M51.4 123.8a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.32a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.31a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m13.1-.17a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m13 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-20-6a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m24 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-49-.14a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m25.2 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.31a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.31a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.3a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.32a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2M51.4 73.3a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2M57.7 67a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2" fill="black" fill-opacity=".2"/><path d="M154 62.8a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-18.9-6.32a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m25.2 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.3a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-18.9-6.32a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.31a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.3a2.1 2.1 0 1 1 0 4.19 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.3a2.1 2.1 0 1 1 0 4.19 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.3a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m-31.5-6.32a2.1 2.1 0 1 1 0 4.23 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2M147.7 6a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2m12.6 0a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2" fill="black" fill-opacity=".1"/></g></g><g transform="translate(26 72)"><rect x="12" y="4" width="44" height="4" rx="2" fill="black" fill-opacity=".8"/></g><g transform="translate(8 20)"><rect x="7" y="16" width="91" height="16" rx="4" fill="black" fill-opacity=".8"/><mask id="eyesRobocop-a" style="mask-type:luminance"><rect x="7" y="16" width="91" height="16" rx="4" fill="white"/></mask><g mask="url(#eyesRobocop-a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M76 7h18L82 37H64zM52 7h9L49 37h-9z" fill="white" fill-opacity=".8"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g7" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#374151"/><stop offset="100%" stop-color="#9ca3af"/>
</linearGradient>
<clipPath id="c7"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g7)" clip-path="url(#c7)"/>
<g transform="translate(20.00 20.00) scale(1.6667)"><rect width="120" height="120" fill="#40a843"/><mask id="botttsNeutral-a" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h120v120H0z"/></mask><g mask="url(#botttsNeutral-a)"><g transform="translate(-48.5 -9.89)"><path fill-rule="evenodd" clip-rule="evenodd" d="M33.88 128.1c4.36-30.7 13.06-4.57 33.99-9.98 9.24-2.4 1.77-12.82 8.67-17.35 5.82-3.82 13.76-.74 20.43-3.21 9.63-3.57 9.5-10.82 18.48-11.9 8.48-1.03 17.37 2.34 25.93 1.08 16.15-2.4 9.53-34.03 21.64-39.61 12.1-5.6 32.05 4.06 34.66 19.87 2.54 15.42-14.46 72.78-14.46 72.78H33.88" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M78.93 128c37.67 0 41.6-14.75 60.13-21.5s34.79 10.71 41.02-6.27 6.2 35.95 6.2 35.95H78.93z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M41 10.87C52.73 9.75 48.96 22.8 53.6 22.8c1.85 0 4.03-8.46 7.38-8.46 2.46 0 2 5.5 7.33 4.91 5-.54 4.07-4 6.6-4 5.44 0 3.38 20.2 7.73 20.2s5.73-14.8 7.65-20.64c2.43-7.4 16.97-11.56 6.6-14.5H40.71" fill="black" fill-opacity=".4"/></g></g><g transform="translate(26 72)"><rect y="1" width="68" height="22" rx="5" fill="black" fill-opacity=".2"/><rect x="4" y="5" width="60" height="14" rx="2" fill="white"/><path fill="black" fill-opacity=".1" d="M14 5h4v14h-4zm24 0h4v14h-4zM26 5h4v14h-4zm24 0h4v14h-4z"/></g><g transform="translate(8 20)"><circle cx="21" cy="30" r="15" fill="white" fill-opacity=".1"/><circle cx="83" cy="30" r="15" fill="white" fill-opacity=".1"/><circle cx="21" cy="30" r="12" fill="white" fill-opacity=".1"/><circle cx="83" cy="30" r="12" fill="white" fill-opacity=".1"/><circle cx="21" cy="30" r="6" fill="white" fill-opacity=".8"/><circle cx="83" cy="30" r="6" fill="white" fill-opacity=".8"/><circle cx="21" cy="30" r="3" fill="white" fill-opacity=".8"/><circle cx="83" cy="30" r="3" fill="white" fill-opacity=".8"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g9" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#374151"/><stop offset="100%" stop-color="#9ca3af"/>
</linearGradient>
<clipPath id="c9"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g9)" clip-path="url(#c9)"/>
<g transform="translate(20.00 20.00) scale(1.6667)"><rect width="120" height="120" fill="#e45e4d"/><mask id="botttsNeutral-a" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h120v120H0z"/></mask><g mask="url(#botttsNeutral-a)"><g transform="translate(-48.5 -9.89)"><path fill-rule="evenodd" clip-rule="evenodd" d="M33.88 128.1c4.36-30.7 13.06-4.57 33.99-9.98 9.24-2.4 1.77-12.82 8.67-17.35 5.82-3.82 13.76-.74 20.43-3.21 9.63-3.57 9.5-10.82 18.48-11.9 8.48-1.03 17.37 2.34 25.93 1.08 16.15-2.4 9.53-34.03 21.64-39.61 12.1-5.6 32.05 4.06 34.66 19.87 2.54 15.42-14.46 72.78-14.46 72.78H33.88" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M78.93 128c37.67 0 41.6-14.75 60.13-21.5s34.79 10.71 41.02-6.27 6.2 35.95 6.2 35.95H78.93z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M41 10.87C52.73 9.75 48.96 22.8 53.6 22.8c1.85 0 4.03-8.46 7.38-8.46 2.46 0 2 5.5 7.33 4.91 5-.54 4.07-4 6.6-4 5.44 0 3.38 20.2 7.73 20.2s5.73-14.8 7.65-20.64c2.43-7.4 16.97-11.56 6.6-14.5H40.71" fill="black" fill-opacity=".4"/></g></g><g transform="translate(26 72)"><rect width="68" height="24" rx="5" fill="black" fill-opacity=".2"/><rect x="4" y="4" width="60" height="16" rx="2" fill="black" fill-opacity=".8"/><path d="M5 13h11l2-4 3 7 4-8 2 9 3-11 3 10 3-3h15l3-4 2 7 3-3h4" stroke="#4EFAC9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(8 20)"><path d="M29.27 9.68c-2.55.13-4.96 2.24-6.25 4.15-1.48-1.76-4.1-3.6-6.65-3.47-5.48.28-8.85 3.8-8.63 8.1.3 5.72 4.88 8.89 9.7 12.24 1.7 1.15 5 4.15 5.42 4.82s2.14.6 2.58-.13a38 38 0 0 1 4.9-5.36c4.43-3.84 8.66-7.47 8.36-13.2-.23-4.3-3.95-7.44-9.43-7.15m58.36.68c-2.55-.14-5.17 1.7-6.65 3.47-1.3-1.9-3.7-4.02-6.25-4.15-5.48-.29-9.2 2.86-9.43 7.16-.3 5.72 3.93 9.35 8.36 13.2 1.6 1.31 4.55 4.63 4.9 5.35.35.7 2.06.82 2.58.13a37 37 0 0 1 5.42-4.82c4.8-3.35 9.4-6.52 9.7-12.24.22-4.3-3.15-7.82-8.63-8.1" fill="#FF5353" fill-opacity=".8"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g2" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c2"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g2)" clip-path="url(#c2)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><mask id="sidesSquareAssymetric-a" style="mask-type:luminance"><rect x="4" y="31" width="36" height="30" rx="4" fill="white"/><rect x="14" y="15" width="26" height="30" rx="4" fill="white"/><rect x="133" y="23" width="36" height="30" rx="4" fill="white"/><rect x="128" y="8" width="36" height="60" rx="4" fill="white"/></mask><g mask="url(#sidesSquareAssymetric-a)"><path d="M-6 0h180v76H-6z" fill="#d95c9a"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/><path fill="black" fill-opacity=".1" d="M-6 47h180v29H-6z"/><circle cx="155" cy="20" r="5" fill="white" fill-opacity=".6"/><circle cx="155" cy="36" r="5" fill="white" fill-opacity=".6"/></g></g><g transform="translate(49 -.6)"><mask id="topBulb01-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M24 16.6a16 16 0 0 1 16-16h4a16 16 0 0 1 16 16v16a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8z" fill="white"/><rect x="14" y="40.59" width="56" height="12" rx="1" fill="white"/></mask><g mask="url(#topBulb01-a)"><path d="M-8 .6H92v52H-8z" fill="#d95c9a"/><path d="M-8 .6H92v52H-8z" fill="white" fill-opacity=".3"/><path fill="white" fill-opacity=".4" d="M12-2.41h60v43H12z"/><path d="M41 4.1c4.93 0 9.37 2.12 12.44 5.51" stroke="white" stroke-width="2" stroke-linecap="round"/><path d="M54.14 20.42 44 30.57v10.02h-4V30.42l-10-10 2.83-2.83 9 9h.48l9.02-9z" fill="white" fill-opacity=".8"/></g></g><g transform="translate(25 44)"><mask id="headSquare03-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 18A18 18 0 0 1 18 0h94a18 18 0 0 1 18 18v27.15a40 40 0 0 1-2.28 13.31L110.24 108a18 18 0 0 1-16.98 12H36.74a18 18 0 0 1-16.98-12L2.28 58.45A40 40 0 0 1 0 45.15z" fill="white"/></mask><g mask="url(#headSquare03-a)"><path d="M-2-2h134v124H-2z" fill="#d95c9a"/><g transform="translate(-44 -7)"><path fill-rule="evenodd" clip-rule="evenodd" d="M143 130.65c-3.19-17.34-9.55-2.58-24.85-5.63-6.76-1.35-1.3-7.25-6.34-9.8-4.26-2.17-10.06-.43-14.94-1.82-7.04-2.02-6.95-6.12-13.5-6.73-6.2-.58-12.7 1.33-18.97.61-11.8-1.35-6.96-19.22-15.82-22.38-8.85-3.15-23.43 2.3-25.34 11.23-1.86 8.71 10.57 41.12 10.57 41.12H143" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M179 122.77c-51.24 0-56.58-25.8-81.8-37.61C72 73.34 49.9 103.9 41.44 74.2S33 137.07 33 137.07h146z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M184.58 9.18c-11.94-.98-5.04 10.8-9.75 10.8-6.97 0-2.1-7.97-10.58-7.78-2.22.06-4.95 1.94-5.12 4.5-.36 5.52 3.72 15.03-1.83 15.3-5.18.25-1.1-9.64-1.65-16.24-.3-3.77-4.18-6.75-6.97-6.75-7.57 0-1.68 15.7-7.13 15.7s-1.21-9.05-5.08-13.84c-4.5-5.57-19.3-8.3-8.75-10.87h57.15" fill="black" fill-opacity=".4"/></g></g></g><g transform="translate(56 128)"><rect x="12" y="4" width="44" height="4" rx="2" fill="black" fill-opacity=".8"/></g><g transform="translate(38 76)"><circle cx="21" cy="30" r="15" fill="white" fill-opacity=".1"/><circle cx="83" cy="30" r="15" fill="white" fill-opacity=".1"/><circle cx="21" cy="30" r="12" fill="white" fill-opacity=".1"/><circle cx="83" cy="30" r="12" fill="white" fill-opacity=".1"/><circle cx="21" cy="30" r="6" fill="white" fill-opacity=".8"/><circle cx="83" cy="30" r="6" fill="white" fill-opacity=".8"/><circle cx="21" cy="30" r="3" fill="white" fill-opacity=".8"/><circle cx="83" cy="30" r="3" fill="white" fill-opacity=".8"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g3" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c3"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g3)" clip-path="url(#c3)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><path d="M32 12c-2.95 11.7-19.9 6.67-23.37 18-3.46 11.35 8.03 20 17.53 20" stroke="#2A3544" stroke-width="6" opacity=".9"/><path d="M144 55c8.4 3.49 20.1-7.6 16-16.5s-16-6.7-16-19.3" stroke="#2A3544" stroke-width="4" opacity=".9"/><mask id="sidesCables01-a" style="mask-type:luminance"><rect x="15" y="35" width="16" height="22" rx="2" fill="white"/><rect x="130" y="42" width="23" height="22" rx="2" fill="white"/><rect x="130" y="6" width="23" height="18" rx="2" fill="white"/></mask><g mask="url(#sidesCables01-a)"><path d="M-6 0h180v76H-6z" fill="#7681f5"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/></g></g><g transform="translate(49 -.6)"><g filter="url(#topGlowingBulb01-a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M24 24.6a16 16 0 0 1 16-16h4a16 16 0 0 1 16 16v8a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8z" fill="white" fill-opacity=".3"/></g><path d="M41 12.1c4.93 0 9.37 2.12 12.44 5.51" stroke="white" stroke-width="2" stroke-linecap="round"/><path d="M54.14 23.42 44 33.57v7.02h-4v-7.17l-10-10 2.83-2.83 9 9h.48l9.02-9z" fill="white" fill-opacity=".8"/><rect x="14" y="40.59" width="56" height="12" rx="1" fill="#48494B"/><defs><filter id="topGlowingBulb01-a" x="16" y=".59" width="52" height="48" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="r0"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="r1"/><feOffset/><feGaussianBlur stdDeviation="4"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0"/><feBlend in2="r0" result="r2"/><feBlend in="SourceGraphic" in2="r2" result="r3"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="r1"/><feOffset/><feGaussianBlur stdDeviation="2"/><feComposite in2="r1" operator="arithmetic" k2="-1" k3="1"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0"/><feBlend in2="r3" result="r4"/></filter></defs></g><g transform="translate(25 44)"><mask id="headRound01-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M66 0c58.35 0 64 40.69 64 78 0 33.32-25.47 42-64 42-37.46 0-66-8.69-66-42C0 40.69 7.65 0 66 0" fill="white"/></mask><g mask="url(#headRound01-a)"><path d="M-4-2h138v124H-4z" fill="#7681f5"/><g transform="translate(-44 -7)"><path d="M66 36h49.42a5 5 0 1 1 0 4H62V6h4zm-8-12h-4V6h4z" fill="black" fill-opacity=".1"/><path d="M163 31a5 5 0 0 1 2 9.58V66h19v4h-23V40.58a5 5 0 0 1 2-9.58" fill="white" fill-opacity=".2"/><path d="M157 74h27v4h-31V52h4zm-85 26a5 5 0 0 0-2 9.58V135H51v4h23v-29.42a5 5 0 0 0-2-9.58" fill="black" fill-opacity=".2"/></g></g></g><g transform="translate(56 128)"><rect x="24" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/><rect x="10" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/><rect x="38" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/><rect x="52" y="6" width="6" height="14" rx="2" fill="black" fill-opacity=".6"/></g><g transform="translate(38 76)"><circle cx="24" cy="30" r="6" fill="white"/><circle cx="80" cy="30" r="6" fill="white"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g4" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c4"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g4)" clip-path="url(#c4)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><path d="M32 12c-2.95 11.7-19.9 6.67-23.37 18-3.46 11.35 8.03 20 17.53 20" stroke="#2A3544" stroke-width="6" opacity=".9"/><path d="M144 55c8.4 3.49 20.1-7.6 16-16.5s-16-6.7-16-19.3" stroke="#2A3544" stroke-width="4" opacity=".9"/><mask id="sidesCables01-a" style="mask-type:luminance"><rect x="15" y="35" width="16" height="22" rx="2" fill="white"/><rect x="130" y="42" width="23" height="22" rx="2" fill="white"/><rect x="130" y="6" width="23" height="18" rx="2" fill="white"/></mask><g mask="url(#sidesCables01-a)"><path d="M-6 0h180v76H-6z" fill="#448ef4"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/></g></g><g transform="translate(49 -.6)"><mask id="topRadar-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M35.8.7A20 20 0 1 0 64.08 29" fill="white"/><circle cx="59.13" cy="5.66" r="4" transform="rotate(45 59.13 5.66)" fill="white"/><path fill="white" d="m56.307 7.077 1.414 1.414-18.384 18.385-1.415-1.414zM39.94 28.71h4v24h-4z"/></mask><g mask="url(#topRadar-a)"><path d="M-8 .6H92v52H-8z" fill="#448ef4"/><path d="M-8 .6H92v52H-8z" fill="white" fill-opacity=".3"/><path fill-rule="evenodd" clip-rule="evenodd" d="M35.8.7A20 20 0 1 0 64.08 29" fill="white" fill-opacity=".2"/><circle cx="59.13" cy="8" r="5.66" transform="rotate(45 59.13 8)" fill="white" fill-opacity=".8"/></g></g><g transform="translate(25 44)"><mask id="headSquare02-a" style="mask-type:luminance"><path d="M0 12A12 12 0 0 1 12 0h106a12 12 0 0 1 12 12v70a38 38 0 0 1-38 38H38A38 38 0 0 1 0 82z" fill="white"/></mask><g mask="url(#headSquare02-a)"><path d="M-2-2h134v124H-2z" fill="#448ef4"/><g transform="translate(-44 -7)"><path fill-rule="evenodd" clip-rule="evenodd" d="M143 130.65c-3.19-17.34-9.55-2.58-24.85-5.63-6.76-1.35-1.3-7.25-6.34-9.8-4.26-2.17-10.06-.43-14.94-1.82-7.04-2.02-6.95-6.12-13.5-6.73-6.2-.58-12.7 1.33-18.97.61-11.8-1.35-6.96-19.22-15.82-22.38-8.85-3.15-23.43 2.3-25.34 11.23-1.86 8.71 10.57 41.12 10.57 41.12H143" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M179 122.77c-51.24 0-56.58-25.8-81.8-37.61C72 73.34 49.9 103.9 41.44 74.2S33 137.07 33 137.07h146z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M184.58 9.18c-11.94-.98-5.04 10.8-9.75 10.8-6.97 0-2.1-7.97-10.58-7.78-2.22.06-4.95 1.94-5.12 4.5-.36 5.52 3.72 15.03-1.83 15.3-5.18.25-1.1-9.64-1.65-16.24-.3-3.77-4.18-6.75-6.97-6.75-7.57 0-1.68 15.7-7.13 15.7s-1.21-9.05-5.08-13.84c-4.5-5.57-19.3-8.3-8.75-10.87h57.15" fill="black" fill-opacity=".4"/></g></g></g><g transform="translate(56 128)"><path fill-rule="evenodd" clip-rule="evenodd" d="M14 6.22C14 17.78 20.47 24 34 24c13.52 0 20-6.34 20-17.78C54 5.5 53.17 4 51 4H17c-2.05 0-3 1.38-3 2.22" fill="black" fill-opacity=".8"/><mask id="mouthSmile02-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M14 6.22C14 17.78 20.47 24 34 24c13.52 0 20-6.34 20-17.78C54 5.5 53.17 4 51 4H17c-2.05 0-3 1.38-3 2.22" fill="white"/></mask><g mask="url(#mouthSmile02-a)"><rect x="26" y="-2" width="16" height="14" rx="2" fill="white"/></g></g><g transform="translate(38 76)"><path d="M29.27 9.68c-2.55.13-4.96 2.24-6.25 4.15-1.48-1.76-4.1-3.6-6.65-3.47-5.48.28-8.85 3.8-8.63 8.1.3 5.72 4.88 8.89 9.7 12.24 1.7 1.15 5 4.15 5.42 4.82s2.14.6 2.58-.13a38 38 0 0 1 4.9-5.36c4.43-3.84 8.66-7.47 8.36-13.2-.23-4.3-3.95-7.44-9.43-7.15m58.36.68c-2.55-.14-5.17 1.7-6.65 3.47-1.3-1.9-3.7-4.02-6.25-4.15-5.48-.29-9.2 2.86-9.43 7.16-.3 5.72 3.93 9.35 8.36 13.2 1.6 1.31 4.55 4.63 4.9 5.35.35.7 2.06.82 2.58.13a37 37 0 0 1 5.42-4.82c4.8-3.35 9.4-6.52 9.7-12.24.22-4.3-3.15-7.82-8.63-8.1" fill="#FF5353" fill-opacity=".8"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g5" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c5"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g5)" clip-path="url(#c5)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><mask id="sidesSquareAssymetric-a" style="mask-type:luminance"><rect x="4" y="31" width="36" height="30" rx="4" fill="white"/><rect x="14" y="15" width="26" height="30" rx="4" fill="white"/><rect x="133" y="23" width="36" height="30" rx="4" fill="white"/><rect x="128" y="8" width="36" height="60" rx="4" fill="white"/></mask><g mask="url(#sidesSquareAssymetric-a)"><path d="M-6 0h180v76H-6z" fill="#b46bd6"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/><path fill="black" fill-opacity=".1" d="M-6 47h180v29H-6z"/><circle cx="155" cy="20" r="5" fill="white" fill-opacity=".6"/><circle cx="155" cy="36" r="5" fill="white" fill-opacity=".6"/></g></g><g transform="translate(49 -.6)"><mask id="topAntennaCrooked-a" style="mask-type:luminance"><path d="M47.54 34.98 43 45.59h-3.74l4.92-10.43-6.05-10.43 3.22-11.84 2.9.8-2.9 10.6z" fill="white"/><path fill="white" d="M30 39.59h24v13H30z"/></mask><g mask="url(#topAntennaCrooked-a)"><path d="M-8 .6H92v52H-8z" fill="#b46bd6"/><path d="M-8 6.6H92v52H-8z" fill="white" fill-opacity=".3"/><path fill="white" fill-opacity=".2" d="M30 39.59h24v13H30z"/></g><circle cx="42" cy="8.59" r="8" fill="#FFE65C"/><circle cx="45" cy="5.59" r="3" fill="white"/></g><g transform="translate(25 44)"><mask id="headSquare03-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 18A18 18 0 0 1 18 0h94a18 18 0 0 1 18 18v27.15a40 40 0 0 1-2.28 13.31L110.24 108a18 18 0 0 1-16.98 12H36.74a18 18 0 0 1-16.98-12L2.28 58.45A40 40 0 0 1 0 45.15z" fill="white"/></mask><g mask="url(#headSquare03-a)"><path d="M-2-2h134v124H-2z" fill="#b46bd6"/><g transform="translate(-44 -7)"><path fill-rule="evenodd" clip-rule="evenodd" d="M143 130.65c-3.19-17.34-9.55-2.58-24.85-5.63-6.76-1.35-1.3-7.25-6.34-9.8-4.26-2.17-10.06-.43-14.94-1.82-7.04-2.02-6.95-6.12-13.5-6.73-6.2-.58-12.7 1.33-18.97.61-11.8-1.35-6.96-19.22-15.82-22.38-8.85-3.15-23.43 2.3-25.34 11.23-1.86 8.71 10.57 41.12 10.57 41.12H143" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M179 122.77c-51.24 0-56.58-25.8-81.8-37.61C72 73.34 49.9 103.9 41.44 74.2S33 137.07 33 137.07h146z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M184.58 9.18c-11.94-.98-5.04 10.8-9.75 10.8-6.97 0-2.1-7.97-10.58-7.78-2.22.06-4.95 1.94-5.12 4.5-.36 5.52 3.72 15.03-1.83 15.3-5.18.25-1.1-9.64-1.65-16.24-.3-3.77-4.18-6.75-6.97-6.75-7.57 0-1.68 15.7-7.13 15.7s-1.21-9.05-5.08-13.84c-4.5-5.57-19.3-8.3-8.75-10.87h57.15" fill="black" fill-opacity=".4"/></g></g></g><g transform="translate(56 128)"><rect x="20" y="2" width="27" height="8" rx="4" fill="black" fill-opacity=".8"/></g><g transform="translate(38 76)"><path fill-rule="evenodd" clip-rule="evenodd" d="M53 0c34.75 0 49 17.47 49 31S82.4 48 53 48C23.95 48 2 44.53 2 31S17.11 0 53 0" fill="black" fill-opacity=".8"/><path fill-rule="evenodd" clip-rule="evenodd" d="M28.82 34.65C22.29 33.3 17.58 28.31 18.3 23.5c.72-4.78 6.6-7.57 13.12-6.22 6.53 1.36 11.24 6.35 10.52 11.15s-6.6 7.59-13.12 6.23m46.6 0C68.9 36 63.02 33.2 62.3 28.4s4-9.8 10.52-11.15 12.4 1.44 13.12 6.24-4 9.8-10.52 11.14" fill="#25A6F5"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g6" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c6"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g6)" clip-path="url(#c6)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><mask id="sidesAntenna02-a" style="mask-type:luminance"><rect x="4" y="28" width="36" height="32" rx="4" fill="white"/><path fill="white" d="M154 9h2v20h-2z"/><rect x="128" y="28" width="36" height="32" rx="4" fill="white"/></mask><g mask="url(#sidesAntenna02-a)"><path d="M-6 0h180v76H-6z" fill="#15a876"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/><path fill="black" fill-opacity=".1" d="M-6 38h180v38H-6z"/></g><path fill="white" fill-opacity=".4" d="M154 8h2v20h-2z"/><circle cx="155" cy="5" r="4" fill="#E1E6E8"/><circle cx="156" cy="4" r="2" fill="white"/></g><g transform="translate(49 -.6)"><g filter="url(#topGlowingBulb01-a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M24 24.6a16 16 0 0 1 16-16h4a16 16 0 0 1 16 16v8a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8z" fill="white" fill-opacity=".3"/></g><path d="M41 12.1c4.93 0 9.37 2.12 12.44 5.51" stroke="white" stroke-width="2" stroke-linecap="round"/><path d="M54.14 23.42 44 33.57v7.02h-4v-7.17l-10-10 2.83-2.83 9 9h.48l9.02-9z" fill="white" fill-opacity=".8"/><rect x="14" y="40.59" width="56" height="12" rx="1" fill="#48494B"/><defs><filter id="topGlowingBulb01-a" x="16" y=".59" width="52" height="48" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"><feFlood flood-opacity="0" result="r0"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="r1"/><feOffset/><feGaussianBlur stdDeviation="4"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0"/><feBlend in2="r0" result="r2"/><feBlend in="SourceGraphic" in2="r2" result="r3"/><feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="r1"/><feOffset/><feGaussianBlur stdDeviation="2"/><feComposite in2="r1" operator="arithmetic" k2="-1" k3="1"/><feColorMatrix values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.5 0"/><feBlend in2="r3" result="r4"/></filter></defs></g><g transform="translate(25 44)"><mask id="headSquare03-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 18A18 18 0 0 1 18 0h94a18 18 0 0 1 18 18v27.15a40 40 0 0 1-2.28 13.31L110.24 108a18 18 0 0 1-16.98 12H36.74a18 18 0 0 1-16.98-12L2.28 58.45A40 40 0 0 1 0 45.15z" fill="white"/></mask><g mask="url(#headSquare03-a)"><path d="M-2-2h134v124H-2z" fill="#15a876"/><g transform="translate(-44 -7)"><path fill-rule="evenodd" clip-rule="evenodd" d="M143 130.65c-3.19-17.34-9.55-2.58-24.85-5.63-6.76-1.35-1.3-7.25-6.34-9.8-4.26-2.17-10.06-.43-14.94-1.82-7.04-2.02-6.95-6.12-13.5-6.73-6.2-.58-12.7 1.33-18.97.61-11.8-1.35-6.96-19.22-15.82-22.38-8.85-3.15-23.43 2.3-25.34 11.23-1.86 8.71 10.57 41.12 10.57 41.12H143" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M179 122.77c-51.24 0-56.58-25.8-81.8-37.61C72 73.34 49.9 103.9 41.44 74.2S33 137.07 33 137.07h146z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M184.58 9.18c-11.94-.98-5.04 10.8-9.75 10.8-6.97 0-2.1-7.97-10.58-7.78-2.22.06-4.95 1.94-5.12 4.5-.36 5.52 3.72 15.03-1.83 15.3-5.18.25-1.1-9.64-1.65-16.24-.3-3.77-4.18-6.75-6.97-6.75-7.57 0-1.68 15.7-7.13 15.7s-1.21-9.05-5.08-13.84c-4.5-5.57-19.3-8.3-8.75-10.87h57.15" fill="black" fill-opacity=".4"/></g></g></g><g transform="translate(56 128)"><rect y="1" width="68" height="22" rx="5" fill="black" fill-opacity=".2"/><rect x="4" y="5" width="60" height="14" rx="2" fill="white"/><path fill="black" fill-opacity=".1" d="M14 5h4v14h-4zm24 0h4v14h-4zM26 5h4v14h-4zm24 0h4v14h-4z"/></g><g transform="translate(38 76)"><path d="m18 19 12-2M20 31c0-3.31 2.9-6 7-6 3.1 0 6 2.69 6 6m53-11-12-3m10 14c0-3.31-2.9-6-6-6-4.1 0-7 2.69-7 6" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g7" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c7"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g7)" clip-path="url(#c7)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><path d="M32 12c-2.95 11.7-19.9 6.67-23.37 18-3.46 11.35 8.03 20 17.53 20" stroke="#2A3544" stroke-width="6" opacity=".9"/><path d="M144 55c8.4 3.49 20.1-7.6 16-16.5s-16-6.7-16-19.3" stroke="#2A3544" stroke-width="4" opacity=".9"/><mask id="sidesCables01-a" style="mask-type:luminance"><rect x="15" y="35" width="16" height="22" rx="2" fill="white"/><rect x="130" y="42" width="23" height="22" rx="2" fill="white"/><rect x="130" y="6" width="23" height="18" rx="2" fill="white"/></mask><g mask="url(#sidesCables01-a)"><path d="M-6 0h180v76H-6z" fill="#40a843"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/></g></g><g transform="translate(49 -.6)"><mask id="topAntenna-a" style="mask-type:luminance"><path d="M30 38.6a2 2 0 0 1 2-2h20a2 2 0 0 1 2 2v14H30zM40 5.59h4v31h-4z" fill="white"/></mask><g mask="url(#topAntenna-a)"><path d="M-8 .6H92v52H-8z" fill="#40a843"/><path d="M-8 3.6H92v52H-8z" fill="white" fill-opacity=".3"/><path fill="white" fill-opacity=".2" d="M30 36.59h24v16H30z"/></g><circle cx="42" cy="8.59" r="8" fill="#FFE65C"/><circle cx="45" cy="5.59" r="3" fill="white"/></g><g transform="translate(25 44)"><mask id="headRound02-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 31v-1c.18-.48.4-1.5 1-3 .83-3.06 2.78-6.56 6-10C16.7 6.6 35.17 0 65 0s48.3 6.6 58 17c3.22 3.44 5.17 6.94 6 10 .6 1.5.82 2.52 1 3v40c0-.1-.03.5 0 1a54 54 0 0 1-1 6c-1.19 6-3.4 11.91-7 17-9.72 16.34-27.74 26-57 26s-47.28-9.66-57-26c-3.6-5.1-5.8-11-7-17a54 54 0 0 1-1-6c.03-.45 0-1.32 0-1z" fill="white"/></mask><g mask="url(#headRound02-a)"><path d="M-4-2h138v124H-4z" fill="#40a843"/><g transform="translate(-44 -7)"><path fill-rule="evenodd" clip-rule="evenodd" d="M33.88 128.1c4.36-30.7 13.06-4.57 33.99-9.98 9.24-2.4 1.77-12.82 8.67-17.35 5.82-3.82 13.76-.74 20.43-3.21 9.63-3.57 9.5-10.82 18.48-11.9 8.48-1.03 17.37 2.34 25.93 1.08 16.15-2.4 9.53-34.03 21.64-39.61 12.1-5.6 32.05 4.06 34.66 19.87 2.54 15.42-14.46 72.78-14.46 72.78H33.88" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M78.93 128c37.67 0 41.6-14.75 60.13-21.5s34.79 10.71 41.02-6.27 6.2 35.95 6.2 35.95H78.93z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M41 10.87C52.73 9.75 48.96 22.8 53.6 22.8c1.85 0 4.03-8.46 7.38-8.46 2.46 0 2 5.5 7.33 4.91 5-.54 4.07-4 6.6-4 5.44 0 3.38 20.2 7.73 20.2s5.73-14.8 7.65-20.64c2.43-7.4 16.97-11.56 6.6-14.5H40.71" fill="black" fill-opacity=".4"/></g></g></g><g transform="translate(56 128)"><path fill-rule="evenodd" clip-rule="evenodd" d="M14 6.22C14 17.78 20.47 24 34 24c13.52 0 20-6.34 20-17.78C54 5.5 53.17 4 51 4H17c-2.05 0-3 1.38-3 2.22" fill="black" fill-opacity=".8"/><mask id="mouthSmile02-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M14 6.22C14 17.78 20.47 24 34 24c13.52 0 20-6.34 20-17.78C54 5.5 53.17 4 51 4H17c-2.05 0-3 1.38-3 2.22" fill="white"/></mask><g mask="url(#mouthSmile02-a)"><rect x="26" y="-2" width="16" height="14" rx="2" fill="white"/></g></g><g transform="translate(38 76)"><path d="m18 19 12-2M20 31c0-3.31 2.9-6 7-6 3.1 0 6 2.69 6 6m53-11-12-3m10 14c0-3.31-2.9-6-6-6-4.1 0-7 2.69-7 6" stroke="black" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g8" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c8"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g8)" clip-path="url(#c8)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><g opacity=".9"><ellipse cx="26.5" cy="23" rx="16.5" ry="18" stroke="#2A3544" stroke-width="6"/><path d="M23.51 36.76c-7.4 4.29-17 1.55-21.42-6.1" stroke="#2A3544" stroke-width="4"/><ellipse cx="22.5" cy="52.5" rx="16.5" ry="14.5" stroke="#2A3544" stroke-width="4"/></g><g opacity=".9"><path d="M162.6 60.42c-4.27-7.41-13.95-9.84-21.6-5.42" stroke="#2A3544" stroke-width="4"/><ellipse cx="142.5" cy="22.5" rx="16.5" ry="15.5" stroke="#2A3544" stroke-width="6"/></g><mask id="sidesCables02-a" style="mask-type:luminance"><rect x="15" y="27" width="16" height="22" rx="2" fill="white"/><rect x="16" y="60" width="16" height="12" rx="2" fill="white"/><rect x="137" y="42" width="16" height="22" rx="2" fill="white"/><rect x="137" width="16" height="22" rx="2" fill="white"/></mask><g mask="url(#sidesCables02-a)"><path d="M-6 0h180v76H-6z" fill="#b18911"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/></g></g><g transform="translate(49 -.6)"><mask id="topLights-a" style="mask-type:luminance"><path d="M2 42.6a2 2 0 0 1 2-2h76a2 2 0 0 1 2 2v10H2z" fill="white"/><path d="M10 27.6a5 5 0 0 1 5-5h6a5 5 0 0 1 5 5v25H10zm24 0a5 5 0 0 1 5-5h6a5 5 0 0 1 5 5v25H34zm24 0a5 5 0 0 1 5-5h6a5 5 0 0 1 5 5v25H58z" fill="white"/></mask><g mask="url(#topLights-a)"><path d="M-8 .6H92v52H-8z" fill="#b18911"/><path d="M-8 .6H92v52H-8z" fill="white" fill-opacity=".3"/><path fill="white" fill-opacity=".6" d="M-8 .59H92v40H-8z"/><rect x="16" y="28.59" width="4" height="8" rx="2" fill="white" fill-opacity=".6"/><rect x="40" y="28.59" width="4" height="8" rx="2" fill="white" fill-opacity=".6"/><rect x="64" y="28.59" width="4" height="8" rx="2" fill="white" fill-opacity=".6"/></g></g><g transform="translate(25 44)"><mask id="headRound01-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M66 0c58.35 0 64 40.69 64 78 0 33.32-25.47 42-64 42-37.46 0-66-8.69-66-42C0 40.69 7.65 0 66 0" fill="white"/></mask><g mask="url(#headRound01-a)"><path d="M-4-2h138v124H-4z" fill="#b18911"/><g transform="translate(-44 -7)"><path d="M72.47 40.03q-.15-.02-.26-.12c-.1-.07-.15-.12-.14.14.01.5.67-.05.4-.02M52.33 54.92s.2-.56.03-.74q-.26.26-.03.74M50.5 58c.06 0 .57-.28.48-.43-.12-.18-.49.34-.49.42m15.4 7.15c-.12-.11-.52-.45-.27-.5.14-.03.5.36.32.54v-.01h-.01zm3.44 49.32c.13-.02.66-.36.34-.45-.21.01-.6.53-.34.51m26.3-12.14c-.05.05-.2.1-.34.12-.15.03-.26.06-.16.1.19.05.84.15.5-.23M48.1 77.63l.06-.1c1.02 0-.68.19-.07.1m75.45 38.3c.07-.03.08-.03.1-.12q-.1 0-.1.1m-51.37 7.87q.1 0 .1-.11-.1 0-.1.1M52.01 55.75c.08-.02.09-.03.1-.12-.08.02-.09.03-.1.12m56.84-1q-.01.11.1.1c-.02-.09-.02-.1-.1-.1m41.43 28.16c-.08.02-.09.03-.1.12.08-.02.09-.03.1-.12" fill="black" fill-opacity=".2"/></g></g></g><g transform="translate(56 128)"><rect x="12" y="4" width="44" height="4" rx="2" fill="black" fill-opacity=".8"/></g><g transform="translate(38 76)"><path d="M28 4a20 20 0 0 1 19.9 18h41.52a5 5 0 1 1 0 4H47.9A20 20 0 1 1 28 4" fill="black" fill-opacity=".2"/><circle cx="94" cy="24" r="2" fill="white"/><circle cx="28" cy="24" r="16" fill="black" fill-opacity=".6"/><circle cx="34" cy="16" r="3" fill="white"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g9" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c9"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g9)" clip-path="url(#c9)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><path d="M32 12c-2.95 11.7-19.9 6.67-23.37 18-3.46 11.35 8.03 20 17.53 20" stroke="#2A3544" stroke-width="6" opacity=".9"/><path d="M144 55c8.4 3.49 20.1-7.6 16-16.5s-16-6.7-16-19.3" stroke="#2A3544" stroke-width="4" opacity=".9"/><mask id="sidesCables01-a" style="mask-type:luminance"><rect x="15" y="35" width="16" height="22" rx="2" fill="white"/><rect x="130" y="42" width="23" height="22" rx="2" fill="white"/><rect x="130" y="6" width="23" height="18" rx="2" fill="white"/></mask><g mask="url(#sidesCables01-a)"><path d="M-6 0h180v76H-6z" fill="#e45e4d"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/></g></g><g transform="translate(49 -.6)"><mask id="topAntennaCrooked-a" style="mask-type:luminance"><path d="M47.54 34.98 43 45.59h-3.74l4.92-10.43-6.05-10.43 3.22-11.84 2.9.8-2.9 10.6z" fill="white"/><path fill="white" d="M30 39.59h24v13H30z"/></mask><g mask="url(#topAntennaCrooked-a)"><path d="M-8 .6H92v52H-8z" fill="#e45e4d"/><path d="M-8 6.6H92v52H-8z" fill="white" fill-opacity=".3"/><path fill="white" fill-opacity=".2" d="M30 39.59h24v13H30z"/></g><circle cx="42" cy="8.59" r="8" fill="#FFE65C"/><circle cx="45" cy="5.59" r="3" fill="white"/></g><g transform="translate(25 44)"><mask id="headSquare02-a" style="mask-type:luminance"><path d="M0 12A12 12 0 0 1 12 0h106a12 12 0 0 1 12 12v70a38 38 0 0 1-38 38H38A38 38 0 0 1 0 82z" fill="white"/></mask><g mask="url(#headSquare02-a)"><path d="M-2-2h134v124H-2z" fill="#e45e4d"/><g transform="translate(-44 -7)"><path d="M66 36h49.42a5 5 0 1 1 0 4H62V6h4zm-8-12h-4V6h4z" fill="black" fill-opacity=".1"/><path d="M163 31a5 5 0 0 1 2 9.58V66h19v4h-23V40.58a5 5 0 0 1 2-9.58" fill="white" fill-opacity=".2"/><path d="M157 74h27v4h-31V52h4zm-85 26a5 5 0 0 0-2 9.58V135H51v4h23v-29.42a5 5 0 0 0-2-9.58" fill="black" fill-opacity=".2"/></g></g></g><g transform="translate(56 128)"><path d="M23.05 4.44a2 2 0 1 1 3.9-.88C27.72 6.96 30.4 9 34 9s6.28-2.04 7.05-5.44a2 2 0 1 1 3.9.88C43.75 9.7 39.43 13 34 13s-9.76-3.3-10.95-8.56" fill="black" fill-opacity=".6"/></g><g transform="translate(38 76)"><circle cx="24" cy="30" r="6" fill="white"/><circle cx="80" cy="30" r="6" fill="white"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g10" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0f2a6b"/><stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
<clipPath id="c10"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g10)" clip-path="url(#c10)"/>
<g transform="translate(20.00 20.00) scale(1.1111)"><g transform="translate(6 66)"><mask id="sidesSquareAssymetric-a" style="mask-type:luminance"><rect x="4" y="31" width="36" height="30" rx="4" fill="white"/><rect x="14" y="15" width="26" height="30" rx="4" fill="white"/><rect x="133" y="23" width="36" height="30" rx="4" fill="white"/><rect x="128" y="8" width="36" height="60" rx="4" fill="white"/></mask><g mask="url(#sidesSquareAssymetric-a)"><path d="M-6 0h180v76H-6z" fill="#7a9f12"/><path d="M-6 0h180v76H-6z" fill="white" fill-opacity=".3"/><path fill="black" fill-opacity=".1" d="M-6 47h180v29H-6z"/><circle cx="155" cy="20" r="5" fill="white" fill-opacity=".6"/><circle cx="155" cy="36" r="5" fill="white" fill-opacity=".6"/></g></g><g transform="translate(49 -.6)"><mask id="topBulb01-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M24 16.6a16 16 0 0 1 16-16h4a16 16 0 0 1 16 16v16a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8z" fill="white"/><rect x="14" y="40.59" width="56" height="12" rx="1" fill="white"/></mask><g mask="url(#topBulb01-a)"><path d="M-8 .6H92v52H-8z" fill="#7a9f12"/><path d="M-8 .6H92v52H-8z" fill="white" fill-opacity=".3"/><path fill="white" fill-opacity=".4" d="M12-2.41h60v43H12z"/><path d="M41 4.1c4.93 0 9.37 2.12 12.44 5.51" stroke="white" stroke-width="2" stroke-linecap="round"/><path d="M54.14 20.42 44 30.57v10.02h-4V30.42l-10-10 2.83-2.83 9 9h.48l9.02-9z" fill="white" fill-opacity=".8"/></g></g><g transform="translate(25 44)"><mask id="headRound01-a" style="mask-type:luminance"><path fill-rule="evenodd" clip-rule="evenodd" d="M66 0c58.35 0 64 40.69 64 78 0 33.32-25.47 42-64 42-37.46 0-66-8.69-66-42C0 40.69 7.65 0 66 0" fill="white"/></mask><g mask="url(#headRound01-a)"><path d="M-4-2h138v124H-4z" fill="#7a9f12"/><g transform="translate(-44 -7)"><path fill-rule="evenodd" clip-rule="evenodd" d="M143 130.65c-3.19-17.34-9.55-2.58-24.85-5.63-6.76-1.35-1.3-7.25-6.34-9.8-4.26-2.17-10.06-.43-14.94-1.82-7.04-2.02-6.95-6.12-13.5-6.73-6.2-.58-12.7 1.33-18.97.61-11.8-1.35-6.96-19.22-15.82-22.38-8.85-3.15-23.43 2.3-25.34 11.23-1.86 8.71 10.57 41.12 10.57 41.12H143" fill="black" fill-opacity=".2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M179 122.77c-51.24 0-56.58-25.8-81.8-37.61C72 73.34 49.9 103.9 41.44 74.2S33 137.07 33 137.07h146z" fill="black" fill-opacity=".1"/><path fill-rule="evenodd" clip-rule="evenodd" d="M184.58 9.18c-11.94-.98-5.04 10.8-9.75 10.8-6.97 0-2.1-7.97-10.58-7.78-2.22.06-4.95 1.94-5.12 4.5-.36 5.52 3.72 15.03-1.83 15.3-5.18.25-1.1-9.64-1.65-16.24-.3-3.77-4.18-6.75-6.97-6.75-7.57 0-1.68 15.7-7.13 15.7s-1.21-9.05-5.08-13.84c-4.5-5.57-19.3-8.3-8.75-10.87h57.15" fill="black" fill-opacity=".4"/></g></g></g><g transform="translate(56 128)"><rect width="68" height="24" rx="5" fill="black" fill-opacity=".2"/><rect x="4" y="4" width="60" height="16" rx="2" fill="black" fill-opacity=".8"/><path d="M5 13h11l2-4 3 7 4-8 2 9 3-11 3 10 3-3h15l3-4 2 7 3-3h4" stroke="#4EFAC9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(38 76)"><rect x="8" y="10" width="88" height="36" rx="4" fill="black" fill-opacity=".8"/><rect x="28" y="21" width="10" height="17" rx="2" fill="#5EF2B8"/><rect x="66" y="21" width="10" height="17" rx="2" fill="#5EF2B8"/><path fill-rule="evenodd" clip-rule="evenodd" d="M83 10h5L76 46h-5z" fill="white" fill-opacity=".4"/></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.1 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g3" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c2d12"/><stop offset="100%" stop-color="#f97316"/>
</linearGradient>
<clipPath id="c3"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g3)" clip-path="url(#c3)"/>
<g transform="translate(20.00 20.00) scale(0.7813)"><g transform="translate(-1.5 -33.5)"><path d="M76.5 131a66 66 0 0 1 6-18.3c2.8-4.9 8.8-9.3 16.2-12.9a91 91 0 0 1 26.4-7.5c19-2.5 40.1.4 53.4 13.9q.4.1.7.5a38 38 0 0 1 5.5 23.8l2.9.2q2.3.7 4 3.4.8 1.6.9 3.7 0 2.2-.3 4.3c-.6 3-1.6 6-3 8.2-2.2 3.7-4.6 4.7-6.2 5h-.2l-2.1 15c-4.2 27.3-11 49.4-19.2 64.9q-6.1 11.7-13.1 18.3a27 27 0 0 1-14.4 7.9c-8.8 1.2-24.8-4.6-41.3-35.6A168 168 0 0 1 75 156.1a7 7 0 0 1-4.4-2.3 14 14 0 0 1-3.3-6.7c-1.4-6.7-.5-11.3 1.8-14 2-2.4 5-3 7.5-2.2" fill="#ffffff"/><path d="M184.3 131.6c2.3-.3 5-.3 6.8 2.9s.1 11-2.3 15c-2 3.5-4.2 4.3-5.4 4.5m-108-21.8c-4.2-2-11 1.2-8.3 14.6 1.1 5.2 4.6 8.4 7.1 8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M82.4 113c-14.6 29-6 82.6 10.7 114.2s32.4 36.8 40.4 35.7c18.5-2.6 37.9-36.2 46.3-92.3 5.7-37.6 5.6-54.2-1.2-64.1" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g transform="translate(119 162)"><path d="M12.8 11.5c1.8 16 7.4 10.4 9.8 19.3 2 7-9.2 7.7-12.1 5.4" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(74 213)"><path d="M35.4 53.2c2.1 9-.1 17 1 20.4M75.8 47c0 7.6-.8 14.4-.8 17.8M64.2 76.1c0 2.6 1 9.8 1 12.4m-3.3.9c-1.4-3.2-3.3-14.1-4.1-16.3M40.2 45.3c1 3.8-1.8 25.1 6.1 42.8M33 79.2a76 76 0 0 1-1.5-22.7m40 28.9A40 40 0 0 1 71 69m-3.5 6.1C66 69.4 69 60.3 69.7 54.5m-44.4-4.1c-3.6 5 0 16.9 0 23.1m60.3-27.2c2 3.5 4 12.3 4 20.8m-7.3-13.3c0 7.2-3.4 16.3-3.4 20.3M49.5 76c-1.7-2.5-6.4-19.6-4.3-26m9.2 22.4c0-3-2-6.2-3-11.7m3.2 26.5c-.7-8-1.5-7.1-1.5-14.1m-17.8 16c-2.6-7.2-6.4-8.6-7.7-13.6m66.1 2.7c0-3 1-8.2 1-10.1M41 95.3c-.8-8-2.5-11.5-2.5-18.4m23.8-.1c-.8-8-2.5-11.5-2.5-18.4M87.2 84c-.7-8-1.8-9.2-1.8-16.1M52.3 97.3c-3.1-7.8-1.7-10-3.8-17.7m9.3 22.8A38 38 0 0 0 55 90m7.4 12.3c.5-1.8 0-5.7 0-8.5M33.6 51.1c.6-1.8 0-4.5 0-7.3m-5.1 3.7c-1.2-2.5 1.3-4.1 1.3-7M50 55v-8.3m19.9 3.5v-5.7m10.9 4.9c0-2.1 0-4.5-1.4-6.8m14.8 15.2c.6-1.7 0-4.5 0-7.2M91.3 47c-.4-1.8-2.2-4-3.5-6.4m-20.5 60.3c.6-1.8 1-5.5 1-9.4M56.3 60c-1-2.2-.5-6.5-2.2-10.5m10.5 8.3c0-2.4-1.6-5.2-1.6-9.7m19.3 40c.5-2.3-.5-5.4.5-9.8m-9.3 17.5c1.1-2.1 1-5.3 3.1-9.3M48 102c-.5-2.7-1.8-7.5-3.5-11.5m33.2-5.3A32 32 0 0 0 75 72.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(101 225)"><path d="M31.5 8.4c-7.2 0-14-1-17-1.9 1 2.6 3.4 12 17 12S48 9.3 49.8 6.5c-6 1.8-11 2-18.3 2" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(3 3)"><path d="M79.3 109.2c11-18.5 70-33.3 96-5.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M76.8 107.2c-7.5-3.3-16-3-22.3-3.1m23.5 7c-4.2-.8-8-1.3-15.5.4m18.9-3.7c-5.6-3.8-13.3-12-15.8-14.2m10.9 5.1c-2-4-5.4-8-8.1-12.8M79 89.2c.6 2.2 1 9 3.7 16.5m-.3-19.5a30 30 0 0 1 2.7 16.2m-19.7-2.8c-3.4 0-6.4-1.5-9.5-3m117.1 3.8c0-3.6 1.2-16 7-20.2m-3.4 24.6L183 87m10.1 5.8a30 30 0 0 0-16.5 12m17.6-20.6c-2 .9-6.3 3.8-8.8 8.3m13.1 4.2c-4.4 1-14.2 4.2-18.8 8.7m20.1-1.6c-5.5.3-17 1-19.7 2m11.9 7.5s-6.2-3.5-13.4-4" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(73 202)"><path d="M61.8 12.6c-4.7-1.7-22.6 3-28 1.5s-8.6-4.8-6.6-7.3 2.5-1 3.3 0c.7.8 1.6 1.5 3 1.2 2-.4 3.3-1.7 2.2-3.7C34.4 1.8 25 1.8 24 4.3s-1.8 10.6 7.4 12.6c9.1 2 29-3.6 30.4-2.8s13.5 2.2 22.8 1.7 19-7.2 16.6-11.5c-2.5-4.4-14-2.9-11.8-.4.7.6 2.6 1.5 4 .4 1.6-1.5 4.4 1.2 0 5.3-3.4 3-24.1 6-29.6 3" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(76 115)"><path d="M31.4 26.2c7.1-2.6 13.3 5.2 13.3 10.9 0 6.8-5.3 12.5-10.3 12.5s-9.9-5.7-9.9-12.5c0-5.7 4-9.8 7.8-11.3M75 21.7c6.5-2.6 11 5.4 11 11 0 6.8-4 12.6-8.6 12.6s-9-4.7-9-12.2 3.6-10.2 7.1-11.6" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M37.6 39q0 0 0 0m41.9-2.3q0 0 0 0" stroke="black" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/><path d="M25.7 17.8c2.8-3.2 10-7.9 17-1.6m21-2.6C68 9.4 78 3.2 83.7 11.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g4" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c2d12"/><stop offset="100%" stop-color="#f97316"/>
</linearGradient>
<clipPath id="c4"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g4)" clip-path="url(#c4)"/>
<g transform="translate(20.00 20.00) scale(0.7813)"><g transform="translate(-1.5 -33.5)"><path d="M74.8 140q3.2-8.4 5.4-12.2-.1-.6.1-1.1c3-4.9 9-9.3 16.4-12.9a91 91 0 0 1 26.3-7.5c19.2-2.5 40.7.5 53.9 14.4q.3.4.4 1 1.5 1.7 3.6 6a109 109 0 0 1 4.4 10.2q2.2-.8 4.6-.5a7 7 0 0 1 5.4 3.3c2 3.5 2.9 10 1.6 15.8a7 7 0 0 1-2.6 4.2 6 6 0 0 1-2.3 1q.8 4.2 1.3 8.5c2.8 23.3-1.3 49.6-26 62.6-23.6 12.4-47 16-65.6 12.3C83 241.3 69 230 65 212.6c-3.2-13.5-1.3-31 2.2-46.7q-2.4-.5-3.9-3-2-3.2-1.7-9.2.8-9.4 4.8-12.8A7 7 0 0 1 71 139a9 9 0 0 1 3.8.9" fill="#ffffff"/><path d="M80.6 127.2c-6.1 10-22.3 57.6-15.6 86.4 8.1 34.4 54.6 44.9 102.1 19.7 48.7-25.8 16.6-104.3 9.7-111.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M185.8 139c2.6-1.2 7.4-1.4 9.3 1.9 1.8 3.2 2.7 9.3 1.4 15.1-.7 3.3-3 4.3-5 4.3M74 141.3c-5.9-3.5-11.4-.2-12.4 12.2-.7 7.9 2.9 11.4 5.4 11" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g transform="translate(118 154)"><path d="M1.5 2.2C5 2.2 9.2 8 9.6 17c.7 14.7-4.2 19.4 0 27 2.7 5 17.8 4.7 21.9-3.6 3.2-6.5-3-12-6.6-20.7-3.5-8.3-5.7-13.4-2-18.2" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(69 197)"><path d="M6.5 14.5q0 0 0 0m-5-4.5q0 0 0 0m92-4.5q0 0 0 0m-10.3 9q0 0 0 0m-60.3-1.1q0 0 0 0M12.4 24.5q0 0 0 0m7.4 14.4q0 0 0 0m52.5 2.9q0 0 0 0m12.3-1.3q0 0 0 0m-16.9-7.7q0 0 0 0m40.1-31.3q0 0 0 0m.5 9.7q0 0 0 0m-9.8 6.4q0 0 0 0m-6.9 14.7q0 0 0 0M82 28q0 0 0 0M44.5 41.8q0 0 0 0m13.3 4.6q0 0 0 0M36 33.4q0 0 0 0m-5.5 9.9q0 0 0 0M57 32.8q0 0 0 0m-36.4-8.3q0 0 0 0" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(90 218)"><path d="M8.5 10.7c3 1.1 9.3 3 24.3 1.7 18.7-1.7 19-3.3 23.4-5.9" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M33.2 7a37 37 0 0 0-18.8 5.2c15.7 12.4 34.8 0 34.8-2.6-2.4-1-2.4-3.3-16-2.5" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(1 17)"><path d="M100.8 98c6.4-10 11-12.3 19.3-17m-32.8 20.8c4.5-1 7.5-3 11-8M94.6 104c3.4.4 6.2-.3 9.8-3.1m-9.3-10c4.6-4.3 9.6-2 15.4-7.3m48 17.4c-2.2-1.2-.5-9 3-13m1.8 3.6c-.2 3.1.5 10.4 4.7 14.6m0-13.9a12 12 0 0 0 5.2 8.5m-65.4-2.2c2.1-5.3 9.7-14 25.1-18.7m-19.3 22.8c3.5-8.8 19-15.7 29.5-25.2m-18.4 25.8c1.5-5 2.5-8 6.4-10.8 7.5-3.5 17-10.5 21-20M134 105.6C134 95 146.7 92 157.7 79m-11.9 22c1.1-5.3 5.3-6.5 12.3-15.5M75.5 99.8c5.2-.2 11.3-2 15.5-6" stroke="#9747ff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(74 196)"><path d="M63.8 9.6c-6-2.3-20.4-2.7-28.3 2.8s-10.1 7-5.8 7.8a74 74 0 0 0 31.6-2.6 7 7 0 0 0 3.3-7.3" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M84 8.6c-12.1-4.4-17.1-1-18.7 2.6-.7 1.5-1.2 4.8 1.3 5.8 3 1.2 22.8 1.3 32-1.6 2.6-2.2-3.4-2.8-14.7-6.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(75 124)"><path d="M31.4 26.2c7.1-2.6 13.3 5.2 13.3 10.9 0 6.8-5.3 12.5-10.3 12.5s-9.9-5.7-9.9-12.5c0-5.7 4-9.8 7.8-11.3M75 21.7c6.5-2.6 11 5.4 11 11 0 6.8-4 12.6-8.6 12.6s-9-4.7-9-12.2 3.6-10.2 7.1-11.6" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M37.6 39q0 0 0 0m41.9-2.3q0 0 0 0" stroke="black" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"/><path d="M25.7 17.8c2.8-3.2 10-7.9 17-1.6m21-2.6C68 9.4 78 3.2 83.7 11.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g5" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c2d12"/><stop offset="100%" stop-color="#f97316"/>
</linearGradient>
<clipPath id="c5"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g5)" clip-path="url(#c5)"/>
<g transform="translate(20.00 20.00) scale(0.7813)"><g transform="translate(-1.5 -33.5)"><path d="M74.8 140q3.2-8.4 5.4-12.2-.1-.6.1-1.1c3-4.9 9-9.3 16.4-12.9a91 91 0 0 1 26.3-7.5c19.2-2.5 40.7.5 53.9 14.4q.3.4.4 1 1.5 1.7 3.6 6a109 109 0 0 1 4.4 10.2q2.2-.8 4.6-.5a7 7 0 0 1 5.4 3.3c2 3.5 2.9 10 1.6 15.8a7 7 0 0 1-2.6 4.2 6 6 0 0 1-2.3 1q.8 4.2 1.3 8.5c2.8 23.3-1.3 49.6-26 62.6-23.6 12.4-47 16-65.6 12.3C83 241.3 69 230 65 212.6c-3.2-13.5-1.3-31 2.2-46.7q-2.4-.5-3.9-3-2-3.2-1.7-9.2.8-9.4 4.8-12.8A7 7 0 0 1 71 139a9 9 0 0 1 3.8.9" fill="#ffffff"/><path d="M80.6 127.2c-6.1 10-22.3 57.6-15.6 86.4 8.1 34.4 54.6 44.9 102.1 19.7 48.7-25.8 16.6-104.3 9.7-111.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M185.8 139c2.6-1.2 7.4-1.4 9.3 1.9 1.8 3.2 2.7 9.3 1.4 15.1-.7 3.3-3 4.3-5 4.3M74 141.3c-5.9-3.5-11.4-.2-12.4 12.2-.7 7.9 2.9 11.4 5.4 11" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g transform="translate(118 154)"><path d="M14.8 17.5c-4 .9-7.2 6.8-4 10 4 4 12.3 4.7 13.1 0 .7-3.9-1.3-6-2.7-6.7" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(69 197)"><path d="M29.4 29.2c1.6 5.1.7 14.8 1.8 18.2m31 .3c-.6-3.3-.7-6.4 0-11.3m2.1-4.8c0-1.8 0-2.7-1.3-4.9m-22.1 2.5c0-2.3 0-3.4.6-5m-5-4.2q1.8-1.6 2.6-2.9M54.7 28c.3-1.6 0-4.4 0-5.3m-28.9-5c.8-1 3.4-3.2 3.4-4m66.3 2c1.2.4 4 2.2 5 2M31 23.2c.7 1.4 2.6 3.4 3.2 4.2M73 24.5c-.5-1.5-2-3.9-2.5-4.7m11.2.3-1-4m7.9-3.6c-1.5-1-2.6-2.2-3.4-2.6M98.4 38a19 19 0 0 0-5.9-8m-55.6 3c1 3.7-2.3 9.8 0 12.6M17.2 38c2.3-1.6 5.3-5.7 6-11.6m-4.5-4.2c-3.7 4.8-3.8 5-6.5 8m35.6 18.7A15 15 0 0 1 46 35.5m9.2 10.6c0-1.8 0-3.8-3-7.6m29.6 6.8c.8-1.7-2.6-6.2-4.4-7.4m28.8-5.4c.6-1.7-1.5-4.7-6-7.8m-9.9 18.7c-.6-3.6-1.9-7.6-3-9.7m-10.9-4.5c.8-.3 2.7-.1 4 3.2m7.4-10.3c.5.4 3 .7 5.2 1.7M70.6 35.7c1.2 3 .7 6.4 0 11.1" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(90 218)"><path d="M17.5 19c2.2-4.6 11.3-13.9 30.3-13.5" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(1 17)"><path d="M79 117.4c-2.7 1.7-7.3 5.2-14 5.2m16.2-11.2a35 35 0 0 1-11.1 5.3m-1-5.6a27 27 0 0 0 12.7-7.3M66 107.1c3.7-.6 12.6-3.1 17.6-7.9m-19.1 1.3c4.4-.4 14.7-2.6 20.6-8m-14.6.5c4-1 13-3.4 16.3-3.1m-13.9-7.4c4.2-.2 13.7.2 18 3.3m86.1 32.5c1.8 1.9 6.9 5.4 12.3 4.2m-13.4-7.5c1.7-1 6.4-2.2 11.9 2m-13.8-7.9c3.5.7 11.1 1.6 13.3 0M163.6 93c2.8-2.7 7.4-7.8 9.5-9.4M168 98.1c4.5 1.1 7.9 2.8 13-.5m-14.3-3.1c3.5-.4 11.8-1.3 16.6-1.3m-22-2.9c1-2.9 3.7-9 7.2-10.4M152.8 82c3.3 0 3.7-8.6 8-10.2m2.7 7.2a32 32 0 0 1-6.6 5.4m23.1 20.4c-2 .1-4.8.2-8.6-.5m-16.8-35.9c-1 4-3.3 9.6-5.6 11.8m1.1-15.2a16 16 0 0 0-5 10.2m-5.2-6.2c.3 2 .9 6.2.5 8.5" stroke="#f24e1e" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M145.2 63.4c-.7 1.4-.7 13.6 1 16.2m-10.8-18.1c-1 3 .2 12 3.6 15.3m-13.6-.5c-.1-1.7-2.1-9.4-3.2-11.2m-8.3-1.9c.2 3 1 9.9 3 12.4M103.7 68c2 1.5 6 5.2 6.6 7.3m-32.8 3.8c4.4-.7 14.3-1.1 18.6 3M92 73.5a9 9 0 0 0 8.6 5.3m-3-7.6c.4 1.3 2.6 4.4 8 6.6" stroke="#f24e1e" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M111 67.8a43 43 0 0 0 12.3 5.1m6-10.6c-.8 2.8-1.1 9.6 4.3 13.9" stroke="#f24e1e" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(74 196)"><path d="M61.8 12.6c-4.7-1.7-22.6 3-28 1.5s-8.6-4.8-6.6-7.3 2.5-1 3.3 0c.7.8 1.6 1.5 3 1.2 2-.4 3.3-1.7 2.2-3.7C34.4 1.8 25 1.8 24 4.3s-1.8 10.6 7.4 12.6c9.1 2 29-3.6 30.4-2.8s13.5 2.2 22.8 1.7 19-7.2 16.6-11.5c-2.5-4.4-14-2.9-11.8-.4.7.6 2.6 1.5 4 .4 1.6-1.5 4.4 1.2 0 5.3-3.4 3-24.1 6-29.6 3" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(75 124)"><path d="M32.3 26.3c3.3.6 11-.1 14.7-7.9m15.7-4.9c.5 2.7 2.7 8.5 7.4 10.8a18 18 0 0 0 10.3 2M18.7 39a134 134 0 0 1 27.1-5m-27.3 7.2c6.8-1.3 20.5-4.5 25.2-5.2m21.1-2.7a69 69 0 0 1 26.3 4.1" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M64.2 33.7c7.1-1.2 20.6 3.7 24.6 5.5" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M64 34.6c7-1.3 21.6 4.9 25.6 6.7" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M64 36c7-1.3 21.6 5 25.6 6.9m-67.2-3.1c6.6-2.3 20.6-6.6 24-5.2m-24.9 8.9a444 444 0 0 1 24-7" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.0 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g7" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c2d12"/><stop offset="100%" stop-color="#f97316"/>
</linearGradient>
<clipPath id="c7"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g7)" clip-path="url(#c7)"/>
<g transform="translate(20.00 20.00) scale(0.7813)"><g transform="translate(-1.5 -33.5)"><path d="M75.4 135.5a57 57 0 0 1 5-14.8c2.8-4.9 8.7-9.3 16.2-12.8a90 90 0 0 1 26.2-7.6c19-2.5 40.4.5 53.6 14.5q.4.5.3 1.2 3.2 6.1 6.3 16.8 2-.9 4.6-.7c2 0 4 .9 5.2 3 2 3.6 2.2 9.1.7 15.4a7 7 0 0 1-3 4.7q-1.3.7-2.5 1 1.8 11.8 2.5 24.3c.5 13.2-.3 26.3-3.3 37.3s-8.4 20-17 24.7a79 79 0 0 1-56 8A51 51 0 0 1 77 219a161 161 0 0 1-5.1-49.6l.3-8.4a5 5 0 0 1-4.9-2.1 17 17 0 0 1-2.6-8.2q-1-8.6 2.3-12.8a7 7 0 0 1 4-2.7q2.2-.5 4.4.3" fill="#ffffff"/><path d="M80.4 120.8c-10.4 19.4-10.8 77.8-3.2 99 11.5 32.3 56.4 43.4 93.3 23.3 33.3-18.1 18-105 6.1-127.6" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M184.4 133.7c2.6-1.1 6.9-1.7 8.7 1.5 1.9 3.2 2 8.4.6 14.7-1 4-3.8 4.8-5.7 4.8M74.2 136.5c-6-2.6-10.8 2.6-9.5 13.7 1 7.8 3.4 9.6 6 9.3" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g transform="translate(117 160)"><path d="M1.5 2.2C5 2.2 9.2 8 9.6 17c.7 14.7-4.2 19.4 0 27 2.7 5 17.8 4.7 21.9-3.6 3.2-6.5-3-12-6.6-20.7-3.5-8.3-5.7-13.4-2-18.2" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(73 203)"><path d="M6.5 14.5q0 0 0 0m-5-4.5q0 0 0 0m92-4.5q0 0 0 0m-10.3 9q0 0 0 0m-60.3-1.1q0 0 0 0M12.4 24.5q0 0 0 0m7.4 14.4q0 0 0 0m52.5 2.9q0 0 0 0m12.3-1.3q0 0 0 0m-16.9-7.7q0 0 0 0m40.1-31.3q0 0 0 0m.5 9.7q0 0 0 0m-9.8 6.4q0 0 0 0m-6.9 14.7q0 0 0 0M82 28q0 0 0 0M44.5 41.8q0 0 0 0m13.3 4.6q0 0 0 0M36 33.4q0 0 0 0m-5.5 9.9q0 0 0 0M57 32.8q0 0 0 0m-36.4-8.3q0 0 0 0" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(102 222)"><path d="M17 9.5c-1.3 2.1.4 7.7 15.3 6.8s19-9 14.5-8.8c-6.2.2-10.7 4.2-20.1 2-3.4-.7-8.5-2-9.7 0" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(1 11)"><path d="M127.3 94.2c-5.1-11.1-20.3-14-29.3 0-12 18.2-2.1 57.6-5.6 83.7-3.5 26-21.4 55.8-44.4 55.8S8 219.1 9.8 200.6" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M121 82.5c-9-7.9-34.2-9-38.2 46.7-3.7 53.6-11.7 64.6-28.6 81-15.8 15-36 17.7-50 10.6" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M124.4 80.2c-11.2-15.7-37-9.8-46.6 2-12 14.7 0 77 0 98.8s-10.1 42.6-41 47" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M125 76.9c-4.5-13-19.1-19.7-35.4-10.1-21.5 12.5-22.1 32-26.3 69.5-4.3 37.4-2.3 70.3-33.9 81.1-18 6.2-33.7-9.5-25.8-26.4" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M30 200c33.7-5 43.3-42.7 44.4-77.6 1.6-49.5 36-60.1 50.6-41m4 6.2c8.4-4.8 34.8-5 38.2 24.7 4.2 37-5.5 72.4 7.8 107.4 5.5 14.3 26 23.6 37.7 19" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M130 82.3c10.8-4.5 31-7.7 38.3 6.4 9.1 17.6 1 83 7.3 103.4s27 37.1 41.6 27.6" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M126.7 80.2c-.7-9 12.4-27 28.1-18.5a59 59 0 0 1 32 42.2c5.7 39.9 2.1 60 5.7 86 3.5 25.9 16.1 46.2 59 32.9" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M127.8 79.7c4.5-7.9 12.4-14 26.4-9.6 17.6 5.6 30.6 29.1 30 94.8-.8 65.8 6 75.6 24.6 79.5 18.5 4 30.7-10.4 35.4-18.6" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M134 77.4c16.9-10.3 55.1-7.7 60.2 27.6 6.2 44.1-3.6 96.4 2 112.5s21.4 19.2 34 15.7" stroke="#699bf7" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(74 201)"><path d="M63.8 9.6c-6-2.3-20.4-2.7-28.3 2.8s-10.1 7-5.8 7.8a74 74 0 0 0 31.6-2.6 7 7 0 0 0 3.3-7.3" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M84 8.6c-12.1-4.4-17.1-1-18.7 2.6-.7 1.5-1.2 4.8 1.3 5.8 3 1.2 22.8 1.3 32-1.6 2.6-2.2-3.4-2.8-14.7-6.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(74 119)"><path d="M33 46.5c-.7-.6-5.1-2.3-7.4-6.2-2.3-3.8-2.5-7.7 0-9.3 1.6-1.2 5.1.6 7.4 2.2m1-.7c1.4-1.6 4-3 6.6-1.5 1.8 1 1.6 7 0 9.3-1.2 1.9-5.2 5-6.7 6m41.3-14.5C73 29.7 68 26.4 66 30c-2.6 4.6 5.6 13 10.2 14.8m1.1.7c2.2-1.7 7-5.4 7.8-6.6 1-1.5 2.8-8 0-10.6s-8.6 1-8.6 5.2m-53-16.7c.6-1.4 3-4.2 7-3.7 5.2.6 6.2 1.5 7.4 3.7m31.8-2.1c.5-1.7 3-4.8 8.2-4 5.2.6 7 3.5 7.3 4.9" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g8" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c2d12"/><stop offset="100%" stop-color="#f97316"/>
</linearGradient>
<clipPath id="c8"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g8)" clip-path="url(#c8)"/>
<g transform="translate(20.00 20.00) scale(0.7813)"><g transform="translate(-1.5 -33.5)"><path d="M75.2 127a46 46 0 0 1 7.2-17c3-5 9-9.5 16.4-13a90 90 0 0 1 26.4-7.7c19-2.5 40.2.5 53.5 14l.6.4c7.8 7.5 9.5 18.5 9.2 30.3v1.3q2.4.4 4.8 3.2c1.8 2.4 2.8 6.3 1.8 12a21 21 0 0 1-2.9 8.1l-.8 1.5q-1.1 2.3-2.7 3.8a4 4 0 0 1-3 .9l-.5 6.4c-2.2 37-4.7 59.8-22.1 79.2-16.9 18.7-43.6 13.3-57 0-10.7-10.7-28.5-33-32-83.4-1.9.3-3.7-1-5-3q-2.4-3.4-4.1-11.8-1.3-8.2 1.5-12a8 8 0 0 1 6.8-3.4h.5q.5-5.6 1.4-9.8" fill="#ffffff"/><path d="M188 135.7c2.2 0 8.2 2.7 6.3 13.8-.9 4.9-2 6-3.7 9.2-1.5 2.8-2.7 4.9-5.5 3.6m-113.2-25c-3.8 0-9 3.2-7 13.8 2.2 11.4 5.6 14.4 7.9 13.7" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M83.7 108c-7.2 10.3-11.8 19.4-9.5 56.5 3.2 51.9 21.4 74.5 32.2 85.2 13.1 13 39.3 18 55.7 0 17.3-19.2 19.8-41.8 22-79.2 1.5-24.9 9.4-52.8-5.4-67" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g transform="translate(119 165)"><path d="M22.8 22c.7.7 2.6 5.4-5.6 6.4-7 .8-7.1-3.7-7.1-5.3" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(75 212)"><path d="m54.4 52.2-.9 5.8m3.2-6c.6 3 .2 4.5 1.3 6.5m3-.5s-1-2.9-1-6m3.3-.5c.6 4 2 6 2 6" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(104 228)"><path d="M14.5 11.5a88 88 0 0 0 36.2 0" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(3)"><path d="M132 74c-6.3 8-18.4 10.4-26 11.5-12.9 2-19.4 9.3-24.9 27.3-7.6 25-3.2 46.5-5.4 65.4-2.7 22.6 4 27.2 5.4 28.4" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M132.7 68.7c-4.9 7-17.9 9-33.6 12.2-19.7 4-32.7 33-31 73 1.4 32 .6 48.4 0 52.7" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M57.4 195c5.2-7.6 4.5-22.4 3.5-33.6-3.5-37 5.2-64.3 17.3-77.6 15.4-16.8 40-8.1 53.9-19.1" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M130.4 61.2c-8.7-5.8-25.4-2-34.2 1.2-11 4-35.2 20-38.8 56.7-4 41.8-14.2 71.6 0 84.8" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M46.4 216.4c4.6-12.7-2.9-23.2-2.9-46.3 0-19.8 9.5-65.4 16.5-78.8C67.5 77 70.3 69.5 84 60.6c16.2-10.4 39.9-15.7 49.1-1.2m3.1-1.1c16.2-15 31.4-2 41.6 10.4 12.8 15.7 16.6 20.9 20 53.9 3.5 33 2 36.8 3 46.9 1 13.1 6 17.8 14.7 24.8" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M136.1 65.9c9.9 9.8 15.8 5 25.8 9.6 15.4 7 20.9 16.8 23.8 31.9s-2.3 48.6 0 59.6c1.8 8.8 5 17.1 16.2 20.2" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M136.1 61.8c12.7-3 16-2.4 26 1.7 12.8 5.2 29.4 26 28.8 56.2-.6 30.1-4.6 54.4 13.3 73.5" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M135.6 70c1 4 5.3 8.7 16.7 11.6 22.8 5.7 27.6 28.3 28.8 49.1s-4.5 38.8 3 54.5c6 12 14.2 16.6 20.8 16.6" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M135 75.1c1.7 11.6 13.9 13.3 29 22.6s14.6 33.3 11.6 58.2c-2.5 21.2 3 35.8 18.7 35.8" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(75 205)"><path d="M59.6 13.4c-14.7 2.1-46 8-58 11.5M125.3 11c-14.7 2-46.5.8-58.5 1.6" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M58.8 13.8c-14.7 2-38 11.6-51.5 13.1M122 14.7c-14.8 2.1-40.6-1.6-53.9-1.6m-8.5-.4C44.9 14.8 14.3 18.4 2 20.2M126.5 5.5C111.8 7.6 79 9.9 66.7 11.7" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(76 124)"><path d="M23 16.7a17 17 0 0 1 11.3-1.9c4.5.6 6.4 2.3 7.7 4.3m28.4-.6c2.2-2.8 6-3.3 8.3-3.4q3.8-.3 7.2.6" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="m1.5 19.1 13.7 8.5m30.5 3.9a32 32 0 0 1 20.5-1.9m28.2-2.3c2.5-1.3 10.6-8.8 13.1-10.6M15.6 28.2c-.3 3.7-.4 13.1 4.4 16.6 4.7 3.5 23.5-.3 25.2-1 1.7-.5 1.7-7.7-.9-14.3-2.1-5.5-20.5-3.7-28-2.3" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M68 26.5c6-2.7 22-1 25 0 3.7 1 4.1 10.3 3.8 13.7-.4 3.3-20.5 3.4-25.2 3.2-3.7-.1-4.9-11.6-4-16.5" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M31 30.8c-.5.2-1.2 1-1 1.9.5 1 3.5 2.5 3.6.8q0-2.6-1.2-1.6m46.7-2.2c-.6.6-1.7 1.8-1 2.3 1 .7 4.3 2.3 4 0-.2-1.8-1.1-1.7-1.5-1.4l-.8.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g9" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c2d12"/><stop offset="100%" stop-color="#f97316"/>
</linearGradient>
<clipPath id="c9"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g9)" clip-path="url(#c9)"/>
<g transform="translate(20.00 20.00) scale(0.7813)"><g transform="translate(-1.5 -33.5)"><path d="M76.5 131a66 66 0 0 1 6-18.3c2.8-4.9 8.8-9.3 16.2-12.9a91 91 0 0 1 26.4-7.5c19-2.5 40.1.4 53.4 13.9q.4.1.7.5a38 38 0 0 1 5.5 23.8l2.9.2q2.3.7 4 3.4.8 1.6.9 3.7 0 2.2-.3 4.3c-.6 3-1.6 6-3 8.2-2.2 3.7-4.6 4.7-6.2 5h-.2l-2.1 15c-4.2 27.3-11 49.4-19.2 64.9q-6.1 11.7-13.1 18.3a27 27 0 0 1-14.4 7.9c-8.8 1.2-24.8-4.6-41.3-35.6A168 168 0 0 1 75 156.1a7 7 0 0 1-4.4-2.3 14 14 0 0 1-3.3-6.7c-1.4-6.7-.5-11.3 1.8-14 2-2.4 5-3 7.5-2.2" fill="#ffffff"/><path d="M184.3 131.6c2.3-.3 5-.3 6.8 2.9s.1 11-2.3 15c-2 3.5-4.2 4.3-5.4 4.5m-108-21.8c-4.2-2-11 1.2-8.3 14.6 1.1 5.2 4.6 8.4 7.1 8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M82.4 113c-14.6 29-6 82.6 10.7 114.2s32.4 36.8 40.4 35.7c18.5-2.6 37.9-36.2 46.3-92.3 5.7-37.6 5.6-54.2-1.2-64.1" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g transform="translate(119 162)"><path d="M23.8 4.5c-.8 5.3-1.2 16.3.3 22.3C27.7 41 22.6 44.3 21 44.3c-5.3 0-6.5-4.7-7.7-18-1.5-16.5-1.6-16.3-3.7-21" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(74 213)"><path d="M35.4 53.2c2.1 9-.1 17 1 20.4M75.8 47c0 7.6-.8 14.4-.8 17.8M64.2 76.1c0 2.6 1 9.8 1 12.4m-3.3.9c-1.4-3.2-3.3-14.1-4.1-16.3M40.2 45.3c1 3.8-1.8 25.1 6.1 42.8M33 79.2a76 76 0 0 1-1.5-22.7m40 28.9A40 40 0 0 1 71 69m-3.5 6.1C66 69.4 69 60.3 69.7 54.5m-44.4-4.1c-3.6 5 0 16.9 0 23.1m60.3-27.2c2 3.5 4 12.3 4 20.8m-7.3-13.3c0 7.2-3.4 16.3-3.4 20.3M49.5 76c-1.7-2.5-6.4-19.6-4.3-26m9.2 22.4c0-3-2-6.2-3-11.7m3.2 26.5c-.7-8-1.5-7.1-1.5-14.1m-17.8 16c-2.6-7.2-6.4-8.6-7.7-13.6m66.1 2.7c0-3 1-8.2 1-10.1M41 95.3c-.8-8-2.5-11.5-2.5-18.4m23.8-.1c-.8-8-2.5-11.5-2.5-18.4M87.2 84c-.7-8-1.8-9.2-1.8-16.1M52.3 97.3c-3.1-7.8-1.7-10-3.8-17.7m9.3 22.8A38 38 0 0 0 55 90m7.4 12.3c.5-1.8 0-5.7 0-8.5M33.6 51.1c.6-1.8 0-4.5 0-7.3m-5.1 3.7c-1.2-2.5 1.3-4.1 1.3-7M50 55v-8.3m19.9 3.5v-5.7m10.9 4.9c0-2.1 0-4.5-1.4-6.8m14.8 15.2c.6-1.7 0-4.5 0-7.2M91.3 47c-.4-1.8-2.2-4-3.5-6.4m-20.5 60.3c.6-1.8 1-5.5 1-9.4M56.3 60c-1-2.2-.5-6.5-2.2-10.5m10.5 8.3c0-2.4-1.6-5.2-1.6-9.7m19.3 40c.5-2.3-.5-5.4.5-9.8m-9.3 17.5c1.1-2.1 1-5.3 3.1-9.3M48 102c-.5-2.7-1.8-7.5-3.5-11.5m33.2-5.3A32 32 0 0 0 75 72.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(101 225)"><path d="M14.5 11.5a88 88 0 0 0 36.2 0" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(3 3)"><path d="M114 52.7a74 74 0 0 0-18.7 46.7m25.9-47.1c6.3 3.9 20 17 24.5 38.6" stroke="#9747ff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M135.3 92.7c-23 2-48.8 11.8-58.8 16.5 0-22.6 19-57 44.7-58.1 25.7-1.3 49.6 19 58.2 41.6-6.8 0-15.3-2.4-44.1 0" stroke="#9747ff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M91.7 103c3.5 3.6 11.9 6.6 29 6.6 19 0 48.2-4.2 52.5-6.6 6.7-3.8 6.5-7.4 5.9-10m-64.9-40.7c-1.2-3.5-3-10.6-1-11.6 2.7-1.2 9 2.4 8.7 10" stroke="#9747ff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(73 202)"><path d="M30.5 11.5c6 4.5 20.8 11 32 0m4.7-.9c3 3.7 17.3 9 30-1.1" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(76 115)"><path d="M33 46.5c-.7-.6-5.1-2.3-7.4-6.2-2.3-3.8-2.5-7.7 0-9.3 1.6-1.2 5.1.6 7.4 2.2m1-.7c1.4-1.6 4-3 6.6-1.5 1.8 1 1.6 7 0 9.3-1.2 1.9-5.2 5-6.7 6m41.3-14.5C73 29.7 68 26.4 66 30c-2.6 4.6 5.6 13 10.2 14.8m1.1.7c2.2-1.7 7-5.4 7.8-6.6 1-1.5 2.8-8 0-10.6s-8.6 1-8.6 5.2m-53-16.7c.6-1.4 3-4.2 7-3.7 5.2.6 6.2 1.5 7.4 3.7m31.8-2.1c.5-1.7 3-4.8 8.2-4 5.2.6 7 3.5 7.3 4.9" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="240" height="240">
<defs>
<linearGradient id="g10" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#7c2d12"/><stop offset="100%" stop-color="#f97316"/>
</linearGradient>
<clipPath id="c10"><circle cx="120" cy="120" r="120"/></clipPath>
</defs>
<rect width="240" height="240" fill="url(#g10)" clip-path="url(#c10)"/>
<g transform="translate(20.00 20.00) scale(0.7813)"><g transform="translate(-1.5 -33.5)"><path d="M74.8 140q3.2-8.4 5.4-12.2-.1-.6.1-1.1c3-4.9 9-9.3 16.4-12.9a91 91 0 0 1 26.3-7.5c19.2-2.5 40.7.5 53.9 14.4q.3.4.4 1 1.5 1.7 3.6 6a109 109 0 0 1 4.4 10.2q2.2-.8 4.6-.5a7 7 0 0 1 5.4 3.3c2 3.5 2.9 10 1.6 15.8a7 7 0 0 1-2.6 4.2 6 6 0 0 1-2.3 1q.8 4.2 1.3 8.5c2.8 23.3-1.3 49.6-26 62.6-23.6 12.4-47 16-65.6 12.3C83 241.3 69 230 65 212.6c-3.2-13.5-1.3-31 2.2-46.7q-2.4-.5-3.9-3-2-3.2-1.7-9.2.8-9.4 4.8-12.8A7 7 0 0 1 71 139a9 9 0 0 1 3.8.9" fill="#ffffff"/><path d="M80.6 127.2c-6.1 10-22.3 57.6-15.6 86.4 8.1 34.4 54.6 44.9 102.1 19.7 48.7-25.8 16.6-104.3 9.7-111.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M185.8 139c2.6-1.2 7.4-1.4 9.3 1.9 1.8 3.2 2.7 9.3 1.4 15.1-.7 3.3-3 4.3-5 4.3M74 141.3c-5.9-3.5-11.4-.2-12.4 12.2-.7 7.9 2.9 11.4 5.4 11" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><g transform="translate(118 154)"><path d="M21.2 9.5c5 4 12 23 6 29s-8.7-4.7-13.8-4.7-9 6-8.5-1c.4-5.6 4-5.5 4.7-5.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(69 197)"><path d="M52.6 49.7c-1 6.7-4.5 9-4.5 9m18.6-6.9c-.4 4.2 1.3 9 1.3 9M54.9 50c.2 4.8-2.1 10.5-2.1 10.5m9.9-10.1 2.6 9.6M50 50.4c-.6 2.1-4.7 5.6-4.7 5.6m2.7-5.5c-1 2-5.5 5-5.5 5m28.4-6c.8 2.5 5.3 6.4 5.3 6.4m-18.5-4.5c1 5.3-1 10-1.3 12m4.9-1.1S60 56 59.8 51.8m8.2-2.3a19 19 0 0 0 5.8 10.7" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(90 218)"><path d="M19.5 16.2c7.2 3.9 23.1 2 25.4-7 .2-1 0-2.7-1.2-2.7" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(1 17)"><path d="M79.3 109.2c11-18.5 70-33.3 96-5.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M76.8 107.2c-7.5-3.3-16-3-22.3-3.1m23.5 7c-4.2-.8-8-1.3-15.5.4m18.9-3.7c-5.6-3.8-13.3-12-15.8-14.2m10.9 5.1c-2-4-5.4-8-8.1-12.8M79 89.2c.6 2.2 1 9 3.7 16.5m-.3-19.5a30 30 0 0 1 2.7 16.2m-19.7-2.8c-3.4 0-6.4-1.5-9.5-3m117.1 3.8c0-3.6 1.2-16 7-20.2m-3.4 24.6L183 87m10.1 5.8a30 30 0 0 0-16.5 12m17.6-20.6c-2 .9-6.3 3.8-8.8 8.3m13.1 4.2c-4.4 1-14.2 4.2-18.8 8.7m20.1-1.6c-5.5.3-17 1-19.7 2m11.9 7.5s-6.2-3.5-13.4-4" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(74 196)"><path d="M63.8 9.6c-6-2.3-20.4-2.7-28.3 2.8s-10.1 7-5.8 7.8a74 74 0 0 0 31.6-2.6 7 7 0 0 0 3.3-7.3" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M84 8.6c-12.1-4.4-17.1-1-18.7 2.6-.7 1.5-1.2 4.8 1.3 5.8 3 1.2 22.8 1.3 32-1.6 2.6-2.2-3.4-2.8-14.7-6.8" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g><g transform="translate(75 124)"><path d="M24.3 17.6c1.3 1.2 4.7 2.4 8.6 1.7 3.5-.5 5-2 6-3.9m28.7-.8c2 2.6 5.3 3.4 7.5 3.6 2.2 0 5-.8 7.2-2" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="m1.5 19 13.7 8.5m30.5 3.8a32 32 0 0 1 20.5-1.8m28.2-2.3c2.5-1.4 10.6-7.8 13.1-9.7" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 44.5c-8.2-3.1-7.5-11.4-3.6-17.2 3-3.8 22.8-2.2 27.2 1.8 4.2 3.8 2.3 11.8-1.8 14.3-3.4 2-16.2 3.1-21.7 1m71.8-19.2c-3.6-1.2-18.2-1-24 2.6-3.8 2.5-6.5 14.7 3.6 16 4.6.7 14.9 0 18.7-1.8 8.2-4 6.8-15.2 1.8-16.8" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/><path d="M27.2 33.8a5 5 0 0 1 4.1-2.6c3.1 0 4 1.3 4.7 2.1m39.4.5a5 5 0 0 1 4.2-2.6c3 0 4 1.3 4.6 2.1" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></g></g></g>
<circle cx="120" cy="120" r="114" fill="none" stroke="#ffffff" stroke-opacity="0.4" stroke-width="2.5"/>
<g transform="translate(196 196)"><circle r="27" fill="#ffffff" fill-opacity="0.22"/><g transform="translate(-12 -12)"></g></g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<defs>
<linearGradient id="bg" x1="50%" y1="0%" x2="50%" y2="100%">
<stop offset="0%" stop-color="#2c3e50"/>
<stop offset="100%" stop-color="#34495e"/>
</linearGradient>
</defs>
<rect width="512" height="512" fill="url(#bg)" clip-path="url(#clip)"/>
<circle cx="256" cy="250" r="70" fill="white" opacity="0.9"/><circle cx="249" cy="247" r="2.5" fill="#2c3e50"/><path d="M259 245 L267 249" stroke="#2c3e50" stroke-width="2.5" stroke-linecap="round"/><path d="M248 255 Q256 265 264 255" stroke="#2c3e50" fill="none" stroke-width="2.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 720 B

Some files were not shown because too many files have changed in this diff Show More