commit 43a35e3f476f01c6915c40fd119a5d510c4aa410 Author: kuekhaoyang Date: Wed Dec 24 23:53:24 2025 +0800 new diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c550055 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +Dockerfile +.dockerignore +node_modules +npm-debug.log +README.md +.next +.git diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..5199918 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,51 @@ +name: 🐛 错误报告 (Bug Report) +description: 创建一个报告以帮助我们改进 KVideo +title: "[Bug]: " +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + 感谢您花时间填写此错误报告! + - type: checkboxes + id: duplicates + attributes: + label: 重复检查 + description: 请确保您已搜索过现有的 Issue,以避免重复报告。 + options: + - label: 我已搜索过现有的 Issue,没有发现类似的问题。 + required: true + - type: textarea + id: description + attributes: + label: 问题描述 + description: 请清晰简洁地描述发生了什么问题。 + placeholder: 比如:点击播放按钮时,播放器没有反应... + validations: + required: true + - type: textarea + id: reproduction + attributes: + label: 复现步骤 + description: 请提供复现该问题的步骤。 + placeholder: | + 1. 打开首页 + 2. 搜索 "Matrix" + 3. 点击第一个结果 + 4. ... + validations: + required: true + - type: input + id: environment + attributes: + label: 环境信息 + description: 请提供您的浏览器版本、操作系统等信息。 + placeholder: Chrome 120, macOS 14.2 + validations: + required: true + - type: textarea + id: logs + attributes: + label: 相关日志 + description: 如果有控制台报错或服务器日志,请粘贴在这里。 + render: shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..9443ca8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: 提问 / Questions + url: https://github.com/KuekHaoYang/KVideo/discussions/new?category=q-a + about: 如果你有关于项目的问题,请在这里提问。 + - name: 安全漏洞 / Security Vulnerability + url: https://github.com/KuekHaoYang/KVideo/security/advisories/new + about: 报告安全漏洞。 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..b9cb616 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,45 @@ +name: 🚀 功能请求 (Feature Request) +description: 建议一个新功能或改进 +title: "[Feat]: " +labels: ["enhancement", "triage"] +body: + - type: markdown + attributes: + value: | + 感谢您建议新功能! + - type: checkboxes + id: duplicates + attributes: + label: 重复检查 + description: 请确保您已搜索过现有的 Issue,以避免重复建议。 + options: + - label: 我已搜索过现有的 Issue,没有发现类似的建议。 + required: true + - type: textarea + id: problem + attributes: + label: 问题描述 + description: 这个功能是为了解决什么问题? + placeholder: 比如:我总是很难找到我上次看到的视频... + validations: + required: true + - type: textarea + id: solution + attributes: + label: 建议的解决方案 + description: 您希望如何解决这个问题? + placeholder: 比如:添加一个"观看历史"的侧边栏... + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: 替代方案 + description: 您是否考虑过其他替代方案? + validations: + required: false + - type: textarea + id: context + attributes: + label: 其他上下文 + description: 任何其他有助于理解该功能的截图或上下文。 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..58ba0f7 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,35 @@ +# 描述 (Description) + +请简要描述您的更改。说明您解决的问题或添加的功能。 + +## 关联 Issue (Related Issue) + +如果有相关的 Issue,请在这里链接 (例如: Fixes #123)。 + +## 更改类型 (Type of Change) + +请删除不适用的选项: + +- [ ] 🐛 Bug 修复 (Bug fix) +- [ ] ✨ 新功能 (New feature) +- [ ] 📝 文档更新 (Documentation update) +- [ ] 💄 样式/UI 更新 (Style/UI update) +- [ ] ♻️ 代码重构 (Refactoring) +- [ ] ⚡️ 性能优化 (Performance improvement) +- [ ] 🔧 配置更改 (Configuration change) + +## 检查清单 (Checklist) + +在提交 PR 之前,请确保您已完成以下检查: + +- [ ] 我已阅读并遵守 [贡献指南](../CONTRIBUTING.md) +- [ ] 我的代码遵循项目的代码风格 +- [ ] 我已对自己更改的代码进行了自我审查 +- [ ] 我已注释了难以理解的代码部分 +- [ ] 我已更新了相应的文档 (如果适用) +- [ ] 我的更改没有产生新的警告或错误 +- [ ] 我已测试了我的更改,确保其按预期工作 + +## 截图/录屏 (Screenshots/Recordings) + +如果您的更改涉及 UI/UX,请提供截图或录屏: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f544e37 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# unused/generated files +contrast-test-results.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..521a9f7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..654e027 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# 行为准则 (Code of Conduct) + +## 誓言 + +为了营造一个开放和热情的环境,作为贡献者和维护者,我们承诺让每一位参与者,无论其年龄、体型、残疾、种族、性别特征、性别认同和表达、经验水平、教育程度、社会地位、国籍、个人形象、种族、宗教或性取向如何,都能在我们的项目中获得无骚扰的体验。 + +## 标准 + +有助于创造积极环境的行为包括: + +* 使用欢迎和包容的语言 +* 尊重不同的观点和经验 +* 优雅地接受建设性的批评 +* 关注什么对社区最有利 +* 对其他社区成员表现出同理心 + +不可接受的行为包括: + +* 使用性语言或图像,以及不受欢迎的性关注或挑逗 +* 挑衅、侮辱/贬损性评论以及个人或政治攻击 +* 公开或私下骚扰 +* 未经明确许可发布他人的私人信息,例如物理地址或电子邮箱地址 +* 其他在职业环境中被认为不适当的行为 + +## 我们的责任 + +项目维护者有责任澄清可接受行为的标准,并应对任何不可接受的行为采取适当和公平的纠正措施。 + +项目维护者有权并有责任删除、编辑或拒绝不符合本行为准则的评论、提交、代码、Wiki 编辑、Issue 和其他贡献,或者暂时或永久禁止任何他们认为行为不当、威胁、冒犯或有害的贡献者。 + +## 范围 + +本行为准则适用于所有项目空间,也适用于代表该项目或其社区的公共空间。代表项目的示例包括使用官方项目电子邮件地址、通过官方社交媒体帐户发帖,或在在线或离线活动中作为指定代表行事。 + +## 执行 + +如发现任何滥用、骚扰或其他不可接受的行为,可以通过联系项目团队进行报告。所有投诉都将得到审查和调查,并将产生被认为必要且适合具体情况的回应。项目团队有义务对事件报告者保密。关于具体执行政策的更多细节可能会单独发布。 + +不真诚地遵守或执行本行为准则的维护者可能会面临项目领导层的暂时或永久性后果。 + +## 归属 + +本行为准则改编自 [Contributor Covenant][homepage],版本 2.1,可在 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1] 获取。 + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b1c8176 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,934 @@ +# 贡献指南 (Contributing Guide) + +欢迎来到 **KVideo** 项目!我们非常感谢你愿意为这个项目做出贡献。无论是修复 Bug、添加新功能、改进文档,还是提出建议,你的每一份贡献都将让这个项目变得更好。 + +为了确保协作顺畅、代码质量一致,请在提交贡献前仔细阅读本指南。 + +## 📋 目录 + +- [行为准则](#行为准则) +- [快速开始](#快速开始) +- [开发环境设置](#开发环境设置) +- [代码规范](#代码规范) +- [Git 工作流程](#git-工作流程) +- [提交规范](#提交规范) +- [Pull Request 指南](#pull-request-指南) +- [设计系统规范](#设计系统规范) +- [测试要求](#测试要求) +- [常见问题](#常见问题) + +## 🤝 行为准则 + +我们致力于构建一个开放、友好、包容的社区环境。请在参与项目时: + +- ✅ 保持尊重和礼貌 +- ✅ 欢迎不同的观点和经验 +- ✅ 接受建设性的批评 +- ✅ 专注于对社区最有利的事情 +- ❌ 不要使用性别化的语言或图像 +- ❌ 不要进行人身攻击或政治攻击 +- ❌ 不要骚扰或歧视他人 + +详细的行为准则请参阅 [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。 + +## 🚀 快速开始 + +### 我能贡献什么? + +以下是一些你可以做出贡献的方式: + +1. **🐛 报告 Bug**:发现了问题?请提交 Issue +2. **💡 提出新功能**:有好想法?在 Discussions 或 Issues 中分享 +3. **📝 改进文档**:发现文档不清晰或有错误?帮助我们改进 +4. **🎨 优化 UI/UX**:让界面更美观、更易用 +5. **⚡ 性能优化**:让应用运行得更快 +6. **🔧 修复 Bug**:解决现有的问题 +7. **✨ 添加功能**:实现新的特性 + +### 第一次贡献? + +如果这是你第一次为开源项目做贡献,我们推荐: + +1. 浏览 [GitHub Issues](https://github.com/KuekHaoYang/KVideo/issues) +2. 寻找标记为 `good first issue` 的问题 +3. 在 Issue 中评论,表明你想要解决这个问题 +4. 按照本指南进行开发和提交 + +## 🛠 开发环境设置 + +### 系统要求 + +确保你的开发环境满足以下要求: + +| 工具 | 最低版本 | 推荐版本 | 检查命令 | +|------|----------|----------|----------| +| **Node.js** | 20.0.0 | 20.x LTS | `node --version` | +| **npm** | 9.0.0 | 10.x | `npm --version` | +| **Git** | 2.30.0 | 最新版本 | `git --version` | + +### 详细设置步骤 + +#### 1. Fork 仓库 + +点击 GitHub 页面右上角的 "Fork" 按钮,将项目 Fork 到你的账号下。 + +#### 2. 克隆仓库 + +```bash +# 克隆你 Fork 的仓库 +git clone https://github.com/YOUR_USERNAME/KVideo.git +cd KVideo + +# 添加上游仓库 +git remote add upstream https://github.com/KuekHaoYang/KVideo.git +``` + +#### 3. 安装依赖 + +```bash +npm install +``` + +#### 4. 启动开发服务器 + +```bash +npm run dev +``` + +访问 `http://localhost:3000` 查看应用。 + +#### 5. 验证环境 + +确保以下命令都能正常运行: + +```bash +# 代码检查 +npm run lint + +# 构建测试 +npm run build +``` + +## 📏 代码规范 + +### 核心规范 + +#### 1. 文件长度限制 ⚠️ + +> [!CAUTION] +> **这是项目的硬性规则!所有项目文件必须保持在 150 行以内(除系统文件外)。** + +**检查命令:** + +```bash +find . -type f -not -path "*/node_modules/*" -not -path "*/.next/*" -not -path "*/.git/*" -not -name "package-lock.json" -not -name "*.png" -not -name "*.md" | xargs wc -l | awk '$1 > 150 && $2 != "total" {print $2 " - " $1 "行"}' +``` + +**如果命令有输出,说明有文件超过 150 行,必须重构!** + +**重构策略:** + +如果文件超过 150 行,请使用以下方法重构: + +##### A. 提取组件 + +**问题:** 一个组件太长,包含太多 JSX + +**解决方案:** 将大组件拆分为多个小组件 + +```typescript +// ❌ 不好:一个 200 行的大组件 +export function VideoPlayer() { + // 150+ 行代码 + return ( +
+ {/* 大量 JSX */} +
+ ); +} + +// ✅ 好:拆分为多个小组件 +export function VideoPlayer() { + return ( +
+ + + +
+ ); +} + +// PlayerControls.tsx (单独文件) +export function PlayerControls() { /* ... */ } + +// ProgressBar.tsx (单独文件) +export function ProgressBar() { /* ... */ } + +// VolumeControl.tsx (单独文件) +export function VolumeControl() { /* ... */ } +``` + +##### B. 提取自定义 Hook + +**问题:** 组件包含大量状态逻辑 + +**解决方案:** 将逻辑提取到自定义 Hook + +```typescript +// ❌ 不好:组件内有大量状态逻辑 +export function SearchPage() { + const [query, setQuery] = useState(''); + const [results, setResults] = useState([]); + const [loading, setLoading] = useState(false); + // ... 大量逻辑 + + const handleSearch = async () => { + // ... 50+ 行逻辑 + }; + + return
{/* JSX */}
; +} + +// ✅ 好:提取到自定义 Hook +export function SearchPage() { + const { query, results, loading, handleSearch } = useSearch(); + return
{/* JSX */}
; +} + +// useSearch.ts (单独文件) +export function useSearch() { + // ... 所有状态逻辑 + return { query, results, loading, handleSearch }; +} +``` + +##### C. 提取工具函数 + +**问题:** 文件包含大量辅助函数 + +**解决方案:** 将工具函数移到 `lib/utils/` + +```typescript +// ❌ 不好:组件文件包含工具函数 +export function VideoCard() { + const formatDuration = (seconds: number) => { + // ... 格式化逻辑 + }; + + const formatDate = (date: Date) => { + // ... 格式化逻辑 + }; + + // ... 更多工具函数 + + return
{/* JSX */}
; +} + +// ✅ 好:提取到工具文件 +import { formatDuration, formatDate } from '@/lib/utils/format-utils'; + +export function VideoCard() { + return
{/* JSX */}
; +} + +// lib/utils/format-utils.ts +export function formatDuration(seconds: number) { /* ... */ } +export function formatDate(date: Date) { /* ... */ } +``` + +##### D. 模块化 + +**问题:** 单个文件处理多个相关功能 + +**解决方案:** 按功能拆分文件并使用桶文件(barrel exports) + +```typescript +// ❌ 不好:player-utils.ts 包含 200 行 +export function parseHLS() { /* ... */ } +export function handlePlayback() { /* ... */ } +export function manageQuality() { /* ... */ } +// ... 更多函数 + +// ✅ 好:拆分为多个文件 +// lib/utils/player/index.ts +export * from './hls-parser'; +export * from './playback-manager'; +export * from './quality-manager'; + +// lib/utils/player/hls-parser.ts +export function parseHLS() { /* ... */ } + +// lib/utils/player/playback-manager.ts +export function handlePlayback() { /* ... */ } + +// lib/utils/player/quality-manager.ts +export function manageQuality() { /* ... */ } +``` + +#### 2. TypeScript 规范 + +**类型安全** + +```typescript +// ❌ 避免使用 any +function processData(data: any) { + return data.value; +} + +// ✅ 使用具体类型 +interface VideoData { + id: string; + title: string; + url: string; +} + +function processData(data: VideoData) { + return data.title; +} + +// ✅ 或使用 unknown(需要类型检查) +function processData(data: unknown) { + if (typeof data === 'object' && data !== null && 'value' in data) { + return (data as { value: string }).value; + } + throw new Error('Invalid data'); +} +``` + +**函数返回类型** + +```typescript +// ❌ 缺少返回类型 +function calculateTotal(items) { + return items.reduce((sum, item) => sum + item.price, 0); +} + +// ✅ 明确返回类型 +function calculateTotal(items: Item[]): number { + return items.reduce((sum, item) => sum + item.price, 0); +} +``` + +**接口定义** + +```typescript +// ✅ 使用 interface 定义对象类型 +interface VideoCardProps { + video: Video; + onPlay: (id: string) => void; + className?: string; +} + +// ✅ 使用 type 定义联合类型 +type ThemeMode = 'light' | 'dark' | 'system'; +``` + +#### 3. React 组件规范 + +**函数组件** + +```typescript +// ✅ 标准函数组件结构 +interface ButtonProps { + variant?: 'primary' | 'secondary'; + children: React.ReactNode; + onClick?: () => void; +} + +export function Button({ variant = 'primary', children, onClick }: ButtonProps) { + return ( + + ); +} +``` + +**组件文件组织** + +```typescript +// 1. 导入 +import React from 'react'; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; + +// 2. 类型定义 +interface ComponentProps { + // ... +} + +// 3. 组件定义 +export function Component({ prop1, prop2 }: ComponentProps) { + // 4. Hooks + const [state, setState] = useState(); + const router = useRouter(); + + // 5. 事件处理函数 + const handleClick = () => { + // ... + }; + + // 6. 渲染 + return ( +
{/* JSX */}
+ ); +} +``` + +**单一职责原则** + +```typescript +// ❌ 组件做太多事情 +export function VideoSection() { + // 获取数据 + // 处理搜索 + // 渲染列表 + // 处理分页 + // 处理过滤 +} + +// ✅ 拆分为专注的组件 +export function VideoSection() { + const videos = useVideos(); + return ( +
+ + + + +
+ ); +} +``` + +#### 4. 样式规范 + +**Tailwind CSS 优先** + +```typescript +// ✅ 使用 Tailwind 类名 +export function Card({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} +``` + +**遵循 Liquid Glass 设计系统** + +```typescript +// ✅ 正确使用圆角 +
{/* 容器:大圆角 */} +
{/* 小元素:完全圆形 */} + +// ❌ 不要使用其他圆角值 +
{/* 错误! */} +
{/* 错误! */} +``` + +**响应式设计** + +```typescript +// ✅ 移动优先的响应式设计 +
+``` + +#### 5. 命名规范 + +**文件命名** + +- 组件文件:`PascalCase.tsx`(例如:`VideoCard.tsx`) +- Hook 文件:`camelCase.ts`(例如:`useVideoPlayer.ts`) +- 工具文件:`kebab-case.ts`(例如:`format-utils.ts`) +- 类型文件:`kebab-case.ts`(例如:`video-types.ts`) + +**变量命名** + +```typescript +// ✅ 清晰的命名 +const videoList = [...]; +const isLoading = false; +const handleSubmit = () => {}; + +// ❌ 模糊的命名 +const data = [...]; +const flag = false; +const fn = () => {}; +``` + +**常量命名** + +```typescript +// ✅ 全大写 + 下划线 +const MAX_VIDEO_DURATION = 7200; +const API_BASE_URL = 'https://api.example.com'; +``` + +#### 6. 导入顺序 + +```typescript +// 1. React 和 Next.js +import React from 'react'; +import { useState } from 'react'; +import Link from 'next/link'; + +// 2. 第三方库 +import { create } from 'zustand'; + +// 3. 项目别名导入 +import { Button } from '@/components/ui/Button'; +import { formatDate } from '@/lib/utils/date-utils'; + +// 4. 相对路径导入 +import { LocalComponent } from './LocalComponent'; + +// 5. 类型导入 +import type { Video } from '@/lib/types/video'; +``` + +## 🔄 Git 工作流程 + +### 分支策略 + +**主分支** + +- `main`:稳定的生产分支,只接受 PR 合并 + +**功能分支命名** + +遵循以下命名规范: + +- `feat/功能名称`:新功能(例如:`feat/add-playlist`) +- `fix/问题描述`:错误修复(例如:`fix/search-crash`) +- `docs/文档修改`:文档更新(例如:`docs/update-readme`) +- `refactor/重构名称`:代码重构(例如:`refactor/player-controls`) +- `perf/优化内容`:性能优化(例如:`perf/image-loading`) +- `style/样式修改`:样式调整(例如:`style/button-spacing`) +- `test/测试内容`:测试相关(例如:`test/add-unit-tests`) +- `chore/其他修改`:构建或工具变动(例如:`chore/update-deps`) + +### 开发流程 + +#### 1. 同步上游仓库 + +在开始新工作前,先同步最新的代码: + +```bash +# 获取上游更新 +git fetch upstream + +# 切换到主分支 +git checkout main + +# 合并上游更新 +git merge upstream/main + +# 推送到你的 Fork +git push origin main +``` + +#### 2. 创建功能分支 + +```bash +# 从 main 创建新分支 +git checkout -b feat/your-feature-name + +# 确认当前分支 +git branch +``` + +#### 3. 进行开发 + +在开发过程中: + +- 频繁提交小的、原子性的改动 +- 编写清晰的提交信息 +- 定期运行 `npm run lint` 检查代码 + +#### 4. 提交前检查 + +**必须通过的检查:** + +```bash +# 1. 代码规范检查 +npm run lint + +# 2. 文件长度检查 +find . -type f -not -path "*/node_modules/*" -not -path "*/.next/*" -not -path "*/.git/*" -not -name "package-lock.json" -not -name "*.png" -not -name "*.md" | xargs wc -l | awk '$1 > 150 && $2 != "total" {print $2 " - " $1 "行"}' + +# 3. 构建测试 +npm run build +``` + +**如果任何检查失败,必须先修复!** + +#### 5. 推送分支 + +```bash +# 推送到你的 Fork +git push origin feat/your-feature-name +``` + +## 📝 提交规范 + +### Conventional Commits + +我们使用 [Conventional Commits](https://www.conventionalcommits.org/) 规范: + +``` +(): + + + +