refactor: 改为从 docs 目录动态读取 md 文件,支持目录层级

This commit is contained in:
2026-03-18 13:43:45 +08:00
parent 351557a19b
commit c5649b3eba
20 changed files with 390 additions and 500 deletions

48
package-lock.json generated
View File

@@ -19,6 +19,7 @@
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.4.1", "@vitejs/plugin-react": "^4.4.1",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"playwright": "^1.58.2",
"postcss": "^8.5.3", "postcss": "^8.5.3",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"typescript": "~5.8.3", "typescript": "~5.8.3",
@@ -2085,6 +2086,53 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/playwright": {
"version": "1.58.2",
"resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.58.2",
"resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/playwright/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.8", "version": "8.5.8",
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz",

View File

@@ -8,20 +8,21 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"clsx": "^2.1.1",
"lucide-react": "^0.511.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"lucide-react": "^0.511.0",
"clsx": "^2.1.1",
"tailwind-merge": "^3.5.0" "tailwind-merge": "^3.5.0"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.3.12", "@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.4.1", "@vitejs/plugin-react": "^4.4.1",
"typescript": "~5.8.3",
"vite": "^6.3.5",
"tailwindcss": "^3.4.17",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"postcss": "^8.5.3" "playwright": "^1.58.2",
"postcss": "^8.5.3",
"tailwindcss": "^3.4.17",
"typescript": "~5.8.3",
"vite": "^6.3.5"
} }
} }

View File

@@ -1,6 +1,8 @@
import { ApiDocViewer } from './components/ApiDocViewer' import { ApiDocViewer } from './components/ApiDocViewer'
import { config } from './config'
function App() { function App() {
document.title = config.projectName
return <ApiDocViewer /> return <ApiDocViewer />
} }

View File

@@ -3,9 +3,16 @@ import { DocTree } from './DocTree'
import { DocContent } from './DocContent' import { DocContent } from './DocContent'
import { parseMarkdown, buildFileTree } from '@/lib/parser' import { parseMarkdown, buildFileTree } from '@/lib/parser'
import type { DocFile, ParsedDoc } from '@/lib/types' import type { DocFile, ParsedDoc } from '@/lib/types'
import { docs } from '@/data/docs' import { config } from '@/config'
const DOCS_FILES: Record<string, string> = docs const modules = import.meta.glob('../docs/**/*.md', { as: 'raw', eager: true })
const DOCS_FILES: Record<string, string> = Object.fromEntries(
Object.entries(modules).map(([path, content]) => {
const relativePath = path.replace('../docs/', '')
return [relativePath, content as string]
})
)
export const ApiDocViewer = () => { export const ApiDocViewer = () => {
const [fileTree, setFileTree] = useState<DocFile[]>([]) const [fileTree, setFileTree] = useState<DocFile[]>([])
@@ -14,8 +21,7 @@ export const ApiDocViewer = () => {
useEffect(() => { useEffect(() => {
const files = Object.keys(DOCS_FILES) const files = Object.keys(DOCS_FILES)
const basePath = '/docs' const tree = buildFileTree(files, '/')
const tree = buildFileTree(files, basePath)
setFileTree(tree) setFileTree(tree)
if (files.length > 0) { if (files.length > 0) {
@@ -41,7 +47,7 @@ export const ApiDocViewer = () => {
const handleReferenceClick = useCallback((ref: string) => { const handleReferenceClick = useCallback((ref: string) => {
const normalizedRef = ref.replace('.md', '') const normalizedRef = ref.replace('.md', '')
const allFiles = Object.keys(DOCS_FILES).map(k => k.replace('/docs/', '')) const allFiles = Object.keys(DOCS_FILES)
const match = allFiles.find(f => f.replace('.md', '') === normalizedRef) const match = allFiles.find(f => f.replace('.md', '') === normalizedRef)
if (match) { if (match) {
setSelectedPath(match) setSelectedPath(match)
@@ -52,7 +58,7 @@ export const ApiDocViewer = () => {
<div className="flex h-screen bg-[#1e1e1e]"> <div className="flex h-screen bg-[#1e1e1e]">
<aside className="w-64 bg-[#252526] border-r border-[#3c3c3c] flex flex-col"> <aside className="w-64 bg-[#252526] border-r border-[#3c3c3c] flex flex-col">
<div className="p-3 border-b border-[#3c3c3c]"> <div className="p-3 border-b border-[#3c3c3c]">
<h2 className="text-sm font-semibold text-gray-200">API </h2> <h2 className="text-sm font-semibold text-gray-200">{config.sidebarTitle}</h2>
</div> </div>
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden">
<DocTree <DocTree

View File

@@ -1,4 +1,3 @@
import React from 'react'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import type { ParsedDoc, DocTable, DocCodeBlock } from '@/lib/types' import type { ParsedDoc, DocTable, DocCodeBlock } from '@/lib/types'

9
src/config.ts Normal file
View File

@@ -0,0 +1,9 @@
export interface DocConfig {
projectName: string
sidebarTitle: string
}
export const config: DocConfig = {
projectName: 'API Docs',
sidebarTitle: '文档目录',
}

View File

@@ -1,37 +0,0 @@
# RHIDevice 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: RHI 设备抽象基类,提供图形设备创建和管理功能。
## 公共方法
| 方法 | 描述 |
|------|------|
| Create() | 创建 RHI 设备实例 |
| Initialize() | 初始化图形设备 |
| Shutdown() | 关闭并释放设备资源 |
## 属性
| 属性 | 描述 |
|------|------|
| capabilities | 获取硬件能力检测结果 |
| deviceName | 获取设备名称 |
## 示例代码
```cpp
using namespace XCEngine::RHI;
class MyDevice {
void Init() {
auto device = RHIDevice::Create();
device->Initialize();
}
}
```
@see RHICommandQueue.md

View File

@@ -1,48 +0,0 @@
# RHITexture 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: 纹理资源抽象,用于存储图像数据。
## 公共方法
| 方法 | 描述 |
|------|------|
| Create() | 创建纹理资源 |
| Destroy() | 销毁纹理资源 |
| Map() | 映射纹理数据到 CPU |
| Unmap() | 取消映射 |
## 纹理属性
| 属性 | 描述 |
|------|------|
| width | 纹理宽度 |
| height | 纹理高度 |
| format | 纹理格式 |
| mipLevels | Mipmap 级别数 |
## 纹理格式枚举
| 格式 | 描述 |
|------|------|
| R8G8B8A8_UNORM | 8位 RGBA |
| R32_FLOAT | 32位单通道浮点 |
| D32_FLOAT | 32位深度 |
| BC7_UNORM | BC7 压缩格式 |
## 使用示例
```cpp
RHITextureDesc desc;
desc.width = 1024;
desc.height = 1024;
desc.format = RHIFormat::R8G8B8A8_UNORM;
desc.mipLevels = 1;
auto texture = device->CreateTexture(desc);
```
@see RHIDevice.md

View File

@@ -1,53 +0,0 @@
# RHIBuffer 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: 缓冲区资源抽象,用于存储顶点、索引、常量等数据。
## 缓冲区类型
| 类型 | 描述 |
|------|------|
| VertexBuffer | 顶点缓冲区 |
| IndexBuffer | 索引缓冲区 |
| ConstantBuffer | 常量缓冲区 |
| ReadBackBuffer | 回读缓冲区 |
## 公共方法
| 方法 | 描述 |
|------|------|
| Create() | 创建缓冲区 |
| Map() | 映射到 CPU 内存 |
| Unmap() | 取消映射 |
| SetData() | 设置数据 |
## 属性
| 属性 | 描述 |
|------|------|
| size | 缓冲区大小 |
| usage | 缓冲区用途 |
| stride | 数据步长 |
## 使用示例
```cpp
RHIBufferDesc vbDesc;
vbDesc.size = sizeof(Vertex) * vertexCount;
vbDesc.usage = RHIBufferUsage::VertexBuffer;
vbDesc.stride = sizeof(Vertex);
auto vertexBuffer = device->CreateBuffer(vbDesc);
vertexBuffer->SetData(vertices.data());
```
## 性能提示
- 对于静态数据,使用 Immutable 模式
- 频繁更新的数据使用 Dynamic 模式
- 大缓冲区考虑使用分页上传
@see RHIDevice.md @see RHICommandList.md

View File

@@ -1,32 +0,0 @@
# RHICommandList 类
**命名空间**: XCEngine.RHI
**类型**: class
**继承**: IRefCounter
**描述**: 命令列表抽象,用于记录渲染命令。
## 公共方法
| 方法 | 描述 |
|------|------|
| Reset() | 重置命令列表 |
| Close() | 关闭命令列表 |
| BeginRenderPass() | 开始渲染通道 |
| EndRenderPass() | 结束渲染通道 |
| Draw() | 发出绘制调用 |
| Dispatch() | 发出计算调度 |
## 示例代码
```cpp
cmdList->BeginRenderPass(passDesc);
cmdList->SetPipelineState(pso);
cmdList->Draw(3, 1, 0, 0);
cmdList->EndRenderPass();
cmdList->Close();
```
@see RHIDevice.md

View File

@@ -1,32 +0,0 @@
# RHICommandQueue 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: 命令队列抽象,用于提交渲染命令到 GPU。
## 公共方法
| 方法 | 描述 |
|------|------|
| Submit() | 提交命令缓冲区到队列 |
| Wait() | 等待队列执行完成 |
| Signal() | 信号同步对象 |
## 枚举
| 枚举值 | 描述 |
|--------|------|
| Direct | 直接命令队列 |
| Compute | 计算命令队列 |
| Copy | 复制命令队列 |
## 示例代码
```cpp
RHICommandQueue* queue = device->GetCommandQueue(RHIQueueType::Direct);
queue->Submit(commandBuffer);
```
@see RHICommandList.md

View File

@@ -1,249 +0,0 @@
export const docs: Record<string, string> = {
'getting-started.md': `# 入门指南
**描述**: 欢迎使用 XCEngine API 文档
## 快速开始
要开始使用 XCEngine请按照以下步骤操作
1. 初始化设备
2. 创建命令队列
3. 执行渲染命令
**命名空间**: XCEngine.RHI
## 系统要求
| 要求 | 最低版本 |
|------|----------|
| DirectX 12 | 12.0 |
| OpenGL | 4.6 |
## 示例代码
\`\`\`cpp
#include <XCEngine/RHI/RHIDevice.h>
int main() {
auto device = RHIDevice::Create();
device->Initialize();
return 0;
}
\`\`\`
@see RHIDevice.md
`,
'RHIDevice.md': `# RHIDevice 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: RHI 设备抽象基类,提供图形设备创建和管理功能。
## 公共方法
| 方法 | 描述 |
|------|------|
| Create() | 创建 RHI 设备实例 |
| Initialize() | 初始化图形设备 |
| Shutdown() | 关闭并释放设备资源 |
## 属性
| 属性 | 描述 |
|------|------|
| capabilities | 获取硬件能力检测结果 |
| deviceName | 获取设备名称 |
## 示例代码
\`\`\`cpp
using namespace XCEngine::RHI;
class MyDevice {
void Init() {
auto device = RHIDevice::Create();
device->Initialize();
}
}
\`\`\`
@see RHICommandQueue.md
`,
'RHICommandQueue.md': `# RHICommandQueue 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: 命令队列抽象,用于提交渲染命令到 GPU。
## 公共方法
| 方法 | 描述 |
|------|------|
| Submit() | 提交命令缓冲区到队列 |
| Wait() | 等待队列执行完成 |
| Signal() | 信号同步对象 |
## 枚举
| 枚举值 | 描述 |
|--------|------|
| Direct | 直接命令队列 |
| Compute | 计算命令队列 |
| Copy | 复制命令队列 |
## 示例代码
\`\`\`cpp
RHICommandQueue* queue = device->GetCommandQueue(RHIQueueType::Direct);
queue->Submit(commandBuffer);
\`\`\`
@see RHICommandList.md
`,
'RHICommandList.md': `# RHICommandList 类
**命名空间**: XCEngine.RHI
**类型**: class
**继承**: IRefCounter
**描述**: 命令列表抽象,用于记录渲染命令。
## 公共方法
| 方法 | 描述 |
|------|------|
| Reset() | 重置命令列表 |
| Close() | 关闭命令列表 |
| BeginRenderPass() | 开始渲染通道 |
| EndRenderPass() | 结束渲染通道 |
| Draw() | 发出绘制调用 |
| Dispatch() | 发出计算调度 |
## 示例代码
\`\`\`cpp
cmdList->BeginRenderPass(passDesc);
cmdList->SetPipelineState(pso);
cmdList->Draw(3, 1, 0, 0);
cmdList->EndRenderPass();
cmdList->Close();
\`\`\`
@see RHIDevice.md
`,
'RHITexture.md': `# RHITexture 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: 纹理资源抽象,用于存储图像数据。
## 公共方法
| 方法 | 描述 |
|------|------|
| Create() | 创建纹理资源 |
| Destroy() | 销毁纹理资源 |
| Map() | 映射纹理数据到 CPU |
| Unmap() | 取消映射 |
## 纹理属性
| 属性 | 描述 |
|------|------|
| width | 纹理宽度 |
| height | 纹理高度 |
| format | 纹理格式 |
| mipLevels | Mipmap 级别数 |
## 纹理格式枚举
| 格式 | 描述 |
|------|------|
| R8G8B8A8_UNORM | 8位 RGBA |
| R32_FLOAT | 32位单通道浮点 |
| D32_FLOAT | 32位深度 |
| BC7_UNORM | BC7 压缩格式 |
## 使用示例
\`\`\`cpp
RHITextureDesc desc;
desc.width = 1024;
desc.height = 1024;
desc.format = RHIFormat::R8G8B8A8_UNORM;
desc.mipLevels = 1;
auto texture = device->CreateTexture(desc);
\`\`\`
@see RHIDevice.md
`,
'RHIBuffer.md': `# RHIBuffer 类
**命名空间**: XCEngine.RHI
**类型**: class
**描述**: 缓冲区资源抽象,用于存储顶点、索引、常量等数据。
## 缓冲区类型
| 类型 | 描述 |
|------|------|
| VertexBuffer | 顶点缓冲区 |
| IndexBuffer | 索引缓冲区 |
| ConstantBuffer | 常量缓冲区 |
| ReadBackBuffer | 回读缓冲区 |
## 公共方法
| 方法 | 描述 |
|------|------|
| Create() | 创建缓冲区 |
| Map() | 映射到 CPU 内存 |
| Unmap() | 取消映射 |
| SetData() | 设置数据 |
## 属性
| 属性 | 描述 |
|------|------|
| size | 缓冲区大小 |
| usage | 缓冲区用途 |
| stride | 数据步长 |
## 使用示例
\`\`\`cpp
RHIBufferDesc vbDesc;
vbDesc.size = sizeof(Vertex) * vertexCount;
vbDesc.usage = RHIBufferUsage::VertexBuffer;
vbDesc.stride = sizeof(Vertex);
auto vertexBuffer = device->CreateBuffer(vbDesc);
vertexBuffer->SetData(vertices.data());
\`\`\`
## 性能提示
- 对于静态数据,使用 Immutable 模式
- 频繁更新的数据使用 Dynamic 模式
- 大缓冲区考虑使用分页上传
@see RHIDevice.md @see RHICommandList.md
`,
}

View File

@@ -1,34 +0,0 @@
# 入门指南
**描述**: 欢迎使用 XCEngine API 文档
## 快速开始
要开始使用 XCEngine请按照以下步骤操作
1. 初始化设备
2. 创建命令队列
3. 执行渲染命令
**命名空间**: XCEngine.RHI
## 系统要求
| 要求 | 最低版本 |
|------|----------|
| DirectX 12 | 12.0 |
| OpenGL | 4.6 |
## 示例代码
```cpp
#include <XCEngine/RHI/RHIDevice.h>
int main() {
auto device = RHIDevice::Create();
device->Initialize();
return 0;
}
```
@see RHIDevice.md

View File

@@ -0,0 +1,65 @@
# 认证
**类型**: module
**描述**: 身份验证和授权相关接口。
## 概述
本 API 使用 Bearer Token 进行身份验证。所有请求都需要在 Header 中携带有效的 Token。
## 登录
### 请求
```http
POST /api/auth/login
Content-Type: application/json
{
"username": "user@example.com",
"password": "your-password"
}
```
### 响应
```json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"user": {
"id": "123",
"name": "张三",
"email": "user@example.com"
}
}
```
## 刷新 Token
### 请求
```http
POST /api/auth/refresh
Authorization: Bearer <current_token>
```
### 响应
```json
{
"token": "new_token_here",
"expiresIn": 3600
}
```
## 错误码
| 状态码 | 描述 |
|--------|------|
| 401 | 用户名或密码错误 |
| 403 | 账户已被禁用 |
| 429 | 请求过于频繁 |
@see users.md

85
src/docs/errors.md Normal file
View File

@@ -0,0 +1,85 @@
# 错误处理
**类型**: module
**描述**: API 错误响应格式和错误码定义。
## 错误响应格式
所有错误响应都遵循以下 JSON 格式:
```json
{
"error": {
"code": "ERROR_CODE",
"message": "错误描述信息",
"details": {}
}
}
```
## 常见错误码
| 错误码 | HTTP 状态码 | 描述 |
|--------|-------------|------|
| VALIDATION_ERROR | 400 | 请求参数验证失败 |
| UNAUTHORIZED | 401 | 未认证或 Token 无效 |
| FORBIDDEN | 403 | 无权限访问 |
| NOT_FOUND | 404 | 资源不存在 |
| RATE_LIMITED | 429 | 请求频率超限 |
| INTERNAL_ERROR | 500 | 服务器内部错误 |
## 错误详情
### VALIDATION_ERROR
```json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求参数验证失败",
"details": {
"email": "邮箱格式不正确",
"password": "密码长度至少为 8 位"
}
}
}
```
### NOT_FOUND
```json
{
"error": {
"code": "NOT_FOUND",
"message": "用户不存在",
"details": {
"resource": "User",
"id": "12345"
}
}
}
```
## 重试策略
当遇到 429 或 5xx 错误时,建议采用指数退避策略:
```javascript
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (!response.ok && i < retries - 1) {
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
continue;
}
return response;
} catch (e) {
if (i === retries - 1) throw e;
}
}
}
```
@see authentication.md

View File

@@ -0,0 +1,28 @@
# 入门指南
**描述**: 欢迎使用 API 文档
## 快速开始
要开始使用本 API请按照以下步骤操作
1. 获取 API 密钥
2. 了解认证方式
3. 调用接口
## 系统要求
| 要求 | 最低版本 |
|------|----------|
| HTTP | 1.1 |
| TLS | 1.2 |
## 示例代码
```javascript
const response = await fetch('/api/users');
const data = await response.json();
console.log(data);
```
@see authentication.md

View File

@@ -0,0 +1,19 @@
# 安装指南
## 环境要求
- Node.js 18+
- npm 9+
## 安装步骤
```bash
npm install
npm run dev
```
## 配置
`.env` 文件中配置 API 地址。
@see ../getting-started.md

112
src/docs/users.md Normal file
View File

@@ -0,0 +1,112 @@
# 用户管理
**命名空间**: api
**类型**: module
**描述**: 用户 CRUD 操作接口。
## 获取用户列表
获取所有用户的列表,支持分页和筛选。
### 请求
```http
GET /api/users?page=1&limit=20&status=active
```
### 查询参数
| 参数 | 类型 | 描述 |
|------|------|------|
| page | number | 页码,默认 1 |
| limit | number | 每页数量,默认 20 |
| status | string | 筛选状态active, inactive, all |
### 响应
```json
{
"data": [
{
"id": "1",
"name": "张三",
"email": "zhangsan@example.com",
"status": "active",
"createdAt": "2024-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"totalPages": 5
}
}
```
## 创建用户
### 请求
```http
POST /api/users
Content-Type: application/json
{
"name": "",
"email": "lisi@example.com",
"password": "secure-password"
}
```
### 响应
```json
{
"id": "2",
"name": "李四",
"email": "lisi@example.com",
"status": "active",
"createdAt": "2024-01-15T00:00:00Z"
}
```
## 获取单个用户
### 请求
```http
GET /api/users/:id
```
### 路径参数
| 参数 | 类型 | 描述 |
|------|------|------|
| id | string | 用户 ID |
## 更新用户
### 请求
```http
PUT /api/users/:id
Content-Type: application/json
{
"name": "",
"status": "inactive"
}
```
## 删除用户
### 请求
```http
DELETE /api/users/:id
```
@see authentication.md @see errors.md

View File

@@ -180,8 +180,8 @@ export function buildFileTree(files: string[], basePath: string): import('./type
const tree: Map<string, import('./types').DocFile> = new Map() const tree: Map<string, import('./types').DocFile> = new Map()
for (const file of files) { for (const file of files) {
const relativePath = file.replace(basePath, '').replace(/^[/\\]/, '') const relativePath = file.split('/').filter(Boolean).join('/')
const parts = relativePath.split(/[/\\]/) const parts = relativePath.split('/')
let currentPath = '' let currentPath = ''
for (let i = 0; i < parts.length; i++) { for (let i = 0; i < parts.length; i++) {

1
src/vite-env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="vite/client" />