继续提交

This commit is contained in:
2025-12-19 15:51:26 +08:00
parent 0ce60d78df
commit cf2203e3eb
500 changed files with 3259 additions and 7739 deletions

View File

@@ -1,70 +0,0 @@
# Node.js 相关
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 源代码和开发文件(精简版不需要)
src
scripts
public
package*.json
vite.config.js
eslint.config.js
index.html
# 构建相关
dist
.vite
# 环境文件
.env*
# IDE/Editor files
.vscode
.idea
*.swp
*.swo
*~
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Git
.git
.gitignore
# Docker 相关(保留 Dockerfile
docker-compose.yml
# 脚本文件(精简版不需要)
docker-entrypoint.sh
# 文档
README*.md
LICENSE
# 日志
logs
*.log
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# ESLint cache
.eslintcache

View File

@@ -1,50 +0,0 @@
# 极简静态资源镜像
FROM nginx:alpine
# 安装 wget 用于健康检查
RUN apk add --no-cache wget
# 极简静态资源镜像
FROM nginx:alpine
# 安装 wget 用于健康检查
RUN apk add --no-cache wget
# 创建自定义 nginx 配置,禁用缓存并确保正确读取挂载目录
RUN printf 'server {\n\
listen 80;\n\
server_name localhost;\n\
root /usr/share/nginx/html;\n\
index index.html index.htm;\n\
\n\
# 禁用缓存\n\
add_header Cache-Control "no-cache, no-store, must-revalidate";\n\
add_header Pragma "no-cache";\n\
add_header Expires "0";\n\
\n\
location / {\n\
try_files $uri $uri/ /index.html;\n\
# 确保每次都读取最新文件\n\
sendfile off;\n\
tcp_nodelay on;\n\
tcp_nopush off;\n\
}\n\
\n\
# 禁用所有静态文件缓存\n\
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {\n\
add_header Cache-Control "no-cache, no-store, must-revalidate";\n\
add_header Pragma "no-cache";\n\
add_header Expires "0";\n\
}\n\
}\n' > /etc/nginx/conf.d/default.conf
# 确保挂载目录存在但为空(避免与外部挂载冲突)
RUN mkdir -p /usr/share/nginx/html && \
rm -rf /usr/share/nginx/html/* && \
echo "Waiting for external mount..." > /usr/share/nginx/html/.placeholder
# 暴露静态服务端口
EXPOSE 80
# 启动 nginx 前台运行
CMD ["nginx", "-g", "daemon off;"]

207
README.md
View File

@@ -1,29 +1,198 @@
# Markdown To Web
# 萌芽笔记 🌱
> 将本地 Markdown 笔记目录“一键”编译为可部署的静态 React 网站
一个优雅的 Markdown 笔记 Web 应用,支持实时预览和 GitHub 风格渲染
此仓库会读取 `public/mengyanote/` 下的所有 Markdown 文件,生成 JSON 数据,并在前端通过 React + react-markdown 渲染,最终可直接构建成 **纯静态站点**(可部署到 GitHub Pages / Netlify / Vercel / 任意 Nginx / OSS 等)。
> **🎉 v2.0 重要更新 (2025-12-19)**
> - ✅ 完全移除 localStorage 依赖,解决浏览器权限弹窗问题
> - ✅ 完美支持移动端浏览器微信、Safari iOS 等)
> - ✅ 隐私模式/无痕模式下正常工作
> - ✅ 添加错误边界和友好的错误提示
> - ✅ 优化网络请求,支持相对路径部署
> - 📖 详细说明请查看 [前端优化说明.md](前端优化说明.md)
本 README中文包含项目定位、特性说明、运行与构建、内部工作原理、目录结构、忽略与定制、部署方式、常见问题FAQ与未来计划Roadmap
## ✨ 特性
如需英文版本,请查看 `README.en.md`
- 📝 **Markdown 渲染**: 使用 `react-markdown` 提供强大的渲染能力
- 🎨 **GitHub 风格**: 标准的 GitHub Markdown 样式
- 🔤 **霞鹜文楷字体**: 全站使用 LXGW WenKai Mono 等宽字体
- 📂 **文件树导航**: 直观的侧边栏目录结构
- 🚀 **快速响应**: FastAPI 后端 + React 前端
- 💡 **代码高亮**: 支持多种编程语言的语法高亮
- 🧮 **数学公式**: KaTeX 数学公式渲染支持
- 📱 **响应式设计**: 完美适配桌面和移动设备
- 🌐 **跨浏览器兼容**: 支持所有主流浏览器和移动端
- 🔒 **隐私模式支持**: 在隐私/无痕模式下正常工作
## ✨ 主要特性
## 🛠 技术栈
- 🚀 **一键生成数据**:脚本自动递归扫描 `public/mengyanote`,提取目录树、文件内容与统计信息。
- 📦 **纯静态输出**:构建阶段就准备好全部数据,无需后端 / Serverless API。
- 🧭 **动态目录树**:可展开/折叠,每个节点保留唯一 `path``id`
- 📝 **Markdown 全面支持**GFM表格 / 任务列表 / 删除线、换行、数学公式KaTeX、代码块高亮Highlight.js
- 🧮 **数学公式渲染**`remark-math` + `rehype-katex`
- 💡 **代码体验增强**:复制按钮、语言标签、自动高亮。
- 📚 **面包屑导航**:基于文件路径实时生成。
- 🧱 **可定制渲染组件**:可移除/替换任意 remark/rehype 插件,或覆写组件(如链接、图片、表格、代码块)。
- 🕶 **深色风格**可自定义CSS 易修改,可换成“极简/原味/图片优先”布局。
- 🔍 **预生成数据多路径兜底**:前端运行时尝试从 `/data``/src/data` 加载,兼容不同构建/部署结构。
- 🧯 **忽略机制**:支持 `ignore.json` 自定义排除目录 / 文件。
- 📊 **统计信息**:生成文件总数、文件夹总数、生成时间等元数据。
### 后端
- **Python 3.8+**
- **FastAPI** - 现代、快速的 Web 框架
- **Uvicorn** - ASGI 服务器
### 前端
- **React 19** - 用户界面库
- **Vite 7** - 极速构建工具
- **react-markdown** - Markdown 渲染
- **github-markdown-css** - GitHub 风格样式
- **rehype/remark 插件** - 扩展 Markdown 功能
- GFM (GitHub Flavored Markdown)
- 代码高亮 (highlight.js)
- 数学公式 (KaTeX)
- HTML 支持
## 🚀 快速开始
### 方式一使用批处理脚本Windows推荐
#### 启动后端
双击运行 `启动后端.bat`
#### 启动前端
双击运行 `启动前端.bat`
### 方式二:手动启动
#### 1. 启动后端服务
```bash
# 进入后端目录
cd mengyanote-backend
# 安装依赖(首次运行)
pip install -r requirements.txt
# 启动服务
python main.py
```
后端将在 `http://localhost:8000` 运行
#### 2. 启动前端服务
```bash
# 进入前端目录
cd mengyanote-frontend
# 安装依赖(首次运行)
npm install
# 启动开发服务器
npm run dev
```
前端将在 `http://localhost:5173` 运行
## 📁 项目结构
```
mengyanote/
├── mengyanote-backend/ # 后端服务
│ ├── main.py # FastAPI 应用入口
│ ├── requirements.txt # Python 依赖
│ └── mengyanote/ # Markdown 文件存储目录
│ ├── 编程语言/
│ ├── 计算机科普/
│ ├── 计算机网络/
│ └── ...
├── mengyanote-frontend/ # 前端应用
│ ├── src/
│ │ ├── components/ # React 组件
│ │ │ ├── Sidebar.jsx # 侧边栏组件
│ │ │ ├── MarkdownRenderer.jsx # Markdown 渲染器
│ │ │ └── ...
│ │ ├── context/ # React Context
│ │ ├── utils/ # 工具函数
│ │ ├── App.jsx # 主应用组件
│ │ └── main.jsx # 应用入口
│ ├── public/ # 静态资源
│ │ └── LXGWWenKaiMono-Medium.ttf # 字体文件
│ ├── package.json # 前端依赖
│ └── vite.config.js # Vite 配置
├── 启动后端.bat # 后端启动脚本
├── 启动前端.bat # 前端启动脚本
├── 启动说明.md # 详细启动说明
└── README.md # 项目说明
```
## 🎯 功能说明
### 后端 API
- `GET /api/tree` - 获取文件树结构
- `GET /api/file?path={path}` - 获取指定文件内容
- `GET /api/health` - 健康检查
- `GET /docs` - API 文档Swagger UI
### 前端功能
- **文件导航**: 点击左侧文件树浏览笔记
- **Markdown 渲染**: 自动渲染 GitHub 风格的 Markdown
- **代码高亮**: 自动识别并高亮代码块
- **数学公式**: 支持行内和块级数学公式
- **图片支持**: 点击图片可放大查看
- **外链处理**: 外部链接自动在新标签页打开
## 🎨 字体配置
项目使用 **霞鹜文楷等宽体 (LXGW WenKai Mono)** 作为全站字体。
字体文件位置:`mengyanote-frontend/public/LXGWWenKaiMono-Medium.ttf`
如果字体文件缺失,系统会自动回退到默认字体。
## 🔧 环境变量
### 前端环境变量(可选)
`mengyanote-frontend` 目录创建 `.env` 文件:
```env
VITE_API_BASE=http://localhost:8000
```
## 📦 构建生产版本
### 前端构建
```bash
cd mengyanote-frontend
npm run build
```
构建产物在 `dist` 目录,可部署到任何静态托管服务。
## 🐛 故障排除
### 前端无法连接后端
1. 确认后端服务正在运行(访问 http://localhost:8000/api/health
2. 检查浏览器控制台的网络请求错误
3. 确认后端端口为 8000
### 字体未加载
1. 确认 `LXGWWenKaiMono-Medium.ttf` 存在于 `public` 目录
2. 检查浏览器开发者工具的网络面板
3. 清除浏览器缓存重试
### Markdown 渲染异常
1. 检查文件编码是否为 UTF-8
2. 查看浏览器控制台错误信息
3. 确认 react-markdown 相关依赖已正确安装
## 📝 开发建议
1. **IDE**: 推荐使用 VS Code
2. **代码规范**: 前端使用 ESLint 进行代码检查
3. **热重载**: 后端和前端都支持热重载,修改代码自动刷新
4. **调试**: 使用 Chrome DevTools 进行前端调试
---
**Made with ❤️ by 树萌芽**
> 该项目适合:知识库发布、学习笔记归档、团队内部静态文档、离线备份网页化。
## 🛠 技术栈

View File

@@ -1,16 +0,0 @@
version: '3.8'
services:
web:
build: .
container_name: markdown-to-web
ports:
- "5173:80"
volumes:
- /shumengya/docker/storage/markdown2web/dist:/usr/share/nginx/html
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80"]
interval: 30s
timeout: 10s
retries: 3

View File

@@ -1,4 +0,0 @@
#!/bin/sh
# 该脚本在简化后的 Docker 流程中不再使用,保留占位避免误调用。
echo "docker-entrypoint.sh 已弃用:当前镜像仅用于静态文件托管。"

View File

@@ -0,0 +1,18 @@
__pycache__
*.pyc
*.pyo
*.pyd
.Python
*.so
*.egg
*.egg-info
dist
build
.git
.gitignore
.vscode
.idea
*.md
README.md
.env
.env.local

View File

@@ -0,0 +1,26 @@
# 使用 Python 3.11 slim 镜像作为基础镜像
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
TZ=Asia/Shanghai
# 复制依赖文件
COPY requirements.txt .
# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 复制应用代码
COPY main.py .
COPY mengyanote ./mengyanote
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Binary file not shown.

View File

@@ -0,0 +1,26 @@
version: '3.8'
services:
mengyanote-backend:
build: .
container_name: mengyanote-backend
restart: unless-stopped
ports:
- "2424:8000"
volumes:
# 持久化 mengyanote 数据目录
- /shumengya/docker/mengyanote/data:/app/mengyanote
environment:
- TZ=Asia/Shanghai
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/tree"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- mengyanote-network
networks:
mengyanote-network:
driver: bridge

257
mengyanote-backend/main.py Normal file
View File

@@ -0,0 +1,257 @@
import os
import json
from datetime import datetime
from pathlib import Path
from typing import List, Literal, Optional, Set
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
BASE_DIR = Path(__file__).resolve().parent
# Markdown 根目录:指向当前后端项目中的 `mengyanote` 文件夹
MARKDOWN_ROOT = BASE_DIR / "mengyanote"
# ignore.json 文件路径
IGNORE_FILE = MARKDOWN_ROOT / "ignore.json"
def load_ignore_list() -> Set[str]:
"""从 ignore.json 加载需要忽略的文件夹列表"""
if not IGNORE_FILE.exists():
return set()
try:
with open(IGNORE_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
return set(data.get('ignore', []))
except Exception:
return set()
# 加载忽略列表
IGNORE_LIST = load_ignore_list()
class NodeType(str):
FOLDER: Literal["folder"] = "folder"
FILE: Literal["file"] = "file"
class DirectoryNode(BaseModel):
name: str
path: str # 相对于 MARKDOWN_ROOT 的路径,使用 / 作为分隔符
type: Literal["folder", "file"]
children: Optional[List["DirectoryNode"]] = None
DirectoryNode.update_forward_refs()
class FileContent(BaseModel):
path: str
content: str
word_count: int = 0
file_size: int = 0 # 文件大小,字节
created_time: str = ""
modified_time: str = ""
app = FastAPI(title="MengyaNote Backend", version="1.0.0")
app.add_middleware(
CORSMiddleware,
# 现在允许任意来源方便本地和静态托管访问,如 http://localhost:9090
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def is_markdown_file(path: Path) -> bool:
return path.is_file() and path.suffix.lower() == ".md"
def should_skip(entry: Path) -> bool:
"""判断是否应该跳过该文件或文件夹"""
name = entry.name
# 跳过隐藏文件/文件夹
if name.startswith("."):
return True
# 跳过 ignore.json 文件本身
if name == "ignore.json":
return True
# 跳过 ignore.json 中配置的文件夹
if entry.is_dir() and name in IGNORE_LIST:
return True
return False
def build_directory_tree(root: Path) -> List[DirectoryNode]:
"""从文件系统构建目录树,结构尽量与原先 JSON 保持一致。"""
if not root.exists() or not root.is_dir():
return []
def walk(current: Path, rel: Path) -> DirectoryNode:
name = current.name
rel_path_str = rel.as_posix() if rel.as_posix() != "." else ""
if current.is_dir():
children_nodes: List[DirectoryNode] = []
for child in sorted(current.iterdir(), key=lambda p: (p.is_file(), p.name.lower())):
if should_skip(child):
continue
child_rel = rel / child.name
# 只收录 Markdown 文件和非空目录
if child.is_dir():
node = walk(child, child_rel)
# 如果目录下完全没有 md 文件/子目录,可以选择丢弃
if node.children:
children_nodes.append(node)
elif is_markdown_file(child):
children_nodes.append(
DirectoryNode(
name=child.name,
path=child_rel.as_posix(),
type="file",
children=None,
)
)
return DirectoryNode(
name=name,
path=rel_path_str or name,
type="folder",
children=children_nodes,
)
else:
# 单独文件的情况一般不会作为根调用
return DirectoryNode(
name=name,
path=rel_path_str or name,
type="file",
children=None,
)
nodes: List[DirectoryNode] = []
for child in sorted(MARKDOWN_ROOT.iterdir(), key=lambda p: (p.is_file(), p.name.lower())):
if should_skip(child):
continue
rel = Path(child.name)
if child.is_dir():
node = walk(child, rel)
if node.children:
nodes.append(node)
elif is_markdown_file(child):
nodes.append(
DirectoryNode(
name=child.name,
path=rel.as_posix(),
type="file",
children=None,
)
)
return nodes
def resolve_markdown_path(relative_path: str) -> Path:
"""将前端传入的相对路径安全地转换为磁盘路径,防止目录穿越。"""
# 统一使用 / 分隔符
safe_path = relative_path.replace("\\", "/").lstrip("/")
candidate = MARKDOWN_ROOT / safe_path
try:
candidate_resolved = candidate.resolve()
except FileNotFoundError:
candidate_resolved = candidate
if not str(candidate_resolved).startswith(str(MARKDOWN_ROOT.resolve())):
raise HTTPException(status_code=400, detail="非法路径")
return candidate_resolved
@app.get("/api/tree", response_model=List[DirectoryNode])
def get_directory_tree() -> List[DirectoryNode]:
"""
获取 Markdown 目录树。
返回结构与原来的 directoryTree.json 尽量保持兼容:
- name: 文件或文件夹名
- path: 相对路径(使用 /
- type: 'folder' | 'file'
- children: 子节点数组
"""
tree = build_directory_tree(MARKDOWN_ROOT)
return tree
@app.get("/api/file", response_model=FileContent)
def get_markdown_file(path: str = Query(..., description="相对于根目录的 Markdown 路径")) -> FileContent:
"""
获取指定 Markdown 文件内容。
Query 参数:
- path: 例如 'AI/大语言模型的API 调用.md'
"""
file_path = resolve_markdown_path(path)
if not file_path.exists() or not file_path.is_file() or not is_markdown_file(file_path):
raise HTTPException(status_code=404, detail="文件不存在")
try:
content = file_path.read_text(encoding="utf-8")
except UnicodeDecodeError:
# 回退编码
content = file_path.read_text(encoding="utf-8", errors="ignore")
# 获取文件统计信息
file_stat = file_path.stat()
# 计算字数(去除空格和换行符)
word_count = len(content.replace(" ", "").replace("\n", "").replace("\r", "").replace("\t", ""))
# 获取文件大小(字节)
file_size = file_stat.st_size
# 获取创建时间和修改时间
try:
# Windows 上 st_ctime 是创建时间Linux 上是元数据更改时间
created_time = datetime.fromtimestamp(file_stat.st_ctime).strftime("%Y年%m月%d%H:%M:%S")
except:
created_time = "未知"
try:
modified_time = datetime.fromtimestamp(file_stat.st_mtime).strftime("%Y年%m月%d%H:%M:%S")
except:
modified_time = "未知"
rel = file_path.relative_to(MARKDOWN_ROOT).as_posix()
return FileContent(
path=rel,
content=content,
word_count=word_count,
file_size=file_size,
created_time=created_time,
modified_time=modified_time
)
@app.get("/api/health")
def health_check():
"""简单健康检查接口。"""
return {"status": "ok"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=int(os.getenv("PORT", 8000)), reload=True)

View File

@@ -13,12 +13,12 @@
"state": {
"type": "markdown",
"state": {
"file": "Docker/优秀好用的Docker镜像/Gitea-私有化仓库部署.md",
"file": "临时/代码片段.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Gitea-私有化仓库部署"
"title": "代码片段"
}
}
]
@@ -154,42 +154,42 @@
},
"active": "a93d6d3811397e7b",
"lastOpenFiles": [
"编程语言/Java/JavaSpring标准项目架构.md",
"树萌芽的小本本/树萌芽已部署网站(不定时持续更新).md",
"Linux/ADB/ADB应用启动命令.md",
"Linux/ADB/ADB常用命令.md",
"树萌芽的小本本/重要信息记录.md",
"树萌芽の小想法/革命后的理想.md",
"临时/Obsidion/Obsidion美化.md",
"临时/Obsidion",
"运维/nginx快速配置反向代理.md",
"临时/代码片段.md",
"计算机网络/计算机网络期末考试综合题简单论述.md",
"计算机科普/网络协议科普.md",
"计算机科普/科普-Nagle 算法.md",
"计算机科普/多模态大模型识别图片,视频,音频原理.md",
"计算机科普/操作系统科普.md",
"计算机科普/编程语言之间的划分.md",
"计算机科普/编程语言科普.md",
"AI/大语言模型的API 调用.md",
"AI/大语言模型的API key.md",
"AI/控制台AI大模型.md",
"Docker/优秀好用的Docker镜像/registry-轻量级自建Docker镜像仓库.md",
"内网穿透/Wireguard/Wireguard基础命令.md",
"编程语言/Golang/Golang标准代码架构.md",
"编程语言/前端/React标准项目架构.md",
"编程语言/前端/Vue标准项目架构.md",
"编程语言/前端/纯静态网页的强大功能与应用.md",
"编程语言/前端/JavaScript趣味题/JavaScript趣味题_128.md",
"编程语言/前端/React项目初始化教程.md",
"编程语言/前端/OpenList美化代码.md",
"编程语言/前端/css注入代码合集.md",
"编程语言/前端/Vue项目初始化教程.md",
"编程语言/前端/代码片段/代码片段-特殊HelloWorld输出.md",
"编程语言/前端/前端html导入css和js方法.md",
"编程语言/前端/代码片段/代码片段-标准HelloWorld输出.md",
"生活科普/男生烫发术语.md",
"生活科普/中国免签知识科普.md",
"编程语言/Golang/Golang各平台编译教程.md",
"树萌芽的小本本/大萌芽-Debian13服务器.md",
"树萌芽的小本本/树萌芽国外身份.md",
"树萌芽的小本本/重要信息记录.md",
"Linux/随身WiFi/随身WiFi一些记录.md",
"树萌芽的小本本/树萌芽の编程想法.md",
"树萌芽的小本本/树萌芽の小秘密.md",
"树萌芽的小本本/树萌芽の吐槽.md",
"树萌芽的小本本/革命后的理想.md",
"编程语言/前端/代码片段",
"编程语言/前端/JavaScript趣味题/JavaScript趣味题_28.md",
"编程语言/前端/React打包成Windows和Android软件方案.md",
"编程语言/前端/JavaScript趣味题/JavaScript趣味题_18.md",
"编程语言/前端/nodejs的markdown库.md",
"实习求职/面试经历/27双非本一腾讯IEG游戏安全后台实习面经.md",
"编程语言/前端/企业级标准React项目架构.md",
"编程语言/前端/JavaScript趣味题",
"实习求职/面试经历",
"编程语言/前端",
"编程语言/前端&HTML&CSS&JS/React项目初始化教程.md",
"数据库/SQLite",
"数据库/MongoDB",
"数据库/MySQL",
"内网穿透/Wireguard/配置/wgs-alyxg.conf",
"内网穿透/Wireguard/配置/wgs-alycd.conf",
"内网穿透/Wireguard/配置/wgc-win.conf",
"Minecraft/基岩版服务器/NukkitLearn/images/5-04.png",
"Minecraft/基岩版服务器/NukkitLearn/images/5-03.png",
"Minecraft/基岩版服务器/NukkitLearn/images/5-02.png",

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 371 KiB

After

Width:  |  Height:  |  Size: 371 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 MiB

After

Width:  |  Height:  |  Size: 4.9 MiB

View File

Before

Width:  |  Height:  |  Size: 5.1 MiB

After

Width:  |  Height:  |  Size: 5.1 MiB

View File

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 4.8 MiB

View File

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

Before

Width:  |  Height:  |  Size: 924 KiB

After

Width:  |  Height:  |  Size: 924 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 4.8 MiB

View File

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 257 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 MiB

After

Width:  |  Height:  |  Size: 4.7 MiB

View File

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

Before

Width:  |  Height:  |  Size: 493 KiB

After

Width:  |  Height:  |  Size: 493 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

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