初始化提交

This commit is contained in:
2025-10-03 16:49:53 +08:00
parent 157ca32e2d
commit bdd67a65fa
1066 changed files with 373311 additions and 261 deletions

169
IFLOW.md Normal file
View File

@@ -0,0 +1,169 @@
# Pandona Engine (PE) - IFLOW 上下文
## 项目概述
Pandona Engine (PE) 是一个功能完整的 JavaScript 2D 游戏引擎,基于 DOM 实现,用于创建各种类型的 2D 游戏和动画。现在支持现代编译时架构,使用 Vite 作为构建工具。
## 核心特性
1. **完整的游戏开发功能**支持创建各种类型的2D游戏
2. **模块化架构**:核心功能拆分为独立模块,易于扩展和维护
3. **插件系统**:支持自定义插件扩展引擎功能
4. **多种元素类型**支持精灵、容器、文本、HTML 和 SVG 元素
5. **高级动画系统**:内置补间动画、帧动画、动画序列和时间轴
6. **音频管理**:支持音效和背景音乐播放
7. **场景管理**:支持多场景切换和过渡效果
8. **事件系统**:增强的事件总线,支持全局和局部事件
9. **内存管理**:完善的资源清理机制
10. **碰撞检测**:矩形碰撞检测系统
11. **输入处理**:键盘和鼠标输入处理
12. **UI系统**按钮、标签、图像、滑块等UI组件
13. **摄像机系统**:支持跟随、缩放、旋转和震动效果
14. **粒子系统**:创建各种视觉效果
15. **组件系统**:基于组件的游戏对象系统
16. **游戏循环**:固定时间步长的游戏循环
17. **编译时架构**:使用 Vite 构建工具,支持热重载和代码优化
## 项目结构
```
cssEngine/
├── index.html # 主页面
├── main.js # 应用入口文件
├── vite.config.js # Vite配置文件
├── package.json # 项目配置
├── README.md # 项目说明文档
├── IFLOW.md # IFLOW上下文文件
├── public/ # 静态资源
│ └── sprite.css # 精灵样式文件
├── style/ # 全局样式
│ └── base.less # 基础样式
├── examples/ # 示例项目
│ ├── spaceShooter.js # 太空射击游戏示例
│ └── spaceShooter.html # 示例游戏HTML文件
├── src/
│ ├── core/ # 核心模块
│ │ └── Engine.js # 引擎主类
│ ├── elements/ # 元素类
│ │ ├── BaseElement.js
│ │ ├── Sprite.js
│ │ ├── Box.js
│ │ ├── TextElement.js
│ │ ├── HtmlElement.js
│ │ ├── SvgElement.js
│ │ ├── GameObject.js
│ │ └── Component.js
│ ├── managers/ # 管理器
│ │ ├── ResourceManager.js
│ │ ├── SceneManager.js
│ │ ├── AudioManager.js
│ │ ├── TransitionManager.js
│ │ └── Camera.js
│ ├── animation/ # 动画系统
│ │ ├── Tween.js
│ │ ├── AnimationSystem.js
│ │ └── AnimationController.js
│ ├── effects/ # 特效系统
│ │ └── ParticleSystem.js
│ ├── ui/ # UI系统
│ │ └── UI.js
│ ├── utils/ # 工具类
│ │ └── EventBus.js # 事件总线
│ └── scenes/ # 场景目录
│ └── index/ # 主场景
│ ├── index.js # 场景结构和逻辑
│ └── index.less # 场景样式
└── test/ # 测试文件
└── ... # 各模块测试
```
## 核心类和API
### Engine 引擎主类
- `constructor(element, options)` - 创建引擎实例
- `use(plugin, options)` - 安装插件
- `load(src)` - 加载资源
- `loadSound(name, src, options)` - 加载音效
- `loadMusic(name, src, options)` - 加载音乐
- `playSound(name, options)` - 播放音效
- `playMusic(name, options)` - 播放音乐
- `createScene(name)` - 创建场景
- `switchScene(name)` - 切换场景
- `tween(target, duration, easing)` - 创建补间动画
- `createFrameAnimation(name, frames, frameRate)` - 创建帧动画
- `playFrameAnimation(sprite, animationName, loop)` - 播放帧动画
- `addGameObject(obj)` - 添加游戏对象
- `removeGameObject(obj)` - 移除游戏对象
- `checkCollision(obj1, obj2)` - 碰撞检测
- `setCameraPosition(x, y)` - 设置摄像机位置
- `follow(target, offset)` - 跟随目标
- `setCameraZoom(zoom)` - 设置摄像机缩放
- `shakeCamera(intensity, duration)` - 屏幕震动
- `addUIElement(element)` - 添加UI元素
- `removeUIElement(element)` - 移除UI元素
### GameObject 游戏对象基类
- `setPosition(x, y)` - 设置位置
- `setSize(width, height)` - 设置尺寸
- `setRotation(rotation)` - 设置旋转角度
- `setScale(scaleX, scaleY)` - 设置缩放
- `setVisible(visible)` - 显示/隐藏
- `addChild(child)` - 添加子对象
- `removeChild(child)` - 移除子对象
- `addComponent(name, component)` - 添加组件
- `getComponent(name)` - 获取组件
- `removeComponent(name)` - 移除组件
### Component 组件基类
- `onAdd(gameObject)` - 当组件被添加到游戏对象时调用
- `onRemove(gameObject)` - 当组件从游戏对象中移除时调用
- `update(deltaTime)` - 更新方法
- `onDestroy(gameObject)` - 销毁方法
### 预定义组件
- `PhysicsComponent` - 物理组件
- `ColliderComponent` - 碰撞组件
- `AnimationComponent` - 动画组件
- `InputComponent` - 输入组件
- `LifecycleComponent` - 生命周期组件
### UI组件
- `UIButton` - 按钮
- `UILabel` - 标签
- `UIImage` - 图像
- `UISlider` - 滑块
## 编译时架构
### 构建工具
- 使用 Vite 作为构建工具
- 支持 ES6 模块系统
- 支持 Less CSS 预处理器
- 支持代码压缩和优化
- 支持开发服务器热重载
### 开发命令
```bash
# 启动开发服务器(热重载)
npm run dev
# 构建生产版本
npm run build
# 预览构建结果
npm run preview
```
### 场景化开发
- 使用 `src/scenes/` 目录组织不同场景
- 每个场景包含 `index.js`(场景逻辑)和 `index.less`(场景样式)
- 支持类似HTML的标签语法定义场景结构
## 开发约定
1. 使用ES6模块系统
2. 遵循面向对象编程原则
3. 使用JSDoc注释重要函数和类
4. 保持代码简洁和可读性
5. 遵循现有的代码风格和命名约定
6. 使用 Less 作为 CSS 预处理器
7. 遵循场景化组织结构
## 示例项目
项目包含一个完整的太空射击游戏示例,展示了引擎的各种功能,位于 `examples/spaceShooter.js``examples/spaceShooter.html`

889
README.md
View File

@@ -1,20 +1,107 @@
# DOM 渲染引擎
# Pandona Engine (PE)
这个轻量级 JavaScript 渲染引擎使用 DOM 元素创建 2D 场景支持精灵、容器、文本、HTML 和 SVG 等元素,提供简单直观的 API 进行场景管理
一个功能完整的 JavaScript 2D 游戏引擎,基于 DOM 实现,用于创建各种类型的 2D 游戏和动画
## 功能特性
## 编译时架构
- 🎭 创建可自定义尺寸的舞台容器
- 🖼️ 支持加载和显示图像精灵
- 📦 创建容器元素用于分组管理
- ✏️ 文本元素创建和样式设置
- 🌐 支持原生 HTML 内容嵌入
- 🎨 SVG 矢量图形支持
- 📍 精确控制元素位置和尺寸
- 🎭 简单的事件绑定机制
Pandona Engine 现在支持现代编译时架构,使用 Vite 作为构建工具,支持:
- ES6 模块系统
- Less CSS 预处理器
- 代码压缩和优化
- 开发服务器热重载
- 场景化组织结构
## 简化版API (推荐)
为了让开发者更容易上手Pandona Engine提供了简化版API参考了PixiJS、Egret和Vue 3的设计理念让代码更加简洁易读。
## 特性
- 🎮 **完整的游戏开发功能**支持创建各种类型的2D游戏
- 🌟 **模块化架构**:核心功能拆分为独立模块,易于扩展和维护
- 🌟 **插件系统**:支持自定义插件扩展引擎功能
- 🎨 **多种元素类型**支持精灵、容器、文本、HTML 和 SVG 元素
- 🌈 **高级动画系统**:内置补间动画、帧动画、动画序列和时间轴
- 🎵 **音频管理**:支持音效和背景音乐播放
- 🌍 **场景管理**:支持多场景切换和过渡效果
- 📡 **事件系统**:增强的事件总线,支持全局和局部事件
- 🧹 **内存管理**:完善的资源清理机制
- 🎯 **碰撞检测**:矩形碰撞检测系统
- 🎮 **输入处理**:键盘和鼠标输入处理
- 📱 **UI系统**按钮、标签、图像、滑块等UI组件
- 🎥 **摄像机系统**:支持跟随、缩放、旋转和震动效果
-**粒子系统**:创建各种视觉效果
- ⚙️ **组件系统**:基于组件的游戏对象系统
- 📈 **游戏循环**:固定时间步长的游戏循环
## 安装
```bash
# 克隆项目
git clone <repository-url>
# 安装依赖
npm install
```
## 快速开始
### 开发模式
```bash
# 启动开发服务器
npm run dev
```
### 构建项目
```bash
# 构建生产版本
npm run build
# 预览构建结果
npm run preview
```
### 简化版API用法 (推荐)
```javascript
// 创建引擎实例
const game = PE.createEngine('#game-container', {
width: 800,
height: 600,
background: '#f0f0f0'
});
// 加载资源
game.assets.load('character.png')
.then(img => {
// 创建精灵 (链式调用)
const player = game.create('sprite', {
image: img,
x: 100,
y: 100,
width: 50,
height: 50
}).position(100, 100)
.size(50, 50)
.on('click', () => {
player.position(200, 150);
});
// 添加到舞台
game.stage.add(player);
// 创建移动动画
game.animate(player, { x: 300, y: 200 }, 1000, {
easing: 'easeInOutQuad',
onComplete: () => {
console.log('动画完成!');
}
});
});
```
### 基本用法
```javascript
@@ -26,10 +113,10 @@ const game = new Engine('#game-container', {
});
// 加载资源
game.Load('character.png')
game.load('character.png')
.then(img => {
// 创建精灵
const player = game.CreateSprite(img, 100, 100, 50, 50);
const player = game.createSprite(img, 100, 100, 50, 50);
// 添加到舞台
game.stage.add(player);
@@ -41,60 +128,465 @@ game.Load('character.png')
});
```
### 使用动画系统
#### 简化版API
```javascript
// 创建移动动画
game.animate(player, { x: 300, y: 200 }, 1000, {
easing: 'easeInOutQuad',
onUpdate: (progress) => {
console.log(`动画进度: ${Math.round(progress * 100)}%`);
},
onComplete: () => {
console.log('动画完成!');
}
});
// 创建帧动画
const walkAnimation = game.animate.frame(player, [
'walk1.png', 'walk2.png', 'walk3.png'
], 10, { loop: true });
// 播放帧动画
walkAnimation.play();
```
#### 基本用法
```javascript
// 创建移动动画
game.tween(player, 1000, 'easeInOutQuad')
.to({ x: 300, y: 200 })
.onUpdate(progress => {
console.log(`动画进度: ${Math.round(progress * 100)}%`);
})
.onComplete(() => {
console.log('动画完成!');
})
.start();
```
### 使用场景管理
#### 简化版API
```javascript
// 创建场景
const menuScene = game.scene.create('menu');
const gameScene = game.scene.create('game');
// 切换场景
game.scene.switch('menu');
// 带过渡效果的场景切换
game.scene.switch('game', {
transition: {
type: 'fade',
duration: 1000
}
});
```
#### 基本用法
```javascript
// 创建场景
const menuScene = game.createScene('menu');
const gameScene = game.createScene('game');
// 切换场景
game.switchScene('menu');
```
## API 参考
### 引擎初始化
#### 简化版API
```javascript
const engine = PE.createEngine(elementSelector, options);
```
#### 基本用法
```javascript
const engine = new Engine(elementSelector, options);
```
| 参数 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `elementSelector` | string | `'body'` | 挂载舞台的 DOM 选择器 |
| `options.width` | number | 800 | 舞台宽度(px) |
| `options.height` | number | 600 | 舞台高度(px) |
| `options.background` | string | `'transparent'` | 舞台背景色 |
| `options.fps` | number | 60 | 游戏循环帧率 |
### 插件系统
```javascript
// 安装插件
engine.use(YourPlugin, options);
```
### 资源加载
#### 简化版API
```javascript
engine.Load(imageUrl)
// 加载单个资源
game.assets.load(imageUrl)
.then(img => { /* 使用图像 */ })
.catch(error => { /* 处理错误 */ });
// 批量加载资源
game.assets.load([imageUrl1, imageUrl2, imageUrl3])
.then(resources => { /* 使用资源 */ })
.catch(error => { /* 处理错误 */ });
// 预加载资源并显示进度
game.assets.preload([
'image1.png',
'image2.png',
'sound1.wav'
], (progress, loaded, total) => {
console.log(`加载进度: ${Math.round(progress * 100)}% (${loaded}/${total})`);
}).then(() => {
console.log('所有资源加载完成!');
});
```
#### 基本用法
```javascript
engine.load(imageUrl)
.then(img => { /* 使用图像 */ })
.catch(error => { /* 处理错误 */ });
```
### 音频管理
```javascript
// 加载音效
engine.loadSound('shoot', 'sounds/shoot.wav');
// 播放音效
engine.playSound('shoot');
// 加载音乐
engine.loadMusic('background', 'sounds/bg.mp3');
// 播放音乐
engine.playMusic('background', { loop: true });
```
### 动画系统
#### 简化版API
```javascript
// 创建补间动画
game.animate(target, { x: 100, y: 200 }, 1000, {
easing: 'easeInOutQuad',
delay: 500,
repeat: 2,
yoyo: true,
onUpdate: (progress) => { /* 更新回调 */ },
onComplete: () => { /* 完成回调 */ }
});
// 创建并行动画
const parallelAnimation = game.animate.parallel([
game.animate(target1, { x: 100 }, 1000),
game.animate(target2, { y: 200 }, 1000)
]);
parallelAnimation.play();
// 创建链式动画
const chainAnimation = game.animate.chain([
game.animate(target, { x: 100 }, 1000),
game.animate(target, { y: 200 }, 1000)
]);
chainAnimation.play();
```
#### 基本用法
```javascript
// 创建补间动画
engine.tween(target, duration, easing)
.to({ x: 100, y: 200 })
.onUpdate(callback)
.onComplete(callback)
.start();
// 创建帧动画
engine.createFrameAnimation('walk', [
'walk1.png', 'walk2.png', 'walk3.png'
], 10);
// 播放帧动画
engine.playFrameAnimation(sprite, 'walk', true);
```
支持的缓动函数:
- `linear`
- `easeInQuad`
- `easeOutQuad`
- `easeInOutQuad`
- `easeInCubic`
- `easeOutCubic`
- `easeInOutCubic`
- `easeInQuart`
- `easeOutQuart`
- `easeInOutQuart`
- `easeInQuint`
- `easeOutQuint`
- `easeInOutQuint`
- `easeInSine`
- `easeOutSine`
- `easeInOutSine`
- `easeInExpo`
- `easeOutExpo`
- `easeInOutExpo`
- `easeInCirc`
- `easeOutCirc`
- `easeInOutCirc`
- `easeInBack`
- `easeOutBack`
- `easeInOutBack`
- `easeInElastic`
- `easeOutElastic`
- `easeInOutElastic`
- `easeInBounce`
- `easeOutBounce`
- `easeInOutBounce`
### 场景管理
#### 简化版API
```javascript
// 创建场景
const scene = engine.scene.create(name);
// 切换场景
engine.scene.switch(name);
// 场景过渡效果
engine.scene.transition.fade('game', 1000);
engine.scene.transition.slide('game', 'left', 500);
engine.scene.transition.zoom('game', 1000);
engine.scene.transition.flip('game', 1000);
```
#### 基本用法
```javascript
// 创建场景
const scene = engine.createScene(name);
// 切换场景
engine.switchScene(name);
// 场景过渡效果
engine.transitionFade('game', 1000);
engine.transitionSlide('game', 'left', 500);
```
### 摄像机系统
#### 简化版API
```javascript
// 设置摄像机位置 (带动画)
engine.camera.move(400, 300, 1000).then(() => {
console.log('摄像机移动完成');
});
// 跟随目标
engine.camera.follow(player, { x: 0, y: 100 });
// 摄像机缩放 (带动画)
engine.camera.zoom(1.5, 1000).then(() => {
console.log('摄像机缩放完成');
});
// 摄像机旋转 (带动画)
engine.camera.rotate(PE.PI, 1000).then(() => {
console.log('摄像机旋转完成');
});
// 屏幕震动
engine.camera.shake(10, 500);
```
#### 基本用法
```javascript
// 设置摄像机位置
engine.setCameraPosition(400, 300);
// 跟随目标
engine.follow(player, { x: 0, y: 100 });
// 摄像机缩放
engine.setCameraZoom(1.5);
// 屏幕震动
engine.shakeCamera(10, 500);
```
### UI系统
#### 简化版API
```javascript
// 创建按钮 (链式调用)
const button = engine.ui.create('button', {
x: 100,
y: 100,
width: 200,
height: 50,
text: '点击我'
}).position(100, 100)
.size(200, 50)
.background('#007acc')
.hover('#005a99')
.pressed('#003d66')
.color('#ffffff')
.on('click', () => {
console.log('按钮被点击了!');
});
engine.ui.add(button);
// 创建标签 (链式调用)
const label = engine.ui.create('label', {
x: 100,
y: 200,
text: 'Hello World'
}).position(100, 200)
.text('Hello World')
.font('20px Arial')
.color('#ffffff');
engine.ui.add(label);
```
#### 基本用法
```javascript
// 创建按钮
const button = new UIButton(100, 100, 200, 50, '点击我');
button.on('click', () => {
console.log('按钮被点击了!');
});
engine.addUIElement(button);
// 创建标签
const label = new UILabel(100, 200, 'Hello World');
engine.addUIElement(label);
```
### 创建元素
#### 精灵 (CreateSprite)
#### 简化版API
```javascript
const sprite = engine.CreateSprite(imgElement, x, y, width, height);
// 创建精灵 (链式调用)
const sprite = engine.create('sprite', {
image: imgElement,
x: 100,
y: 100,
width: 50,
height: 50
}).position(100, 100)
.size(50, 50)
.alpha(0.8)
.scale(1.2)
.rotate(PE.PI_2)
.on('click', () => {
console.log('精灵被点击了!');
});
// 创建容器 (链式调用)
const container = game.create('box', {
x: 50,
y: 50,
width: 200,
height: 200
}).position(50, 50)
.size(200, 200)
.addChild(sprite);
// 创建文本 (链式调用)
const text = game.create('text', {
text: 'Hello World',
x: 100,
y: 100
}).position(100, 100)
.text('Hello World')
.font('bold 24px Arial')
.color('#ff0000')
.align('center');
// 创建HTML元素 (链式调用)
const html = game.create('html', {
html: '<button>Click</button>',
x: 300,
y: 200
}).position(300, 200)
.html('<button>Click</button>');
// 创建SVG元素 (链式调用)
const svg = game.create('svg', {
x: 400,
y: 300,
width: 100,
height: 100,
content: '<circle cx="50" cy="50" r="40"/>'
}).position(400, 300)
.size(100, 100)
.content('<circle cx="50" cy="50" r="40"/>');
```
#### 基本用法
##### 精灵 (createSprite)
```javascript
const sprite = engine.createSprite(imgElement, x, y, width, height);
```
- 支持位置和尺寸控制
- 支持事件绑定
#### 容器 (CreateBox)
##### 容器 (createBox)
```javascript
const container = engine.CreateBox(x, y, width, height);
const container = engine.createBox(x, y, width, height);
```
- 用于元素分组管理
- 支持嵌套结构
#### 文本 (CreateText)
##### 文本 (createText)
```javascript
const text = engine.CreateText('Hello', 50, 50);
const text = engine.createText('Hello', 50, 50);
text.setColor('#ff0000');
text.setFont('bold 24px Arial');
```
#### HTML 元素 (CreateHtml)
##### HTML 元素 (createHtml)
```javascript
const html = engine.CreateHtml('<button>Click</button>', 300, 200);
const html = engine.createHtml('<button>Click</button>', 300, 200);
```
#### SVG 元素 (CreateSvg)
##### SVG 元素 (createSvg)
```javascript
const svg = engine.CreateSvg(400, 300, 100, 100, '<circle cx="50" cy="50" r="40"/>');
const svg = engine.createSvg(400, 300, 100, 100, '<circle cx="50" cy="50" r="40"/>');
```
### 元素通用方法
#### 简化版API
所有创建的元素支持以下链式调用方法:
| 方法 | 说明 | 示例 |
|------|------|------|
| `position(x, y)` | 设置元素位置 | `sprite.position(150, 200)` |
| `size(width, height)` | 设置元素尺寸 | `box.size(100, 80)` |
| `alpha(value)` | 设置透明度 | `sprite.alpha(0.8)` |
| `scale(x, y)` | 设置缩放 | `sprite.scale(1.2, 1.2)` |
| `rotate(angle)` | 设置旋转 | `sprite.rotate(PE.PI_2)` |
| `add(child)` | 添加子元素 | `container.add(sprite)` |
| `remove(child)` | 移除子元素 | `container.remove(sprite)` |
| `on(event, callback)` | 绑定事件 | `sprite.on('click', handleClick)` |
| `visible(visible)` | 显示/隐藏元素 | `sprite.visible(false)` |
#### 基本用法
所有创建的元素支持以下方法:
| 方法 | 说明 | 示例 |
@@ -109,57 +601,342 @@ const svg = engine.CreateSvg(400, 300, 100, 100, '<circle cx="50" cy="50" r="40"
| `hide()` | 隐藏元素 | `sprite.hide()` |
| `show()` | 显示元素 | `sprite.show()` |
### 游戏对象系统
#### 简化版API
```javascript
// 创建游戏对象 (链式调用)
const player = engine.gameobject.create({
x: 100,
y: 100,
width: 50,
height: 50,
name: 'player',
components: {
physics: PE.components.physics({
velocityX: 100,
velocityY: 0
}),
collider: PE.components.collider({
width: 50,
height: 50
})
},
events: {
update: (deltaTime) => {
// 更新逻辑
}
}
}).position(100, 100)
.size(50, 50)
.addComponent('animation', PE.components.animation())
.on('update', (deltaTime) => {
// 更新逻辑
});
// 添加到引擎
engine.addGameObject(player);
```
#### 基本用法
```javascript
// 创建游戏对象
const player = new GameObject(100, 100, 50, 50);
// 添加组件
player.addComponent('physics', new PhysicsComponent({
velocityX: 100,
velocityY: 0
}));
player.addComponent('collider', new ColliderComponent({
width: 50,
height: 50
}));
// 添加到引擎
engine.addGameObject(player);
```
### 碰撞检测
```javascript
// 检查两个对象是否碰撞
if (engine.checkCollision(player, enemy)) {
console.log('发生碰撞!');
}
```
### 音频系统
#### 简化版API
```javascript
// 加载音频
engine.sound.load('shoot', 'sounds/shoot.wav');
engine.sound.load('background', 'sounds/bg.mp3', { type: 'music' });
// 播放音频
engine.sound.play('shoot');
engine.sound.play('background', { loop: true, type: 'music' });
// 控制音量
engine.sound.volume(0.5, 'master'); // 主音量
engine.sound.volume(0.8, 'sound'); // 音效音量
engine.sound.volume(0.6, 'music'); // 音乐音量
// 停止音频
engine.sound.stop('background', 'music');
// 暂停/恢复音频
engine.sound.pause('background', 'music');
engine.sound.resume('background', 'music');
```
#### 基本用法
```javascript
// 加载音效
engine.loadSound('shoot', 'sounds/shoot.wav');
// 播放音效
engine.playSound('shoot');
// 加载音乐
engine.loadMusic('background', 'sounds/bg.mp3');
// 播放音乐
engine.playMusic('background', { loop: true });
```
### 销毁引擎
```javascript
engine.Destroy();
engine.destroy();
```
## 使用示例
## 项目结构
```
cssEngine/
├── index.html # 主页面
├── main.js # 应用入口文件
├── vite.config.js # Vite配置文件
├── package.json # 项目配置
├── README.md # 项目说明文档
├── public/ # 静态资源
│ └── sprite.css # 精灵样式文件
├── style/ # 全局样式
│ └── base.less # 基础样式
├── examples/ # 示例项目
│ ├── spaceShooter.js # 太空射击游戏示例
│ └── spaceShooter.html # 示例游戏HTML文件
├── src/
│ ├── core/ # 核心模块
│ │ ├── Engine.js # 引擎主类
│ │ └── SimplifiedEngine.js # 简化版引擎API
│ ├── elements/ # 元素类
│ │ ├── BaseElement.js
│ │ ├── Sprite.js
│ │ ├── Box.js
│ │ ├── TextElement.js
│ │ ├── HtmlElement.js
│ │ ├── SvgElement.js
│ │ ├── GameObject.js
│ │ ├── Component.js
│ │ ├── SimplifiedElements.js # 简化版元素API
│ │ ├── SimplifiedGameObject.js # 简化版游戏对象API
│ │ └── SimplifiedComponent.js # 简化版组件API
│ ├── managers/ # 管理器
│ │ ├── ResourceManager.js
│ │ ├── SceneManager.js
│ │ ├── AudioManager.js
│ │ ├── TransitionManager.js
│ │ ├── Camera.js
│ │ ├── SimplifiedResourceManager.js # 简化版资源管理API
│ │ ├── SimplifiedSceneManager.js # 简化版场景管理API
│ │ ├── SimplifiedAudioManager.js # 简化版音频管理API
│ │ └── SimplifiedCamera.js # 简化版摄像机API
│ ├── animation/ # 动画系统
│ │ ├── Tween.js
│ │ ├── AnimationSystem.js
│ │ ├── AnimationController.js
│ │ └── SimplifiedAnimation.js # 简化版动画API
│ ├── effects/ # 特效系统
│ │ ├── ParticleSystem.js
│ │ └── SimplifiedParticleSystem.js # 简化版粒子系统API
│ ├── ui/ # UI系统
│ │ ├── UI.js
│ │ └── SimplifiedUI.js # 简化版UI系统API
│ ├── utils/ # 工具类
│ │ ├── EventBus.js
│ │ └── SimplifiedEventBus.js # 简化版事件系统API
│ └── scenes/ # 场景目录
│ └── index/ # 主场景
│ ├── index.js # 场景结构和逻辑
│ └── index.less # 场景样式
└── test/ # 测试文件
└── ... # 各模块测试
```
## 开发指南
### 编译时架构开发指南
Pandona Engine 现在支持现代编译时架构,使用 Vite 作为构建工具:
```bash
# 启动开发服务器(热重载)
npm run dev
# 构建生产版本
npm run build
# 预览构建结果
npm run preview
```
### 场景化开发
使用场景化组织结构开发应用:
```
src/scenes/
├── index/ # 主场景
│ ├── index.js # 场景结构和逻辑
│ └── index.less # 场景样式
└── other-scene/ # 其他场景
├── index.js
└── index.less
```
### 简化版API开发指南
简化版API提供了更简洁的开发方式推荐新项目使用
### 创建游戏角色
```javascript
const game = new Engine('#game', {
width: 1024,
height: 768
// 使用简化版API创建游戏
const game = PE.createEngine('#game-container', {
width: 800,
height: 600,
background: '#000022'
});
game.Load('player.png').then(playerImg => {
const player = game.CreateSprite(playerImg, 512, 384, 64, 64);
// 添加角色名标签
const nameTag = game.CreateText('Player 1', 0, -20);
nameTag.setColor('gold');
nameTag.setFont('bold 14px sans-serif');
player.add(nameTag);
game.stage.add(player);
// 使用链式调用创建和配置元素
const player = game.create('sprite', {
image: playerImage,
x: 100,
y: 100,
width: 50,
height: 50
}).position(100, 100)
.size(50, 50)
.on('click', () => {
console.log('Player clicked!');
});
// 添加到舞台
game.stage.add(player);
// 创建动画
game.animate(player, { x: 300, y: 200 }, 1000, {
easing: 'easeInOutQuad',
onComplete: () => {
console.log('Animation completed!');
}
});
```
### 创建 UI 面板
### 扩展功能
可以通过插件系统扩展引擎功能:
```javascript
const panel = game.CreateBox(50, 50, 200, 300);
panel.setStyle('background: rgba(0,0,0,0.7); border-radius: 10px;');
// 定义插件
const MyPlugin = {
name: 'MyPlugin',
install(engine, options) {
// 扩展引擎功能
engine.myMethod = () => {
console.log('This is my custom method');
};
}
};
const title = game.CreateText('控制面板', 20, 20);
title.setStyle('font-size: 20px; color: white;');
const button = game.CreateHtml(
'<button style="padding: 8px 16px; background: blue; color: white;">开始</button>',
50, 80
);
panel.add(title);
panel.add(button);
game.stage.add(panel);
// 使用插件
engine.use(MyPlugin);
```
### 自定义元素
可以通过继承 `BaseElement` 创建自定义元素:
```javascript
import BaseElement from './src/elements/BaseElement.js';
class CustomElement extends BaseElement {
constructor(x, y, width, height, engineId) {
super('div', engineId);
this.setPosition(x, y);
this.setSize(width, height);
// 添加自定义逻辑
}
}
```
### 游戏对象和组件
可以通过GameObject和Component系统创建复杂的游戏对象
```javascript
import { GameObject } from './src/elements/GameObject.js';
import { PhysicsComponent, ColliderComponent } from './src/elements/Component.js';
class Player extends GameObject {
constructor(x, y) {
super(x, y, 50, 50);
// 添加物理组件
this.addComponent('physics', new PhysicsComponent({
velocityX: 0,
velocityY: 0,
friction: 0.9
}));
// 添加碰撞组件
this.addComponent('collider', new ColliderComponent({
width: 50,
height: 50
}));
}
update(deltaTime) {
super.update(deltaTime);
// 自定义更新逻辑
}
}
```
## 示例游戏
项目包含一个完整的太空射击游戏示例,展示了引擎的各种功能:
1. 游戏对象系统
2. 组件系统
3. 碰撞检测
4. 动画系统
5. 音频管理
6. 场景管理
7. UI系统
8. 摄像机系统
9. 粒子效果
10. 场景过渡效果
要运行示例游戏,请在浏览器中打开 `examples/spaceShooter.html`
## 注意事项
1. 所有元素位置基于舞台左上角(0,0)计算
2. 使用 `Destroy()` 方法会完全移除舞台并清理内存
2. 使用 `destroy()` 方法会完全移除舞台并清理内存
3. 图像元素自动添加 `user-drag: none` 防止拖动
4. 舞台默认启用硬件加速 (`transform: translateZ(0)`)
5. 每个引擎实例有唯一ID标识 (`data-engine-id`)
6. 游戏循环基于requestAnimationFrame实现
7. 坐标系统基于世界坐标和屏幕坐标的转换
这个轻量级引擎适合创建简单的 2D 界面和游戏原型,使用原生 DOM 操作,无需额外依赖。
Pandona Engine是一个功能完整的2D游戏引擎适合创建各种类型的2D游戏和动画使用原生DOM操作无需额外依赖。

26
check_example.js Normal file
View File

@@ -0,0 +1,26 @@
import { readdir, stat } from 'fs/promises';
import { join } from 'path';
async function listDirectory(path, indent = 0) {
try {
const items = await readdir(path);
for (const item of items) {
const itemPath = join(path, item);
const stats = await stat(itemPath);
const prefix = ' '.repeat(indent);
if (stats.isDirectory()) {
console.log(`${prefix}[DIR] ${item}`);
await listDirectory(itemPath, indent + 1);
} else {
console.log(`${prefix}[FILE] ${item}`);
}
}
} catch (error) {
console.error(`Error reading directory ${path}:`, error.message);
}
}
// 显示项目中的example目录结构
console.log('项目中的example目录结构:');
await listDirectory('J:\\git\\cssEngine\\template-example');

26
debug.js Normal file
View File

@@ -0,0 +1,26 @@
import PE from './src/index.js';
// 创建引擎实例
const engine = PE.create();
// 等待一段时间让场景加载
setTimeout(() => {
console.log('Engine:', engine);
console.log('Current scene:', engine.currentScene);
console.log('Stage:', engine.stage);
// 检查场景容器中的元素
if (engine.currentScene) {
console.log('Scene container:', engine.currentScene.container);
console.log('Scene elements:', engine.currentScene.elements);
console.log('Scene container children:', engine.currentScene.container.children.length);
// 遍历所有子元素
for (let i = 0; i < engine.currentScene.container.children.length; i++) {
const child = engine.currentScene.container.children[i];
console.log('Child element:', i, child.tagName, child.className, child.style.cssText);
}
} else {
console.log('No current scene loaded');
}
}, 2000);

View File

@@ -0,0 +1 @@
*{background:white}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
*{background:white}.test-sprite{width:100px;height:100px;background-color:#ff5722;border-radius:50%;cursor:pointer;position:absolute;top:50px;left:50px;border:2px solid #e64a19}.test-box{width:200px;height:50px;background-color:#2196f3;color:#fff;display:flex;align-items:center;justify-content:center;position:absolute;top:200px;left:50px;border:2px solid #1976d2;border-radius:4px;cursor:pointer}.test-text{position:absolute;top:300px;left:50px;color:#333;font-size:20px;font-weight:700}.menu-item{width:150px;height:40px;background-color:#9c27b0;color:#fff;display:flex;align-items:center;justify-content:center;position:absolute;left:50px;border:2px solid #7b1fa2;border-radius:4px;cursor:pointer;font-family:Arial,sans-serif;font-size:16px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
const e=[{path:"/sence1",title:"主场景"},{path:"/sence2",title:"测试场景"}],t="PC",s={sence:e,platform:t};export{s as default,t as platform,e as sence};

15
fetch-page.js Normal file
View File

@@ -0,0 +1,15 @@
import http from 'http';
http.get('http://localhost:3003/', (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(data.substring(0, 500));
});
}).on('error', (err) => {
console.log('Error: ' + err.message);
});

21
guide/README.md Normal file
View File

@@ -0,0 +1,21 @@
# PE引擎使用指南
欢迎使用PE引擎本指南将帮助你了解和使用PE引擎的所有功能。
## 目录
- [简介](./introduction.md)
- [快速上手](./quick-start.md)
- [基础教程](./essentials/)
- [创建应用实例](./essentials/application.md)
- [元素系统](./essentials/elements.md)
- [场景系统](./essentials/scenes.md)
- [样式处理](./essentials/styling.md)
- [事件处理](./essentials/event-handling.md)
- [生命周期](./essentials/lifecycle.md)
- [组件](./components/)
- [组件基础](./components/README.md)
- [进阶主题](./advanced/)
- [进阶概念](./advanced/README.md)
- [API参考](./api/)
- [API文档](./api/README.md)

455
guide/advanced/README.md Normal file
View File

@@ -0,0 +1,455 @@
# 进阶主题
在掌握了PE引擎的基础知识后我们可以探索一些更高级的功能和概念。这些进阶主题将帮助你构建更复杂、更高效的应用。
## 目录
- [动画系统](./animation)
- [状态管理](./state-management)
- [插件系统](./plugins)
- [性能优化](./performance)
- [测试](./testing)
- [部署](./deployment)
## 动画系统
PE引擎内置了强大的动画系统支持补间动画、帧动画和序列动画。
### 补间动画
补间动画允许你在两个状态之间创建平滑的过渡:
```javascript
// 创建补间动画
const tween = game.tween(element.element)
.to({
left: '500px',
top: '300px',
opacity: 0.5
}, 2000) // 持续2秒
.easing('easeInOutQuad') // 缓动函数
.onUpdate(() => {
console.log('动画更新')
})
.onComplete(() => {
console.log('动画完成')
})
.start()
```
### 帧动画
帧动画通过连续播放一系列图像来创建动画效果:
```javascript
// 创建帧动画
game.createFrameAnimation('walk', [
'img/walk1.png',
'img/walk2.png',
'img/walk3.png',
'img/walk4.png'
], 10) // 10帧每秒
// 播放帧动画
game.playFrameAnimation(sprite, 'walk', true) // 循环播放
```
### 序列动画
序列动画允许你按顺序播放多个动画:
```javascript
// 创建动画序列
game.createSequence('moveAndFade', [
{
target: element.element,
props: { left: '500px' },
duration: 1000
},
{
target: element.element,
props: { opacity: 0 },
duration: 500
}
])
// 播放序列动画
game.playSequence('moveAndFade')
```
## 状态管理
对于复杂应用合理的状态管理至关重要。PE引擎提供了灵活的状态管理方案。
### 全局状态
```javascript
// 创建全局状态管理器
class StateManager {
constructor() {
this.state = {}
this.listeners = {}
}
// 设置状态
setState(key, value) {
this.state[key] = value
this.notifyListeners(key, value)
}
// 获取状态
getState(key) {
return this.state[key]
}
// 订阅状态变化
subscribe(key, callback) {
if (!this.listeners[key]) {
this.listeners[key] = []
}
this.listeners[key].push(callback)
}
// 通知监听器
notifyListeners(key, value) {
if (this.listeners[key]) {
this.listeners[key].forEach(callback => callback(value))
}
}
}
// 创建全局状态管理器实例
const stateManager = new StateManager()
// 在组件中使用
stateManager.setState('user', { name: '张三', age: 25 })
stateManager.subscribe('user', (user) => {
console.log('用户信息更新:', user)
})
```
### 场景状态
每个场景也可以有自己的状态管理:
```html
<script>
// 场景内部状态
let sceneState = {
data: [],
loading: false,
error: null
}
// 更新场景状态
function updateState(newState) {
sceneState = { ...sceneState, ...newState }
updateUI()
}
// 异步数据加载
async function loadData() {
updateState({ loading: true })
try {
const data = await fetchData()
updateState({ data, loading: false })
} catch (error) {
updateState({ error: error.message, loading: false })
}
}
</script>
```
## 插件系统
PE引擎支持插件系统允许你扩展引擎的功能。
### 创建插件
```javascript
// 定义插件
const LoggerPlugin = {
name: 'LoggerPlugin',
install(engine, options) {
console.log('LoggerPlugin installed with options:', options)
// 扩展引擎功能
engine.log = function(message) {
console.log(`[PE Logger]: ${message}`)
}
// 监听引擎事件
engine.eventBus.on('sceneChanged', (data) => {
this.log(`Scene changed to: ${data.path}`)
})
}
}
// 使用插件
game.use(LoggerPlugin, { level: 'debug' })
```
### 内置插件
PE引擎提供了一些有用的内置插件
```javascript
// 路由插件
import { RouterPlugin } from 'pe-engine/plugins'
game.use(RouterPlugin, {
mode: 'history', // 或 'hash'
routes: [
{ path: '/', component: 'Home' },
{ path: '/about', component: 'About' }
]
})
// 状态管理插件
import { StatePlugin } from 'pe-engine/plugins'
game.use(StatePlugin, {
initialState: {
user: null,
theme: 'light'
}
})
```
## 性能优化
构建高性能的PE应用需要注意以下几个方面
### 1. 虚拟化列表
对于大量数据的列表渲染,使用虚拟化技术:
```javascript
// 虚拟化列表组件
class VirtualList {
constructor(container, items, itemHeight) {
this.container = container
this.items = items
this.itemHeight = itemHeight
this.visibleCount = Math.ceil(container.clientHeight / itemHeight) + 2
this.startIndex = 0
this.render()
this.bindEvents()
}
render() {
const fragment = document.createDocumentFragment()
const endIndex = Math.min(this.startIndex + this.visibleCount, this.items.length)
for (let i = this.startIndex; i < endIndex; i++) {
const itemElement = this.createItemElement(this.items[i], i)
itemElement.style.transform = `translateY(${i * this.itemHeight}px)`
fragment.appendChild(itemElement)
}
this.container.innerHTML = ''
this.container.appendChild(fragment)
}
createItemElement(item, index) {
const element = document.createElement('div')
element.className = 'list-item'
element.textContent = item.text
element.style.height = `${this.itemHeight}px`
element.style.position = 'absolute'
element.style.width = '100%'
return element
}
bindEvents() {
this.container.addEventListener('scroll', () => {
const newStartIndex = Math.floor(this.container.scrollTop / this.itemHeight)
if (newStartIndex !== this.startIndex) {
this.startIndex = newStartIndex
this.render()
}
})
}
}
```
### 2. 防抖和节流
对于频繁触发的操作,使用防抖和节流:
```javascript
// 防抖函数
function debounce(func, wait) {
let timeout
return function(...args) {
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(this, args), wait)
}
}
// 节流函数
function throttle(func, limit) {
let inThrottle
return function(...args) {
if (!inThrottle) {
func.apply(this, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
// 使用示例
const handleResize = debounce(() => {
console.log('窗口大小改变')
}, 300)
const handleScroll = throttle(() => {
console.log('页面滚动')
}, 100)
window.addEventListener('resize', handleResize)
window.addEventListener('scroll', handleScroll)
```
### 3. 懒加载
对于图片和组件,使用懒加载技术:
```javascript
// 图片懒加载
class ImageLazyLoader {
constructor() {
this.images = []
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadImage(entry.target)
this.observer.unobserve(entry.target)
}
})
})
}
loadImage(img) {
const src = img.dataset.src
if (src) {
img.src = src
img.classList.remove('lazy')
img.classList.add('loaded')
}
}
observe(img) {
this.observer.observe(img)
}
}
// 使用懒加载
const lazyLoader = new ImageLazyLoader()
document.querySelectorAll('img[data-src]').forEach(img => {
lazyLoader.observe(img)
})
```
## 测试
编写测试是确保应用质量的重要环节。
### 单元测试
使用Jest等测试框架编写单元测试
```javascript
// 测试元素创建功能
describe('Element Creation', () => {
test('should create sprite element', () => {
const sprite = game.create('sprite', { x: 100, y: 100 })
expect(sprite).toBeDefined()
expect(sprite.getX()).toBe(100)
expect(sprite.getY()).toBe(100)
})
test('should create box element', () => {
const box = game.create('box', { width: 200, height: 100 })
expect(box.getWidth()).toBe(200)
expect(box.getHeight()).toBe(100)
})
})
```
### 集成测试
测试组件和场景的集成:
```javascript
// 测试场景切换
describe('Scene Navigation', () => {
test('should navigate to about page', async () => {
// 模拟场景切换
await game.switchToPath('/about')
// 验证场景是否正确加载
const currentScene = game.getCurrentScene()
expect(currentScene.path).toBe('/about')
})
})
```
## 部署
将PE应用部署到生产环境需要注意以下几点
### 构建优化
```javascript
// vite.config.js
import { defineConfig } from 'vite'
import { resolve } from 'path'
export default defineConfig({
build: {
// 代码分割
rollupOptions: {
output: {
manualChunks: {
vendor: ['pe-engine'],
utils: ['./src/utils'],
}
}
},
// 压缩
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
},
// 静态资源处理
assetsInclude: ['**/*.pe', '**/*.less']
})
```
### 环境配置
```javascript
// 配置不同环境
const config = {
development: {
apiBaseUrl: 'http://localhost:3000',
debug: true
},
production: {
apiBaseUrl: 'https://api.example.com',
debug: false
}
}
const currentConfig = config[process.env.NODE_ENV || 'development']
```
通过以上进阶主题的学习你将能够构建更复杂、更高效的PE应用。在接下来的章节中我们将深入探讨每个主题的更多细节。

600
guide/api/README.md Normal file
View File

@@ -0,0 +1,600 @@
# API参考
本章节提供了PE引擎所有公共API的详细参考文档。
## 全局API
### PE
PE是引擎的主要入口点。
#### PE.create(options)
创建一个新的PE引擎实例。
```javascript
import PE from 'pe-engine'
const game = PE.create({
width: 800,
height: 600,
background: '#f0f0f0',
fps: 60
})
```
**参数:**
| 参数 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| options | Object | {} | 配置选项 |
| options.width | number | 800 | 舞台宽度 |
| options.height | number | 600 | 舞台高度 |
| options.background | string | '#f0f0f0' | 背景色 |
| options.fps | number | 60 | 帧率 |
**返回值:**
Engine实例
#### PE.navigateTo(path)
导航到指定路径的场景。
```javascript
PE.navigateTo('/about')
```
**参数:**
| 参数 | 类型 | 描述 |
|------|------|------|
| path | string | 目标场景路径 |
#### PE.version
获取PE引擎版本。
```javascript
console.log(PE.version) // '1.0.0'
```
### 生命周期钩子
#### onLoad(callback)
场景加载时调用的钩子。
```javascript
onLoad(() => {
console.log('场景加载完成')
})
```
#### onShow(callback)
场景显示时调用的钩子。
```javascript
onShow(() => {
console.log('场景显示')
})
```
#### onHide(callback)
场景隐藏时调用的钩子。
```javascript
onHide(() => {
console.log('场景隐藏')
})
```
#### onDestory(callback)
场景销毁时调用的钩子。
```javascript
onDestory(() => {
console.log('场景销毁')
})
```
## Engine实例API
### 元素创建
#### create(type, options)
创建元素。
```javascript
const sprite = game.create('sprite', { x: 100, y: 100 })
const box = game.create('box', { x: 200, y: 200, width: 100, height: 50 })
```
**参数:**
| 参数 | 类型 | 描述 |
|------|------|------|
| type | string | 元素类型 ('sprite', 'box', 'text', 'html', 'svg') |
| options | Object | 元素配置选项 |
**返回值:**
创建的元素实例
#### createSprite(image, x, y, width, height)
创建精灵元素。
```javascript
const sprite = game.createSprite('path/to/image.png', 100, 100, 50, 50)
```
#### createBox(x, y, width, height)
创建盒子元素。
```javascript
const box = game.createBox(100, 100, 200, 100)
```
#### createText(text, x, y)
创建文本元素。
```javascript
const text = game.createText('Hello PE!', 100, 100)
```
#### createHtml(html, x, y)
创建HTML元素。
```javascript
const htmlElement = game.createHtml('<div>HTML内容</div>', 100, 100)
```
#### createSvg(x, y, width, height, content)
创建SVG元素。
```javascript
const svgElement = game.createSvg(100, 100, 200, 200, '<circle cx="100" cy="100" r="50" />')
```
### 场景管理
#### switchToPath(path)
切换到指定路径的场景。
```javascript
await game.switchToPath('/about')
```
#### preloadScenes(paths)
预加载场景。
```javascript
await game.preloadScenes(['/home', '/about', '/contact'])
```
#### getCurrentPath()
获取当前场景路径。
```javascript
const currentPath = game.getCurrentPath()
```
#### getCurrentScene()
获取当前场景对象。
```javascript
const currentScene = game.getCurrentScene()
```
#### getSceneElement(name)
获取场景中的元素。
```javascript
const element = game.getSceneElement('my-button')
```
### 动画系统
#### tween(target, duration, easing)
创建补间动画。
```javascript
const tween = game.tween(element.element, 1000, 'easeInOutQuad')
.to({ left: '500px' })
.start()
```
#### createFrameAnimation(name, frames, frameRate)
创建帧动画。
```javascript
game.createFrameAnimation('walk', [
'img/walk1.png',
'img/walk2.png',
'img/walk3.png'
], 10)
```
#### playFrameAnimation(sprite, animationName, loop)
播放帧动画。
```javascript
game.playFrameAnimation(sprite, 'walk', true)
```
#### createSequence(name, animations)
创建动画序列。
```javascript
game.createSequence('moveAndFade', [
{ target: element.element, props: { left: '500px' }, duration: 1000 },
{ target: element.element, props: { opacity: 0 }, duration: 500 }
])
```
#### playSequence(sequenceName, loop)
播放动画序列。
```javascript
game.playSequence('moveAndFade')
```
### 资源管理
#### load(src)
加载资源。
```javascript
const image = await game.load('path/to/image.png')
```
#### loadSound(name, src, options)
加载音效。
```javascript
game.loadSound('jump', 'path/to/jump.mp3')
```
#### loadMusic(name, src, options)
加载音乐。
```javascript
game.loadMusic('bgm', 'path/to/background.mp3')
```
#### playSound(name, options)
播放音效。
```javascript
game.playSound('jump')
```
#### playMusic(name, options)
播放音乐。
```javascript
game.playMusic('bgm')
```
### 摄像机控制
#### setCameraPosition(x, y)
设置摄像机位置。
```javascript
game.setCameraPosition(100, 100)
```
#### moveCamera(x, y)
移动摄像机。
```javascript
game.moveCamera(50, 50)
```
#### setCameraZoom(zoom)
设置摄像机缩放。
```javascript
game.setCameraZoom(1.5)
```
#### follow(target, offset)
跟随目标。
```javascript
game.follow(playerSprite, { x: 0, y: -50 })
```
#### shakeCamera(intensity, duration)
摄像机震动效果。
```javascript
game.shakeCamera(10, 500)
```
### 事件系统
#### eventBus
获取事件总线实例。
```javascript
// 发送事件
game.eventBus.emit('custom-event', { data: 'example' })
// 监听事件
game.eventBus.on('custom-event', (data) => {
console.log('接收到事件:', data)
})
```
### 定时器
#### setTimeout(callback, delay)
设置定时器。
```javascript
const timer = game.setTimeout(() => {
console.log('定时器触发')
}, 1000)
```
#### clearTimeout(timer)
清除定时器。
```javascript
game.clearTimeout(timer)
```
#### setInterval(callback, interval)
设置间隔执行。
```javascript
const intervalId = game.setInterval(() => {
console.log('间隔执行')
}, 1000)
```
#### clearInterval(intervalId)
清除间隔执行。
```javascript
game.clearInterval(intervalId)
```
## 元素API
所有PE元素都具有以下通用API
### 位置和尺寸
#### setPosition(x, y)
设置元素位置。
```javascript
element.setPosition(100, 200)
```
#### setSize(width, height)
设置元素尺寸。
```javascript
element.setSize(300, 150)
```
#### getX()
获取元素X坐标。
```javascript
const x = element.getX()
```
#### getY()
获取元素Y坐标。
```javascript
const y = element.getY()
```
#### getWidth()
获取元素宽度。
```javascript
const width = element.getWidth()
```
#### getHeight()
获取元素高度。
```javascript
const height = element.getHeight()
```
### 样式操作
#### setStyle(property, value)
设置单个样式属性。
```javascript
element.setStyle('backgroundColor', '#3498db')
```
#### setStyles(styles)
设置多个样式属性。
```javascript
element.setStyles({
backgroundColor: '#3498db',
borderRadius: '8px'
})
```
#### addClass(className)
添加CSS类。
```javascript
element.addClass('my-class')
```
#### removeClass(className)
移除CSS类。
```javascript
element.removeClass('my-class')
```
#### hasClass(className)
检查是否包含CSS类。
```javascript
if (element.hasClass('my-class')) {
// 执行相应操作
}
```
### 显示控制
#### show()
显示元素。
```javascript
element.show()
```
#### hide()
隐藏元素。
```javascript
element.hide()
```
#### toggle()
切换显示状态。
```javascript
element.toggle()
```
### 文本元素特有API
#### setText(text)
设置文本内容。
```javascript
textElement.setText('新的文本内容')
```
### HTML元素特有API
#### setHtml(html)
设置HTML内容。
```javascript
htmlElement.setHtml('<div>新的HTML内容</div>')
```
## 插件API
### use(plugin, options)
安装插件。
```javascript
game.use(MyPlugin, { option1: 'value1' })
```
## 配置选项
### 引擎配置
```javascript
const game = PE.create({
// 舞台尺寸
width: 800,
height: 600,
// 背景色
background: '#f0f0f0',
// 帧率
fps: 60
})
```
### 资源加载配置
```javascript
game.loadSound('jump', 'path/to/jump.mp3', {
volume: 0.5,
loop: false
})
game.loadMusic('bgm', 'path/to/background.mp3', {
volume: 0.8,
loop: true
})
```
### 动画配置
```javascript
const tween = game.tween(element.element, 1000, 'easeInOutQuad')
.to({ left: '500px' }, 1000)
.delay(500) // 延迟500ms
.onStart(() => console.log('动画开始'))
.onUpdate(() => console.log('动画更新'))
.onComplete(() => console.log('动画完成'))
.start()
```
通过以上API参考你可以详细了解PE引擎提供的所有功能和使用方法。在实际开发中建议结合示例代码和文档说明来更好地理解和使用这些API。

715
guide/components/README.md Normal file
View File

@@ -0,0 +1,715 @@
# 组件
组件是PE引擎中可复用的UI构建块它们将模板、样式和逻辑封装在一起使得代码更加模块化和可维护。
## 什么是组件?
组件是PE应用中独立的、可复用的代码单元它包含
1. **模板** - 定义组件的结构和元素
2. **样式** - 定义组件的外观和样式
3. **逻辑** - 定义组件的行为和功能
## 创建组件
在PE中组件可以通过创建独立的目录结构来定义
```
components/
├── button/
│ ├── index.pe
│ └── index.less
├── card/
│ ├── index.pe
│ └── index.less
└── modal/
├── index.pe
└── index.less
```
### 组件模板文件
组件模板文件与场景模板文件结构相同:
```html
<!-- components/button/index.pe -->
<sence>
<box class="pe-button" @click="handleClick">
<text class="button-text">{{ label }}</text>
</box>
</sence>
<script>
// 组件属性
let label = '按钮'
// 组件事件
let onClick = null
// 设置属性
function setLabel(newLabel) {
label = newLabel
const textElement = game.getSceneElement('button-text')
if (textElement) {
textElement.element.textContent = label
}
}
// 设置事件处理器
function setOnClick(handler) {
onClick = handler
}
// 事件处理函数
function handleClick() {
if (typeof onClick === 'function') {
onClick()
}
}
// 组件初始化
onLoad(() => {
console.log('按钮组件加载')
})
onShow(() => {
console.log('按钮组件显示')
})
</script>
```
### 组件样式文件
```less
/* components/button/index.less */
.pe-button {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
&:hover {
background-color: #2980b9;
}
&:active {
transform: translateY(1px);
}
.button-text {
margin: 0;
font-weight: normal;
}
}
```
## 使用组件
在场景中使用组件非常简单:
```html
<!-- scenes/home/index.pe -->
<sence>
<!-- 使用按钮组件 -->
<button @click="handleButtonClick"></button>
<!-- 带属性的组件 -->
<button label="提交" @click="handleSubmit"></button>
<button label="取消" @click="handleCancel"></button>
</sence>
<script>
// 导入组件
import Button from '../../components/button/index.pe'
function handleButtonClick() {
console.log('按钮被点击')
}
function handleSubmit() {
console.log('提交按钮被点击')
}
function handleCancel() {
console.log('取消按钮被点击')
}
</script>
```
## 组件通信
组件之间可以通过属性和事件进行通信。
### Props属性
父组件向子组件传递数据:
```html
<!-- 父组件 -->
<sence>
<user-card
name="张三"
age="25"
email="zhangsan@example.com"
@edit="handleEditUser">
</user-card>
</sence>
<!-- 子组件 (components/user-card/index.pe) -->
<sence>
<box class="user-card">
<text class="user-name">{{ name }}</text>
<text class="user-age">年龄: {{ age }}</text>
<text class="user-email">邮箱: {{ email }}</text>
<box class="edit-button" @click="handleEdit">编辑</box>
</box>
</sence>
<script>
// 组件属性
let name = ''
let age = ''
let email = ''
// 组件事件
let onEdit = null
// 设置属性的方法
function setName(value) {
name = value
}
function setAge(value) {
age = value
}
function setEmail(value) {
email = value
}
// 设置事件处理器
function setOnEdit(handler) {
onEdit = handler
}
// 事件处理函数
function handleEdit() {
if (typeof onEdit === 'function') {
onEdit({ name, age, email })
}
}
</script>
```
### Events事件
子组件向父组件传递数据:
```html
<!-- 子组件触发事件 -->
<script>
function handleEdit() {
// 触发自定义事件
const event = new CustomEvent('edit', {
detail: { name, age, email }
})
// 获取组件根元素并触发事件
const rootElement = game.getSceneElement('user-card')
if (rootElement) {
rootElement.element.dispatchEvent(event)
}
}
</script>
```
## 插槽Slots
插槽允许父组件向子组件传递内容:
```html
<!-- 子组件 (components/card/index.pe) -->
<sence>
<box class="card">
<box class="card-header">
<text class="card-title">{{ title }}</text>
</box>
<box class="card-body">
<!-- 默认插槽 -->
<slot></slot>
</box>
<box class="card-footer">
<!-- 具名插槽 -->
<slot name="footer"></slot>
</box>
</box>
</sence>
<script>
let title = '卡片标题'
function setTitle(value) {
title = value
}
</script>
```
```html
<!-- 父组件中使用插槽 -->
<sence>
<card title="我的卡片">
<!-- 默认插槽内容 -->
<text>这是卡片的主要内容</text>
<!-- 具名插槽内容 -->
<box slot="footer">
<button>确定</button>
<button>取消</button>
</box>
</card>
</sence>
```
## 动态组件
PE支持动态组件可以根据条件渲染不同的组件
```html
<sence>
<!-- 动态组件 -->
<component :is="currentComponent"
v-for="prop in componentProps"
:key="prop.key"
v-bind="prop.value">
</component>
<!-- 切换组件的按钮 -->
<box class="btn" @click="switchToButton">显示按钮</box>
<box class="btn" @click="switchToCard">显示卡片</box>
</sence>
<script>
let currentComponent = 'button'
let componentProps = {}
function switchToButton() {
currentComponent = 'button'
componentProps = { label: '动态按钮' }
}
function switchToCard() {
currentComponent = 'card'
componentProps = { title: '动态卡片' }
}
</script>
```
## 组件生命周期
组件也有自己的生命周期钩子,与场景生命周期类似:
```html
<sence>
<box class="component">组件内容</box>
</sence>
<script>
// 组件生命周期钩子
onLoad(() => {
console.log('组件加载')
// 组件初始化
})
onShow(() => {
console.log('组件显示')
// 组件显示时的操作
})
onHide(() => {
console.log('组件隐藏')
// 组件隐藏时的操作
})
onDestory(() => {
console.log('组件销毁')
// 组件销毁时的清理操作
})
</script>
```
## 组件最佳实践
### 1. 单一职责原则
每个组件应该只负责一个功能:
```html
<!-- 好的做法 -->
<user-avatar></user-avatar>
<user-name></user-name>
<user-email></user-email>
<!-- 避免 -->
<user-info></user-info> <!-- 包含太多功能 -->
```
### 2. 合理的组件结构
```html
<!-- components/product-card/index.pe -->
<sence>
<box class="product-card">
<sprite class="product-image"></sprite>
<box class="product-info">
<text class="product-name">{{ name }}</text>
<text class="product-price">¥{{ price }}</text>
<box class="product-actions">
<button @click="addToCart">加入购物车</button>
<button @click="viewDetails">查看详情</button>
</box>
</box>
</box>
</sence>
<script>
// 属性
let name = ''
let price = 0
let image = ''
// 事件
let onAddToCart = null
let onViewDetails = null
// 方法
function setName(value) {
name = value
}
function setPrice(value) {
price = value
}
function setImage(value) {
image = value
}
function setOnAddToCart(handler) {
onAddToCart = handler
}
function setOnViewDetails(handler) {
onViewDetails = handler
}
// 事件处理
function addToCart() {
if (typeof onAddToCart === 'function') {
onAddToCart({ name, price })
}
}
function viewDetails() {
if (typeof onViewDetails === 'function') {
onViewDetails({ name, price })
}
}
</script>
```
### 3. 组件样式隔离
```less
/* components/product-card/index.less */
.product-card {
width: 300px;
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
background: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
.product-image {
width: 100%;
height: 200px;
background-color: #f5f5f5;
}
.product-info {
padding: 15px;
.product-name {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.product-price {
color: #e74c3c;
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
}
.product-actions {
display: flex;
gap: 10px;
.button {
flex: 1;
}
}
}
}
```
## 完整示例
以下是一个完整的组件示例:
### components/modal/index.pe
```html
<sence>
<box class="modal-overlay" @click="handleOverlayClick">
<box class="modal-container" @click.stop>
<box class="modal-header">
<text class="modal-title">{{ title }}</text>
<box class="modal-close" @click="close"></box>
</box>
<box class="modal-body">
<slot></slot>
</box>
<box class="modal-footer">
<slot name="footer">
<button class="btn btn-primary" @click="confirm">确定</button>
<button class="btn btn-secondary" @click="close">取消</button>
</slot>
</box>
</box>
</box>
</sence>
<script>
// 属性
let title = '模态框'
let visible = false
// 事件
let onConfirm = null
let onClose = null
// 方法
function setTitle(value) {
title = value
const titleElement = game.getSceneElement('modal-title')
if (titleElement) {
titleElement.element.textContent = title
}
}
function setVisible(value) {
visible = value
const overlay = game.getSceneElement('modal-overlay')
if (overlay) {
overlay.element.style.display = visible ? 'flex' : 'none'
}
}
function setOnConfirm(handler) {
onConfirm = handler
}
function setOnClose(handler) {
onClose = handler
}
// 事件处理
function handleOverlayClick() {
close()
}
function confirm() {
if (typeof onConfirm === 'function') {
onConfirm()
}
close()
}
function close() {
setVisible(false)
if (typeof onClose === 'function') {
onClose()
}
}
// 生命周期
onLoad(() => {
console.log('模态框组件加载')
// 初始化隐藏
setVisible(false)
})
onShow(() => {
console.log('模态框组件显示')
})
onHide(() => {
console.log('模态框组件隐藏')
})
onDestory(() => {
console.log('模态框组件销毁')
})
</script>
```
### components/modal/index.less
```less
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-container {
background: white;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
min-width: 300px;
max-width: 500px;
max-height: 80vh;
overflow: hidden;
display: flex;
flex-direction: column;
}
.modal-header {
padding: 15px 20px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
.modal-title {
font-size: 18px;
font-weight: bold;
margin: 0;
}
.modal-close {
cursor: pointer;
font-size: 20px;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
&:hover {
background-color: #f5f5f5;
}
}
}
.modal-body {
padding: 20px;
flex: 1;
overflow-y: auto;
}
.modal-footer {
padding: 15px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
gap: 10px;
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
&.btn-primary {
background-color: #3498db;
color: white;
&:hover {
background-color: #2980b9;
}
}
&.btn-secondary {
background-color: #95a5a6;
color: white;
&:hover {
background-color: #7f8c8d;
}
}
}
}
```
### 使用模态框组件
```html
<!-- scenes/home/index.pe -->
<sence>
<box class="page">
<button @click="showModal">打开模态框</button>
<!-- 使用模态框组件 -->
<modal title="确认操作" @confirm="handleConfirm" @close="handleClose">
<text>您确定要执行此操作吗?</text>
<box slot="footer">
<button class="btn btn-danger" @click="handleConfirm">确认</button>
<button class="btn btn-secondary" @click="handleClose">取消</button>
</box>
</modal>
</box>
</sence>
<script>
let modalVisible = false
function showModal() {
modalVisible = true
// 更新模态框可见性
const modal = game.getSceneElement('modal')
if (modal) {
modal.setVisible(true)
}
}
function handleConfirm() {
console.log('用户确认操作')
// 执行确认操作
}
function handleClose() {
console.log('用户关闭模态框')
modalVisible = false
}
</script>
```
通过以上内容你已经了解了PE引擎的组件系统。组件是构建复杂应用的基础合理使用组件可以大大提高代码的可维护性和复用性。

View File

@@ -0,0 +1,84 @@
# 基础教程
在本章节中你将学习PE引擎的核心概念。我们假定你已经完成了[快速上手](../quick-start)指南并对HTML、CSS和JavaScript有基本的了解。
## 目录
- [创建应用实例](./application)
- [元素系统](./elements)
- [场景系统](./scenes)
- [样式处理](./styling)
- [事件处理](./event-handling)
- [生命周期](./lifecycle)
## 什么是PE引擎
PEPandona Engine是一个现代化的前端应用开发框架专注于提供简洁的API和高效的开发体验。它采用编译时架构支持基于路由表的场景管理让开发者能够快速构建交互式Web应用。
PE的核心特性包括
1. **基于路由的场景管理**:通过简单的路由配置,轻松管理应用的不同状态和视图。
2. **声明式模板语法**使用类似HTML的模板语法让代码更直观易懂。
3. **组件化开发**将UI拆分为独立的可复用组件提高代码的可维护性。
4. **内置动画系统**提供丰富的动画API轻松实现流畅的交互动画。
5. **响应式数据绑定**自动追踪数据变化确保UI与数据保持同步。
6. **强大的样式系统**支持CSS预处理器提供灵活的样式管理方案。
## 核心概念概览
在深入了解每个概念之前让我们先快速了解一下PE引擎的核心概念
### 应用实例
每个PE应用都从创建一个应用实例开始
```javascript
import PE from 'pe-engine'
const game = PE.create()
```
这个实例包含了所有PE引擎的功能是整个应用的核心。
### 元素系统
PE提供了丰富的内置元素类型包括
- `sprite`:精灵元素,可用于显示图像或创建基本图形
- `box`:盒子元素,用于创建矩形区域
- `text`:文本元素,用于显示文本内容
- `html`HTML元素用于嵌入原生HTML内容
- `svg`SVG元素用于创建矢量图形
### 场景系统
场景是PE应用的基本构建块。每个场景对应一个路由路径包含
- 模板定义(`<sence>`标签)
- 样式定义(`.less`文件)
- 逻辑代码(`<script>`标签)
### 样式处理
PE使用Less作为CSS预处理器每个场景可以有对应的样式文件。样式会自动作用于场景内的元素避免全局样式污染。
### 事件处理
PE提供简洁的事件绑定语法通过`@`前缀可以直接在模板中绑定事件处理器:
```html
<box @click="handleClick">点击我</box>
```
### 生命周期
每个场景都有完整的生命周期钩子,包括:
- `onLoad`:场景加载时调用
- `onShow`:场景显示时调用
- `onHide`:场景隐藏时调用
- `onDestory`:场景销毁时调用
这些钩子让你能够在适当的时机执行初始化、清理等操作。
在接下来的章节中我们将深入探讨这些概念帮助你掌握PE引擎的使用方法。

View File

@@ -0,0 +1,194 @@
# 创建应用实例
在PE引擎中所有应用都从创建一个应用实例开始。这个实例是整个应用的核心包含了引擎的所有功能。
## 创建应用实例
要创建一个PE应用实例你需要从`pe-engine`包中导入PE对象然后调用`PE.create()`方法:
```javascript
import PE from 'pe-engine'
// 创建应用实例
const game = PE.create()
```
`PE.create()`方法会返回一个引擎实例该实例包含了所有PE引擎的功能包括元素创建、场景管理、动画系统等。
## 应用配置
在创建应用实例时,你可以传入配置选项来自定义应用的行为:
```javascript
import PE from 'pe-engine'
// 带配置的应用实例
const game = PE.create({
width: 1024, // 舞台宽度默认800
height: 768, // 舞台高度默认600
background: '#ffffff', // 舞台背景色,默认'#f0f0f0'
fps: 60 // 帧率默认60
})
```
### 配置选项说明
| 选项 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| width | number | 800 | 应用舞台的宽度(像素) |
| height | number | 600 | 应用舞台的高度(像素) |
| background | string | '#f0f0f0' | 舞台背景色 |
| fps | number | 60 | 应用的帧率 |
## 应用实例的核心功能
创建的应用实例提供了丰富的API来管理你的应用
### 元素创建
使用`create`方法可以创建各种元素:
```javascript
// 创建精灵
const sprite = game.create('sprite')
sprite.setPosition(100, 100)
sprite.setSize(100, 100)
// 创建盒子
const box = game.create('box', {
x: 200,
y: 200,
width: 150,
height: 100
})
// 创建文本
const text = game.create('text', {
text: 'Hello PE!',
x: 300,
y: 300
})
```
### 场景管理
应用实例提供了场景管理功能:
```javascript
// 切换到指定路径的场景
PE.navigateTo('/about')
// 预加载场景
game.preloadScenes(['/home', '/about', '/contact'])
```
### 动画系统
应用实例集成了动画系统:
```javascript
// 创建补间动画
game.tween(sprite.element)
.to({ left: '500px', top: '500px' }, 1000)
.easing('easeInOutQuad')
.start()
// 创建帧动画
game.createFrameAnimation('walk', [
'img/walk1.png',
'img/walk2.png',
'img/walk3.png'
], 10)
game.playFrameAnimation(sprite, 'walk', true)
```
### 资源管理
应用实例提供了资源加载和管理功能:
```javascript
// 加载图像资源
const image = await game.load('path/to/image.png')
// 加载音频资源
game.loadSound('jump', 'path/to/jump.mp3')
game.loadMusic('bgm', 'path/to/background.mp3')
```
### 摄像机控制
应用实例支持摄像机系统:
```javascript
// 设置摄像机位置
game.setCameraPosition(100, 100)
// 跟随目标
game.follow(playerSprite)
// 摄像机震动效果
game.shakeCamera(10, 500)
```
## 完整示例
以下是一个完整的应用实例创建和使用示例:
```javascript
import PE from 'pe-engine'
// 创建应用实例
const game = PE.create({
width: 800,
height: 600,
background: '#2c3e50'
})
// 创建一个精灵
const player = game.create('sprite')
player.setPosition(100, 100)
player.setSize(50, 50)
// 为精灵添加样式
player.element.style.backgroundColor = '#3498db'
player.element.style.borderRadius = '50%'
// 添加点击事件
player.element.addEventListener('click', () => {
// 播放动画
game.tween(player.element)
.to({
left: Math.random() * 700 + 'px',
top: Math.random() * 500 + 'px'
}, 500)
.easing('easeOutQuad')
.start()
})
// 添加键盘控制
document.addEventListener('keydown', (e) => {
const speed = 10
const pos = {
left: parseInt(player.element.style.left) || 0,
top: parseInt(player.element.style.top) || 0
}
switch(e.key) {
case 'ArrowUp':
player.setPosition(pos.left, pos.top - speed)
break
case 'ArrowDown':
player.setPosition(pos.left, pos.top + speed)
break
case 'ArrowLeft':
player.setPosition(pos.left - speed, pos.top)
break
case 'ArrowRight':
player.setPosition(pos.left + speed, pos.top)
break
}
})
```
通过以上内容你已经了解了如何创建和配置PE应用实例以及如何使用其核心功能。在下一章节中我们将深入探讨PE的元素系统。

View File

@@ -0,0 +1,303 @@
# 元素系统
PE引擎提供了丰富的内置元素类型让你能够快速创建各种UI组件和视觉元素。元素是构成PE应用界面的基本单位。
## 元素类型概览
PE引擎目前支持以下几种内置元素类型
1. **Sprite精灵** - 用于显示图像或创建基本图形
2. **Box盒子** - 用于创建矩形区域
3. **Text文本** - 用于显示文本内容
4. **HTML** - 用于嵌入原生HTML内容
5. **SVG** - 用于创建矢量图形
## 创建元素
在PE中所有元素都通过应用实例的`create`方法创建:
```javascript
// 通用创建方法
const element = game.create(type, options)
```
### Sprite精灵
Sprite是最常用的元素类型可以用来显示图像或创建基本图形。
```javascript
// 创建一个默认的sprite
const sprite = game.create('sprite')
// 创建带配置的sprite
const spriteWithConfig = game.create('sprite', {
x: 100,
y: 100,
width: 50,
height: 50
})
// 设置位置和尺寸
sprite.setPosition(200, 200)
sprite.setSize(100, 100)
// 添加样式
sprite.element.style.backgroundColor = '#3498db'
sprite.element.style.borderRadius = '50%'
```
### Box盒子
Box元素用于创建矩形区域常用于创建按钮、面板等UI组件。
```javascript
// 创建一个box
const box = game.create('box', {
x: 100,
y: 100,
width: 200,
height: 100
})
// Box元素的DOM元素可以直接操作样式
box.element.style.backgroundColor = '#e74c3c'
box.element.style.borderRadius = '8px'
```
### Text文本
Text元素用于显示文本内容。
```javascript
// 创建文本元素
const text = game.create('text', {
text: 'Hello PE!',
x: 100,
y: 100
})
// 更新文本内容
text.setText('新的文本内容')
// 设置文本样式
text.element.style.color = '#ffffff'
text.element.style.fontSize = '24px'
text.element.style.fontWeight = 'bold'
```
### HTML元素
HTML元素允许你嵌入原生HTML内容。
```javascript
// 创建HTML元素
const htmlElement = game.create('html', {
html: '<div class="custom-html">这是HTML内容</div>',
x: 100,
y: 100
})
// 更新HTML内容
htmlElement.setHtml('<div class="updated-html">更新的内容</div>')
```
### SVG元素
SVG元素用于创建矢量图形。
```javascript
// 创建SVG元素
const svgElement = game.create('svg', {
x: 100,
y: 100,
width: 200,
height: 200,
content: '<circle cx="100" cy="100" r="50" fill="#3498db" />'
})
```
## 元素的通用方法和属性
所有PE元素都有一些通用的方法和属性
### 位置和尺寸
```javascript
// 设置位置
element.setPosition(100, 200)
// 设置尺寸
element.setSize(300, 150)
// 获取位置
const x = element.getX()
const y = element.getY()
// 获取尺寸
const width = element.getWidth()
const height = element.getHeight()
```
### 样式操作
```javascript
// 设置单个样式属性
element.setStyle('backgroundColor', '#3498db')
// 设置多个样式属性
element.setStyles({
backgroundColor: '#3498db',
borderRadius: '8px',
border: '2px solid #2980b9'
})
// 添加CSS类
element.addClass('my-class')
// 移除CSS类
element.removeClass('my-class')
// 检查是否包含CSS类
if (element.hasClass('my-class')) {
// 执行相应操作
}
```
### 显示和隐藏
```javascript
// 隐藏元素
element.hide()
// 显示元素
element.show()
// 切换显示状态
element.toggle()
```
## 在场景模板中使用元素
在PE的场景系统中你也可以直接在模板中定义元素
```html
<sence>
<!-- 使用sprite元素 -->
<sprite class="player" x="100" y="100" width="50" height="50"></sprite>
<!-- 使用box元素 -->
<box class="panel" x="200" y="200" width="300" height="200">面板内容</box>
<!-- 使用text元素 -->
<text class="title" x="100" y="50">欢迎使用PE引擎</text>
<!-- 使用循环创建多个元素 -->
<box for="{item} in menuItems"
class="menu-item"
x="{item.x}"
y="{item.y}"
@click="handleMenuClick(item.id)">
{{ item.label }}
</box>
</sence>
```
对应的样式文件(.less
```less
.player {
background-color: #3498db;
border-radius: 50%;
transition: all 0.3s ease;
}
.player:hover {
transform: scale(1.1);
}
.panel {
background-color: #ecf0f1;
border: 2px solid #bdc3c7;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.title {
color: #2c3e50;
font-size: 24px;
font-weight: bold;
}
.menu-item {
width: 150px;
height: 40px;
background-color: #e74c3c;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
cursor: pointer;
margin: 5px 0;
}
```
## 元素的事件处理
PE元素支持丰富的事件处理
```javascript
// 添加点击事件
element.element.addEventListener('click', (event) => {
console.log('元素被点击了')
})
// 添加鼠标移入事件
element.element.addEventListener('mouseenter', (event) => {
element.setStyle('backgroundColor', '#e74c3c')
})
// 添加鼠标移出事件
element.element.addEventListener('mouseleave', (event) => {
element.setStyle('backgroundColor', '#3498db')
})
// 在场景模板中直接绑定事件
// <box @click="handleClick" @mouseenter="handleMouseEnter">点击我</box>
```
## 自定义元素
除了内置元素,你还可以创建自定义元素:
```javascript
// 创建自定义元素类
class CustomElement extends BaseElement {
constructor(x, y, width, height, engineId) {
super(x, y, width, height, engineId)
// 初始化自定义元素
this.element.style.backgroundColor = '#9b59b6'
this.element.style.borderRadius = '10px'
}
// 自定义方法
animateColor() {
const colors = ['#9b59b6', '#3498db', '#e74c3c', '#2ecc71']
let index = 0
setInterval(() => {
this.element.style.backgroundColor = colors[index % colors.length]
index++
}, 1000)
}
}
// 注册自定义元素
// 在场景中使用
// <custom-element x="100" y="100" width="100" height="100"></custom-element>
```
通过以上内容你已经了解了PE引擎的元素系统。在下一章节中我们将深入探讨场景系统的使用。

View File

@@ -0,0 +1,605 @@
# 事件处理
PE引擎提供了简洁而强大的事件处理机制让你能够轻松响应用户的交互操作。事件处理是创建交互式应用的关键部分。
## 事件绑定语法
在PE的场景模板中你可以使用`@`前缀直接绑定事件处理器:
```html
<sence>
<!-- 基本事件绑定 -->
<box @click="handleClick">点击我</box>
<!-- 多个事件绑定 -->
<box @click="handleClick" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
悬停我
</box>
<!-- 传递参数 -->
<box @click="handleClickWithParam('button1')">按钮1</box>
<box @click="handleClickWithParam('button2')">按钮2</box>
</sence>
<script>
function handleClick() {
console.log('元素被点击了')
}
function handleMouseEnter() {
console.log('鼠标进入元素')
}
function handleMouseLeave() {
console.log('鼠标离开元素')
}
function handleClickWithParam(buttonId) {
console.log('点击了按钮:', buttonId)
}
</script>
```
## 支持的事件类型
PE支持所有标准的DOM事件类型
### 鼠标事件
```html
<sence>
<box @click="handleClick"
@dblclick="handleDoubleClick"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
@mousedown="handleMouseDown"
@mouseup="handleMouseUp"
@contextmenu="handleContextMenu">
鼠标事件示例
</box>
</sence>
<script>
function handleClick(event) {
console.log('点击事件:', event)
}
function handleDoubleClick(event) {
console.log('双击事件:', event)
}
function handleMouseEnter(event) {
console.log('鼠标进入:', event)
}
function handleMouseLeave(event) {
console.log('鼠标离开:', event)
}
function handleMouseDown(event) {
console.log('鼠标按下:', event)
}
function handleMouseUp(event) {
console.log('鼠标释放:', event)
}
function handleContextMenu(event) {
event.preventDefault() // 阻止右键菜单
console.log('右键点击:', event)
}
</script>
```
### 键盘事件
```html
<sence>
<text @keydown="handleKeyDown"
@keyup="handleKeyUp"
@keypress="handleKeyPress"
tabindex="0">
按键事件示例(点击后按键盘)
</text>
</sence>
<script>
function handleKeyDown(event) {
console.log('按键按下:', event.key, event.code)
}
function handleKeyUp(event) {
console.log('按键释放:', event.key, event.code)
}
function handleKeyPress(event) {
console.log('按键按压:', event.key)
}
</script>
```
### 表单事件
```html
<sence>
<html @input="handleInput"
@change="handleChange"
@focus="handleFocus"
@blur="handleBlur"
html="<input type='text' placeholder='输入内容...' />">
</html>
</sence>
<script>
function handleInput(event) {
console.log('输入事件:', event.target.value)
}
function handleChange(event) {
console.log('值改变事件:', event.target.value)
}
function handleFocus(event) {
console.log('获得焦点')
}
function handleBlur(event) {
console.log('失去焦点')
}
</script>
```
### 触摸事件
```html
<sence>
<box @touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
@touchcancel="handleTouchCancel">
触摸事件示例
</box>
</sence>
<script>
function handleTouchStart(event) {
console.log('触摸开始:', event.touches.length, '个触摸点')
}
function handleTouchMove(event) {
console.log('触摸移动:', event.touches[0].clientX, event.touches[0].clientY)
}
function handleTouchEnd(event) {
console.log('触摸结束')
}
function handleTouchCancel(event) {
console.log('触摸取消')
}
</script>
```
## 事件对象
所有事件处理器都会接收到一个事件对象,包含有关事件的详细信息:
```javascript
function handleClick(event) {
// 事件基本信息
console.log('事件类型:', event.type)
console.log('目标元素:', event.target)
console.log('当前元素:', event.currentTarget)
// 鼠标位置
console.log('页面坐标:', event.pageX, event.pageY)
console.log('客户端坐标:', event.clientX, event.clientY)
// 键盘信息
console.log('是否按下Ctrl键:', event.ctrlKey)
console.log('是否按下Alt键:', event.altKey)
console.log('是否按下Shift键:', event.shiftKey)
// 阻止默认行为
event.preventDefault()
// 阻止事件冒泡
event.stopPropagation()
}
```
## 在JavaScript中绑定事件
除了在模板中绑定事件你也可以在JavaScript代码中直接绑定事件
```javascript
// 获取场景元素
const element = game.getSceneElement('my-button')
// 添加事件监听器
element.element.addEventListener('click', function(event) {
console.log('元素被点击了')
})
// 添加多个事件监听器
element.element.addEventListener('mouseenter', handleMouseEnter)
element.element.addEventListener('mouseleave', handleMouseLeave)
// 移除事件监听器
element.element.removeEventListener('click', handleClick)
```
## 事件修饰符
PE支持一些常用的事件修饰符类似于Vue.js的语法
```html
<sence>
<!-- 阻止默认行为 -->
<box @click.prevent="handleClick">阻止默认行为</box>
<!-- 阻止事件冒泡 -->
<box @click.stop="handleClick">阻止事件冒泡</box>
<!-- 只触发一次 -->
<box @click.once="handleClick">只触发一次</box>
<!-- 组合修饰符 -->
<box @click.stop.prevent="handleClick">阻止冒泡和默认行为</box>
</sence>
```
## 自定义事件
你也可以创建和触发自定义事件:
```javascript
// 创建自定义事件
const customEvent = new CustomEvent('myCustomEvent', {
detail: { message: '这是自定义事件数据' }
})
// 触发自定义事件
element.element.dispatchEvent(customEvent)
// 监听自定义事件
element.element.addEventListener('myCustomEvent', function(event) {
console.log('接收到自定义事件:', event.detail.message)
})
```
## 事件总线
PE引擎内置了事件总线允许你在不同组件间进行通信
```javascript
// 发送事件
game.eventBus.emit('user-login', { username: 'john' })
// 监听事件
game.eventBus.on('user-login', function(data) {
console.log('用户登录:', data.username)
})
// 移除事件监听器
game.eventBus.off('user-login', handlerFunction)
```
## 在场景脚本中处理事件
在场景的`<script>`部分,你可以定义事件处理函数:
```html
<sence>
<box class="counter-button" @click="incrementCounter">计数器: {{ counter }}</box>
<box class="reset-button" @click="resetCounter">重置</box>
</sence>
<script>
// 数据定义
let counter = 0
// 事件处理函数
function incrementCounter() {
counter++
console.log('计数器:', counter)
// 更新UI
const button = game.getSceneElement('counter-button')
button.element.textContent = `计数器: ${counter}`
}
function resetCounter() {
counter = 0
console.log('计数器已重置')
// 更新UI
const button = game.getSceneElement('counter-button')
button.element.textContent = `计数器: ${counter}`
}
// 生命周期钩子中绑定事件
onLoad(() => {
console.log('场景加载完成,计数器初始化为', counter)
})
onShow(() => {
console.log('场景显示,当前计数器值为', counter)
})
</script>
```
## 事件处理最佳实践
### 1. 合理使用事件委托
对于大量相似元素,使用事件委托可以提高性能:
```html
<sence>
<!-- 使用事件委托处理列表项点击 -->
<box class="list-container" @click="handleListItemClick">
<box class="list-item" data-id="1">项目1</box>
<box class="list-item" data-id="2">项目2</box>
<box class="list-item" data-id="3">项目3</box>
</box>
</sence>
<script>
function handleListItemClick(event) {
// 检查点击的是否是列表项
if (event.target.classList.contains('list-item')) {
const id = event.target.getAttribute('data-id')
console.log('点击了项目:', id)
}
}
</script>
```
### 2. 及时移除事件监听器
在适当的时机移除事件监听器,避免内存泄漏:
```javascript
onLoad(() => {
// 添加事件监听器
document.addEventListener('keydown', handleGlobalKeyDown)
})
onDestory(() => {
// 移除事件监听器
document.removeEventListener('keydown', handleGlobalKeyDown)
})
function handleGlobalKeyDown(event) {
// 全局键盘事件处理
if (event.key === 'Escape') {
PE.navigateTo('/')
}
}
```
### 3. 使用防抖和节流
对于频繁触发的事件(如滚动、输入),使用防抖和节流优化性能:
```javascript
// 防抖函数
function debounce(func, wait) {
let timeout
return function(...args) {
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(this, args), wait)
}
}
// 节流函数
function throttle(func, limit) {
let inThrottle
return function(...args) {
if (!inThrottle) {
func.apply(this, args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
// 使用防抖处理输入事件
const handleInput = debounce(function(event) {
console.log('输入内容:', event.target.value)
}, 300)
// 使用节流处理滚动事件
const handleScroll = throttle(function(event) {
console.log('页面滚动')
}, 100)
```
## 完整示例
以下是一个完整的事件处理示例:
### scenes/game/index.pe
```html
<sence>
<box class="game-container">
<!-- 玩家角色 -->
<sprite class="player"
@mousedown="startDrag"
@mouseup="stopDrag"
@touchstart="startDrag"
@touchend="stopDrag">
</sprite>
<!-- 敌人 -->
<sprite for="{enemy} in enemies"
class="enemy"
:class="{ 'enemy-active': enemy.active }"
@click="hitEnemy(enemy.id)">
</sprite>
<!-- 控制按钮 -->
<box class="control-panel">
<box class="btn btn-primary" @click="startGame">开始游戏</box>
<box class="btn btn-secondary" @click="pauseGame">暂停</box>
<box class="btn btn-danger" @click="resetGame">重置</box>
</box>
<!-- 游戏信息 -->
<text class="score">得分: {{ score }}</text>
<text class="lives">生命: {{ lives }}</text>
</box>
</sence>
<script>
// 游戏状态
let score = 0
let lives = 3
let gameRunning = false
let dragging = false
// 敌人数据
const enemies = [
{ id: 1, active: true },
{ id: 2, active: true },
{ id: 3, active: true }
]
// 事件处理函数
function startDrag(event) {
if (!gameRunning) return
dragging = true
console.log('开始拖拽玩家')
// 添加鼠标移动事件监听器
document.addEventListener('mousemove', handleDrag)
document.addEventListener('touchmove', handleDrag)
}
function stopDrag(event) {
dragging = false
console.log('停止拖拽玩家')
// 移除鼠标移动事件监听器
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)
}
function handleDrag(event) {
if (!dragging || !gameRunning) return
const player = game.getSceneElement('player')
if (player) {
// 获取鼠标或触摸位置
let x, y
if (event.type === 'touchmove') {
x = event.touches[0].clientX
y = event.touches[0].clientY
} else {
x = event.clientX
y = event.clientY
}
// 更新玩家位置
player.setPosition(x - 25, y - 25) // 考虑精灵尺寸
}
}
function hitEnemy(enemyId) {
if (!gameRunning) return
// 找到被击中的敌人
const enemy = enemies.find(e => e.id === enemyId)
if (enemy && enemy.active) {
// 标记敌人为非活跃状态
enemy.active = false
// 增加得分
score += 10
updateScore()
console.log('击中敌人:', enemyId)
}
}
function startGame() {
gameRunning = true
score = 0
lives = 3
updateScore()
updateLives()
// 重置敌人状态
enemies.forEach(enemy => {
enemy.active = true
})
console.log('游戏开始')
}
function pauseGame() {
gameRunning = !gameRunning
console.log(gameRunning ? '游戏继续' : '游戏暂停')
}
function resetGame() {
gameRunning = false
score = 0
lives = 3
updateScore()
updateLives()
// 重置敌人状态
enemies.forEach(enemy => {
enemy.active = true
})
console.log('游戏重置')
}
// 更新得分显示
function updateScore() {
const scoreElement = game.getSceneElement('score')
if (scoreElement) {
scoreElement.element.textContent = `得分: ${score}`
}
}
// 更新生命显示
function updateLives() {
const livesElement = game.getSceneElement('lives')
if (livesElement) {
livesElement.element.textContent = `生命: ${lives}`
}
}
// 生命周期钩子
onLoad(() => {
console.log('游戏场景加载完成')
})
onShow(() => {
console.log('游戏场景显示')
// 初始化游戏状态
updateScore()
updateLives()
})
onHide(() => {
console.log('游戏场景隐藏')
// 暂停游戏
gameRunning = false
})
onDestory(() => {
console.log('游戏场景销毁')
// 清理事件监听器
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)
})
</script>
```
通过以上内容你已经了解了PE引擎的事件处理机制。在下一章节中我们将探讨生命周期的相关内容。

View File

@@ -0,0 +1,564 @@
# 生命周期
PE引擎为场景提供了完整的生命周期钩子让你能够在适当的时机执行初始化、清理和其他操作。理解生命周期对于开发高效、稳定的PE应用至关重要。
## 生命周期钩子概览
每个PE场景都有四个主要的生命周期钩子
1. **onLoad** - 场景加载时调用(只执行一次)
2. **onShow** - 场景显示时调用(每次显示都会执行)
3. **onHide** - 场景隐藏时调用
4. **onDestory** - 场景销毁时调用(只执行一次)
## onLoad
`onLoad`钩子在场景首次加载时调用,只执行一次。这是进行一次性初始化操作的最佳时机。
```html
<sence>
<box class="welcome">欢迎来到PE引擎</box>
</sence>
<script>
// 在onLoad中进行一次性初始化
onLoad(() => {
console.log('场景加载完成')
// 初始化数据
initializeData()
// 设置初始状态
setupInitialState()
// 加载资源
loadResources()
// 添加全局事件监听器
document.addEventListener('keydown', handleGlobalKeyDown)
})
function initializeData() {
// 初始化应用数据
console.log('初始化数据')
}
function setupInitialState() {
// 设置初始状态
console.log('设置初始状态')
}
function loadResources() {
// 加载必要的资源
console.log('加载资源')
}
function handleGlobalKeyDown(event) {
// 处理全局键盘事件
if (event.key === 'Escape') {
PE.navigateTo('/')
}
}
</script>
```
## onShow
`onShow`钩子在场景每次显示时调用。无论是首次显示还是从其他场景切换回来,都会执行此钩子。
```html
<sence>
<box class="content">场景内容</box>
<text class="last-updated">最后更新: {{ lastUpdated }}</text>
</sence>
<script>
let lastUpdated = ''
onShow(() => {
console.log('场景显示')
// 更新时间戳
updateTimestamp()
// 刷新数据
refreshData()
// 启动动画
startAnimations()
// 恢复定时器
resumeTimers()
})
function updateTimestamp() {
lastUpdated = new Date().toLocaleString()
const timeElement = game.getSceneElement('last-updated')
if (timeElement) {
timeElement.element.textContent = `最后更新: ${lastUpdated}`
}
}
function refreshData() {
// 刷新场景数据
console.log('刷新数据')
}
function startAnimations() {
// 启动场景动画
console.log('启动动画')
}
function resumeTimers() {
// 恢复定时器
console.log('恢复定时器')
}
</script>
```
## onHide
`onHide`钩子在场景隐藏时调用,比如切换到其他场景时。
```html
<sence>
<box class="content">场景内容</box>
</sence>
<script>
onHide(() => {
console.log('场景隐藏')
// 暂停动画
pauseAnimations()
// 暂停定时器
pauseTimers()
// 保存状态
saveState()
})
function pauseAnimations() {
// 暂停场景动画
console.log('暂停动画')
}
function pauseTimers() {
// 暂停定时器
console.log('暂停定时器')
}
function saveState() {
// 保存场景状态
console.log('保存状态')
}
</script>
```
## onDestory
`onDestory`钩子在场景销毁时调用,通常发生在应用关闭或场景被永久移除时。
```html
<sence>
<box class="content">场景内容</box>
</sence>
<script>
onDestory(() => {
console.log('场景销毁')
// 清理资源
cleanupResources()
// 移除事件监听器
removeEventListeners()
// 停止所有动画
stopAllAnimations()
// 清理定时器
clearAllTimers()
})
function cleanupResources() {
// 清理加载的资源
console.log('清理资源')
}
function removeEventListeners() {
// 移除添加的事件监听器
document.removeEventListener('keydown', handleGlobalKeyDown)
console.log('移除事件监听器')
}
function stopAllAnimations() {
// 停止所有动画
console.log('停止所有动画')
}
function clearAllTimers() {
// 清理所有定时器
console.log('清理定时器')
}
</script>
```
## 生命周期执行顺序
理解生命周期钩子的执行顺序对于正确管理场景状态非常重要:
### 首次加载场景
1. `onLoad` - 场景加载时执行(一次)
2. `onShow` - 场景显示时执行
### 切换到其他场景
1. 当前场景: `onHide` - 场景隐藏时执行
### 切换回场景
1. `onShow` - 场景显示时执行
### 销毁场景
1. `onHide` - 场景隐藏时执行(如果当前显示)
2. `onDestory` - 场景销毁时执行(一次)
## 实际应用示例
以下是一个完整的生命周期应用示例:
### scenes/dashboard/index.pe
```html
<sence>
<box class="dashboard">
<text class="title">仪表板</text>
<text class="status">状态: {{ status }}</text>
<text class="last-update">最后更新: {{ lastUpdate }}</text>
<box class="data-container">
<box for="{item} in dataItems" class="data-card">
<text class="card-title">{{ item.title }}</text>
<text class="card-value">{{ item.value }}</text>
</box>
</box>
<box class="controls">
<box class="btn btn-primary" @click="refreshData">刷新数据</box>
<box class="btn btn-secondary" @click="PE.navigateTo('/settings')">设置</box>
</box>
</box>
</sence>
<script>
// 数据状态
let status = '加载中...'
let lastUpdate = ''
let dataItems = []
let refreshTimer = null
let dataRefreshInterval = null
// onLoad - 只执行一次
onLoad(() => {
console.log('仪表板加载')
// 初始化数据
initializeData()
// 设置全局事件监听器
setupEventListeners()
// 加载初始数据
loadData()
})
// onShow - 每次显示时执行
onShow(() => {
console.log('仪表板显示')
// 更新状态
updateStatus('运行中')
// 启动定时刷新
startAutoRefresh()
// 恢复动画
resumeAnimations()
})
// onHide - 隐藏时执行
onHide(() => {
console.log('仪表板隐藏')
// 暂停自动刷新
stopAutoRefresh()
// 暂停动画
pauseAnimations()
// 保存当前状态
saveCurrentState()
})
// onDestory - 销毁时执行
onDestory(() => {
console.log('仪表板销毁')
// 清理所有资源
cleanup()
})
// 初始化数据
function initializeData() {
dataItems = [
{ title: '用户数', value: '0' },
{ title: '订单数', value: '0' },
{ title: '收入', value: '¥0' }
]
updateUI()
}
// 设置事件监听器
function setupEventListeners() {
// 监听网络状态变化
window.addEventListener('online', handleOnline)
window.addEventListener('offline', handleOffline)
}
// 加载数据
async function loadData() {
try {
updateStatus('加载数据中...')
// 模拟API调用
const data = await fetchDataFromAPI()
dataItems = data
lastUpdate = new Date().toLocaleString()
updateStatus('数据加载完成')
updateUI()
} catch (error) {
console.error('加载数据失败:', error)
updateStatus('数据加载失败')
}
}
// 模拟API调用
function fetchDataFromAPI() {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{ title: '用户数', value: Math.floor(Math.random() * 1000) },
{ title: '订单数', value: Math.floor(Math.random() * 500) },
{ title: '收入', value: '¥' + (Math.random() * 10000).toFixed(2) }
])
}, 1000)
})
}
// 启动自动刷新
function startAutoRefresh() {
if (dataRefreshInterval) {
clearInterval(dataRefreshInterval)
}
dataRefreshInterval = setInterval(() => {
loadData()
}, 30000) // 每30秒刷新一次
// 启动更新时间戳定时器
if (refreshTimer) {
clearInterval(refreshTimer)
}
refreshTimer = setInterval(() => {
updateTimestamp()
}, 1000)
}
// 停止自动刷新
function stopAutoRefresh() {
if (dataRefreshInterval) {
clearInterval(dataRefreshInterval)
dataRefreshInterval = null
}
if (refreshTimer) {
clearInterval(refreshTimer)
refreshTimer = null
}
}
// 更新状态显示
function updateStatus(newStatus) {
status = newStatus
const statusElement = game.getSceneElement('status')
if (statusElement) {
statusElement.element.textContent = `状态: ${status}`
}
}
// 更新时间戳显示
function updateTimestamp() {
if (lastUpdate) {
const timeElement = game.getSceneElement('last-update')
if (timeElement) {
const elapsed = Math.floor((new Date() - new Date(lastUpdate)) / 1000)
timeElement.element.textContent = `最后更新: ${lastUpdate} (${elapsed}秒前)`
}
}
}
// 更新UI
function updateUI() {
// 更新数据卡片
const container = game.getSceneElement('data-container')
if (container) {
// 清空现有内容
container.element.innerHTML = ''
// 添加新的数据卡片
dataItems.forEach(item => {
const card = document.createElement('div')
card.className = 'data-card'
card.innerHTML = `
<div class="card-title">${item.title}</div>
<div class="card-value">${item.value}</div>
`
container.element.appendChild(card)
})
}
}
// 刷新数据
function refreshData() {
loadData()
}
// 处理在线状态
function handleOnline() {
updateStatus('在线')
}
// 处理离线状态
function handleOffline() {
updateStatus('离线')
}
// 恢复动画
function resumeAnimations() {
console.log('恢复动画')
}
// 暂停动画
function pauseAnimations() {
console.log('暂停动画')
}
// 保存当前状态
function saveCurrentState() {
console.log('保存当前状态')
}
// 清理资源
function cleanup() {
// 停止所有定时器
stopAutoRefresh()
// 移除事件监听器
window.removeEventListener('online', handleOnline)
window.removeEventListener('offline', handleOffline)
// 清理数据
dataItems = []
status = ''
lastUpdate = ''
console.log('资源清理完成')
}
</script>
```
## 生命周期最佳实践
### 1. 合理分配操作到不同钩子
```javascript
// 正确的做法
onLoad(() => {
// 一次性初始化操作
initializeDatabase()
setupGlobalEventListeners()
})
onShow(() => {
// 每次显示时的操作
startDataPolling()
resumeAnimations()
})
onHide(() => {
// 隐藏时的操作
stopDataPolling()
pauseAnimations()
})
onDestory(() => {
// 销毁时的操作
cleanupResources()
removeEventListeners()
})
```
### 2. 及时清理资源
```javascript
onLoad(() => {
// 创建定时器
this.timer = setInterval(updateData, 5000)
// 添加事件监听器
document.addEventListener('keydown', handleKeyDown)
})
onDestory(() => {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
// 移除事件监听器
document.removeEventListener('keydown', handleKeyDown)
})
```
### 3. 处理异步操作
```javascript
onLoad(async () => {
try {
// 异步加载数据
this.data = await loadDataFromServer()
updateUI()
} catch (error) {
console.error('加载数据失败:', error)
showErrorMessage()
}
})
onDestory(() => {
// 取消未完成的请求
if (this.currentRequest) {
this.currentRequest.cancel()
}
})
```
通过以上内容你已经了解了PE引擎的生命周期机制。这些生命周期钩子为你提供了强大的控制能力让你能够精确管理场景的创建、显示、隐藏和销毁过程。

377
guide/essentials/scenes.md Normal file
View File

@@ -0,0 +1,377 @@
# 场景系统
PE引擎的场景系统是其核心特性之一它提供了一种结构化的方式来组织和管理应用的不同视图和状态。每个场景都是一个独立的单元包含模板、样式和逻辑代码。
## 场景的基本结构
PE场景由三个主要部分组成
1. **模板Template** - 定义场景的结构和元素
2. **样式Style** - 定义场景的外观和样式
3. **脚本Script** - 定义场景的逻辑和行为
### 场景文件结构
一个典型的PE场景目录结构如下
```
scenes/
├── home/
│ ├── index.pe # 场景模板文件
│ └── index.less # 场景样式文件
└── about/
├── index.pe
└── index.less
```
## 场景模板文件(.pe
场景模板文件使用类似HTML的语法包含`sence`标签和`script`标签:
```html
<sence>
<!-- 场景元素定义 -->
<sprite class="logo"></sprite>
<box class="welcome-box">欢迎来到PE引擎!</box>
<text class="description">这是一个简单的场景示例</text>
<!-- 循环渲染元素 -->
<box for="{item} in menuItems"
class="menu-item"
@click="handleMenuClick(item.id)">
{{ item.label }}
</box>
</sence>
<script>
// 场景数据和逻辑
const menuItems = [
{ id: 'home', label: '首页' },
{ id: 'about', label: '关于' },
{ id: 'contact', label: '联系' }
]
// 生命周期钩子
onLoad(() => {
console.log('场景加载完成')
})
onShow(() => {
console.log('场景显示')
})
// 事件处理函数
function handleMenuClick(id) {
console.log('点击了菜单项:', id)
PE.navigateTo(`/${id}`)
}
</script>
```
### 模板语法
PE场景模板支持以下语法特性
#### 元素定义
```html
<!-- 基本元素 -->
<sprite class="my-sprite" x="100" y="100" width="50" height="50"></sprite>
<box class="my-box">盒子内容</box>
<text class="my-text">文本内容</text>
```
#### 事件绑定
使用`@`前缀绑定事件处理器:
```html
<box @click="handleClick" @mouseenter="handleMouseEnter">点击我</box>
```
#### 循环渲染
使用`for`指令循环渲染元素:
```html
<box for="{item} in items" class="item">{{ item.name }}</box>
```
#### 插值表达式
使用双大括号语法插入数据:
```html
<text>当前计数: {{ count }}</text>
```
## 场景样式文件(.less
每个场景可以有对应的Less样式文件样式会自动作用于该场景内的元素
```less
// home/index.less
.logo {
width: 100px;
height: 100px;
background-color: #3498db;
border-radius: 50%;
position: absolute;
top: 50px;
left: 50px;
}
.welcome-box {
width: 300px;
height: 100px;
background-color: #2c3e50;
color: white;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 200px;
left: 50px;
border-radius: 8px;
}
.description {
color: #7f8c8d;
font-size: 16px;
position: absolute;
top: 320px;
left: 50px;
}
.menu-item {
width: 150px;
height: 40px;
background-color: #e74c3c;
color: white;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
left: 50px;
border-radius: 4px;
cursor: pointer;
// 使用Less变量和嵌套
&:hover {
background-color: darken(#e74c3c, 10%);
}
&:nth-child(1) { top: 380px; }
&:nth-child(2) { top: 430px; }
&:nth-child(3) { top: 480px; }
}
```
## 场景路由配置
场景通过`sences.json`文件进行路由配置:
```json
{
"sence": [
{
"path": "/",
"title": "首页"
},
{
"path": "/about",
"title": "关于我们"
},
{
"path": "/products",
"title": "产品"
}
],
"platform": "PC"
}
```
## 场景生命周期
每个场景都有完整的生命周期钩子,让你能够在适当的时机执行操作:
### onLoad
场景加载时调用,只在场景首次加载时执行一次:
```javascript
onLoad(() => {
console.log('场景加载完成')
// 初始化操作
initializeData()
})
```
### onShow
场景显示时调用,每次场景被显示时都会执行:
```javascript
onShow(() => {
console.log('场景显示')
// 每次显示时的操作
refreshData()
})
```
### onHide
场景隐藏时调用:
```javascript
onHide(() => {
console.log('场景隐藏')
// 清理操作
pauseAnimations()
})
```
### onDestory
场景销毁时调用:
```javascript
onDestory(() => {
console.log('场景销毁')
// 最终清理操作
clearInterval(timer)
})
```
## 场景间导航
PE提供了简单的API来在场景间进行导航
```javascript
// 导航到指定路径的场景
PE.navigateTo('/about')
// 在模板中使用
<box @click="PE.navigateTo('/products')">查看产品</box>
```
## 完整示例
以下是一个完整的场景示例:
### scenes/product/index.pe
```html
<sence>
<box class="header">产品列表</box>
<box for="{product} in products"
class="product-card"
@click="viewProduct(product.id)">
<text class="product-name">{{ product.name }}</text>
<text class="product-price">¥{{ product.price }}</text>
</box>
<box class="back-button" @click="PE.navigateTo('/')">返回首页</box>
</sence>
<script>
// 产品数据
const products = [
{ id: 1, name: '产品A', price: 99.99 },
{ id: 2, name: '产品B', price: 149.99 },
{ id: 3, name: '产品C', price: 199.99 }
]
// 生命周期钩子
onLoad(() => {
console.log('产品页面加载')
})
onShow(() => {
console.log('产品页面显示')
})
// 事件处理函数
function viewProduct(id) {
console.log('查看产品:', id)
// 可以通过全局变量或事件传递数据
window.currentProductId = id
PE.navigateTo('/product-detail')
}
</script>
```
### scenes/product/index.less
```less
.header {
width: 100%;
height: 60px;
background-color: #34495e;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
}
.product-card {
width: 300px;
height: 100px;
background-color: #ecf0f1;
border: 1px solid #bdc3c7;
border-radius: 8px;
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
justify-content: center;
padding: 0 20px;
cursor: pointer;
&:hover {
background-color: #d5dbdb;
}
&:nth-child(2) { top: 100px; } // 第一个产品卡
&:nth-child(3) { top: 220px; } // 第二个产品卡
&:nth-child(4) { top: 340px; } // 第三个产品卡
}
.product-name {
color: #2c3e50;
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
.product-price {
color: #e74c3c;
font-size: 16px;
font-weight: bold;
}
.back-button {
width: 100px;
height: 40px;
background-color: #95a5a6;
color: white;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
bottom: 20px;
left: 20px;
border-radius: 4px;
cursor: pointer;
&:hover {
background-color: #7f8c8d;
}
}
```
通过以上内容你已经了解了PE引擎的场景系统。在下一章节中我们将探讨样式处理的相关内容。

546
guide/essentials/styling.md Normal file
View File

@@ -0,0 +1,546 @@
# 样式处理
PE引擎使用Less作为CSS预处理器提供了强大而灵活的样式管理方案。每个场景可以有对应的样式文件确保样式的作用域隔离和可维护性。
## Less预处理器
Less是一种向后兼容CSS的语言扩展为CSS添加了变量、嵌套、混合等功能让样式编写更加高效和可维护。
### 变量
使用变量来存储常用的值:
```less
// 定义变量
@primary-color: #3498db;
@secondary-color: #2ecc71;
@font-size-large: 18px;
@border-radius: 4px;
// 使用变量
.header {
background-color: @primary-color;
font-size: @font-size-large;
border-radius: @border-radius;
}
```
### 嵌套
Less支持嵌套语法让样式结构更清晰
```less
.navbar {
background-color: #34495e;
// 嵌套选择器
.nav-item {
color: white;
padding: 10px 15px;
// 伪类嵌套
&:hover {
background-color: #2c3e50;
}
// 子元素嵌套
.icon {
margin-right: 5px;
}
}
}
```
### 混合Mixins
混合允许你定义可复用的样式块:
```less
// 定义混合
.border-radius(@radius: 4px) {
border-radius: @radius;
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
}
.box-shadow(@x: 0, @y: 0, @blur: 10px, @color: rgba(0,0,0,0.1)) {
box-shadow: @arguments;
-webkit-box-shadow: @arguments;
-moz-box-shadow: @arguments;
}
// 使用混合
.button {
.border-radius(8px);
.box-shadow(0, 2px, 5px, rgba(0,0,0,0.2));
}
```
### 函数
Less提供了丰富的内置函数
```less
// 颜色函数
.button {
background-color: #3498db;
// 变暗10%
&:hover {
background-color: darken(#3498db, 10%);
}
// 变亮10%
&:active {
background-color: lighten(#3498db, 10%);
}
}
// 数学函数
.progress-bar {
width: percentage(0.5); // 50%
height: unit(20, px); // 20px
}
```
## 场景样式隔离
PE引擎确保每个场景的样式只作用于该场景内的元素避免样式冲突。
### 样式作用域
每个场景的样式文件会自动包装在特定的选择器中:
```less
/* scenes/home/index.less */
// 原始样式
.welcome-box {
background-color: #3498db;
}
// 实际生成的样式
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
background-color: #3498db;
}
```
### 全局样式
你可以在项目根目录创建全局样式文件:
```less
/* styles/base.less */
// 全局变量
@primary-color: #3498db;
@secondary-color: #2ecc71;
@text-color: #2c3e50;
@bg-color: #ecf0f1;
// 全局重置样式
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
color: @text-color;
background-color: @bg-color;
}
// 全局工具类
.text-center {
text-align: center;
}
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
```
在主文件中引入全局样式:
```javascript
// main.js
import './styles/base.less'
import PE from 'pe-engine'
```
## 样式组织最佳实践
### 按功能组织样式
```less
/* styles/components.less */
// 按钮组件
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
&.btn-primary {
background-color: @primary-color;
color: white;
&:hover {
background-color: darken(@primary-color, 10%);
}
}
&.btn-secondary {
background-color: @secondary-color;
color: white;
&:hover {
background-color: darken(@secondary-color, 10%);
}
}
}
// 卡片组件
.card {
background-color: white;
border-radius: 8px;
padding: 20px;
.box-shadow();
.card-header {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
}
.card-body {
font-size: 14px;
line-height: 1.5;
}
}
```
### 响应式样式
使用媒体查询创建响应式设计:
```less
/* styles/responsive.less */
// 响应式断点变量
@screen-sm: 576px;
@screen-md: 768px;
@screen-lg: 992px;
@screen-xl: 1200px;
// 媒体查询混合
.responsive(@rules) {
@media (max-width: @screen-sm) {
@rules();
}
}
.responsive-md(@rules) {
@media (min-width: @screen-md) {
@rules();
}
}
// 响应式网格系统
.container {
width: 100%;
padding: 0 15px;
margin: 0 auto;
.responsive({
max-width: 100%;
});
.responsive-md({
max-width: 750px;
});
@media (min-width: @screen-lg) {
max-width: 970px;
}
@media (min-width: @screen-xl) {
max-width: 1170px;
}
}
```
## 动态样式
PE支持在运行时动态修改样式
### 通过JavaScript修改样式
```javascript
// 获取场景元素
const element = game.getSceneElement('my-element')
// 修改单个样式属性
element.setStyle('backgroundColor', '#e74c3c')
// 修改多个样式属性
element.setStyles({
backgroundColor: '#3498db',
color: 'white',
fontSize: '18px'
})
// 添加/移除CSS类
element.addClass('active')
element.removeClass('inactive')
```
### 条件样式
在场景脚本中根据条件动态应用样式:
```html
<sence>
<box class="status-box" :class="{ 'status-online': isOnline, 'status-offline': !isOnline }">
状态: {{ isOnline ? '在线' : '离线' }}
</box>
</sence>
<script>
let isOnline = false
// 模拟状态变化
setInterval(() => {
isOnline = !isOnline
}, 5000)
onShow(() => {
// 根据状态更新样式
const statusBox = game.getSceneElement('status-box')
if (isOnline) {
statusBox.setStyles({
backgroundColor: '#2ecc71',
color: 'white'
})
} else {
statusBox.setStyles({
backgroundColor: '#e74c3c',
color: 'white'
})
}
})
</script>
```
## CSS自定义属性CSS变量
PE也支持使用CSS自定义属性
```less
/* styles/theme.less */
:root {
--primary-color: #3498db;
--secondary-color: #2ecc71;
--text-color: #2c3e50;
--bg-color: #ecf0f1;
}
.themed-element {
background-color: var(--primary-color);
color: var(--text-color);
&:hover {
background-color: var(--secondary-color);
}
}
```
## 样式调试
### 开发者工具
在浏览器开发者工具中你可以看到PE为每个场景生成的特定选择器
```css
/* 实际生成的样式 */
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
background-color: #3498db;
}
```
### 样式覆盖
如果需要覆盖场景样式,可以使用更具体的选择器:
```less
/* 覆盖特定场景的样式 */
[data-scene-id="pe-scene-1634567890-abc123"] .welcome-box {
background-color: #e74c3c !important;
}
```
## 完整示例
以下是一个完整的样式处理示例:
### styles/variables.less
```less
// 颜色变量
@colors: {
primary: #3498db;
secondary: #2ecc71;
success: #27ae60;
warning: #f39c12;
danger: #e74c3c;
info: #3498db;
light: #f8f9fa;
dark: #343a40;
}
// 尺寸变量
@sizes: {
xs: 0.25rem;
sm: 0.5rem;
md: 1rem;
lg: 1.5rem;
xl: 3rem;
}
// 字体变量
@fonts: {
family: 'Helvetica Neue', Arial, sans-serif;
size-base: 16px;
size-sm: 0.875rem;
size-lg: 1.25rem;
weight-normal: 400;
weight-bold: 700;
}
```
### styles/mixins.less
```less
// 清除浮动
.clearfix() {
&::after {
display: block;
content: "";
clear: both;
}
}
// 文本省略
.text-ellipsis() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 三角形
.triangle(@direction: down, @size: 5px, @color: #000) {
width: 0;
height: 0;
border-style: solid;
when (@direction = up) {
border-width: 0 @size @size @size;
border-color: transparent transparent @color transparent;
}
when (@direction = down) {
border-width: @size @size 0 @size;
border-color: @color transparent transparent transparent;
}
when (@direction = left) {
border-width: @size @size @size 0;
border-color: transparent @color transparent transparent;
}
when (@direction = right) {
border-width: @size 0 @size @size;
border-color: transparent transparent transparent @color;
}
}
```
### scenes/dashboard/index.less
```less
@import '../../styles/variables.less';
@import '../../styles/mixins.less';
.dashboard {
padding: @sizes[md];
.clearfix();
.header {
margin-bottom: @sizes[lg];
.title {
font-size: @fonts[size-lg];
font-weight: @fonts[weight-bold];
color: @colors[dark];
.text-ellipsis();
}
.subtitle {
font-size: @fonts[size-sm];
color: lighten(@colors[dark], 30%);
}
}
.stats-container {
display: flex;
flex-wrap: wrap;
gap: @sizes[md];
margin-bottom: @sizes[lg];
.stat-card {
flex: 1;
min-width: 200px;
background-color: white;
border-radius: @sizes[sm];
padding: @sizes[md];
.box-shadow(0, 2px, 8px, rgba(0,0,0,0.1));
.stat-value {
font-size: 2rem;
font-weight: @fonts[weight-bold];
color: @colors[primary];
margin-bottom: @sizes[sm];
}
.stat-label {
font-size: @fonts[size-sm];
color: lighten(@colors[dark], 40%);
}
}
}
.chart-container {
background-color: white;
border-radius: @sizes[sm];
padding: @sizes[md];
.box-shadow(0, 2px, 8px, rgba(0,0,0,0.1));
.chart-title {
font-size: @fonts[size-base];
font-weight: @fonts[weight-bold];
margin-bottom: @sizes[md];
}
}
@media (max-width: 768px) {
.stats-container {
flex-direction: column;
.stat-card {
min-width: 100%;
}
}
}
}
```
通过以上内容你已经了解了PE引擎的样式处理机制。在下一章节中我们将探讨事件处理的相关内容。

169
guide/introduction.md Normal file
View File

@@ -0,0 +1,169 @@
# 简介
你正在阅读的是 Pandona Engine (PE) 的文档!
PE 是一款用于构建 2D 游戏和动画的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发 2D 游戏和动画。无论是简单的互动动画还是复杂的 2D 游戏PE 都可以胜任。
下面是一个最基本的示例:
```javascript
import PE from 'PE'
// 创建游戏实例
const game = PE.create()
// 创建一个精灵元素
const sprite = game.create('sprite', {
x: 100,
y: 100,
width: 50,
height: 50
})
// 将精灵添加到游戏中
game.addGameObject(sprite)
```
```html
<sence>
<sprite class="my-sprite" @click="handleClick"></sprite>
<box class="my-box">Hello PE!</box>
</sence>
<script>
function handleClick() {
console.log('Sprite clicked!')
}
</script>
```
**结果展示**
上面的示例展示了 PE 的核心功能:
- **声明式渲染**PE 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述游戏场景和元素。
- **响应性**PE 会自动跟踪游戏状态并在其发生变化时响应式地更新渲染。
- **组件化**PE 支持基于场景的组件化开发,每个场景可以包含多个元素。
## 什么是 PE
PE (Pandona Engine) 是一款用于构建 2D 游戏和动画的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发 2D 游戏和动画。
## 渐进式框架
PE 是一个框架,也是一个生态。其功能覆盖了大部分 2D 游戏和动画开发常见的需求。但游戏开发世界是十分多样化的不同的开发者在构建的东西可能在形式和规模上会有很大的不同。考虑到这一点PE 的设计非常注重灵活性和"可以被逐步集成"这个特点。根据你的需求场景,你可以用不同的方式使用 PE
- 无需构建步骤,渐进式增强静态的 HTML
- 在任何页面中作为 Web Components 嵌入
- 单场景应用 (SSA)
- 全屏 / 画布渲染 (CSR)
- 开发桌面端、移动端 2D 游戏
## 场景组件
在大多数 PE 项目中,我们可以使用一种类似 HTML 格式的文件来书写游戏场景,它被称为**场景文件** (也被称为 `*.pe` 文件)。顾名义PE 的场景文件会将一个场景的逻辑 (JavaScript),模板 (HTML) 和样式 (CSS) 封装在同一个文件里。下面我们将用场景文件的格式重写上面的示例:
```html
<sence>
<sprite class="my-sprite" @click="handleClick"></sprite>
<box class="my-box">Hello PE!</box>
</sence>
<script>
function handleClick() {
console.log('Sprite clicked!')
}
</script>
<style>
.my-sprite {
width: 50px;
height: 50px;
background-color: blue;
}
.my-box {
color: white;
background-color: red;
}
</style>
```
场景文件是 PE 的标志性功能。你可以使用它来编写游戏场景。你可以在后续相关章节里了解更多关于场景文件的用法及用途。
## API 风格
PE 的场景可以按两种不同的风格书写:**模板式 API** 和 **编程式 API**
### 模板式 API (Template API)
使用模板式 API我们可以在 `.pe` 文件中使用类似 HTML 的语法来描述场景元素,并在 `<script>` 标签中编写逻辑代码。
```html
<sence>
<sprite class="my-sprite" @click="increment"></sprite>
<text class="counter">Count is: {{ count }}</text>
</sence>
<script>
let count = 0
function increment() {
count++
}
</script>
```
### 编程式 API (Programmatic API)
通过编程式 API我们可以使用 JavaScript 代码直接创建和管理游戏元素。
```javascript
import PE from 'PE'
// 创建游戏实例
const game = PE.create()
// 创建响应式状态
let count = 0
// 创建精灵元素
const sprite = game.create('sprite', {
x: 100,
y: 100,
width: 50,
height: 50
})
// 添加点击事件
sprite.element.addEventListener('click', () => {
count++
})
// 将精灵添加到游戏中
game.addGameObject(sprite)
```
### 该选哪一个?
两种 API 风格都能够覆盖大部分的游戏开发场景。它们只是同一个底层系统所提供的两套不同的接口。
模板式 API 以"场景"的概念为中心,对于有 Web 开发背景的用户来说,这通常与基于 HTML 的心智模型更为一致。同时,它将渲染相关的细节抽象出来,并强制按照模板和脚本的方式来组织代码,从而对初学者而言更为友好。
编程式 API 的核心思想是直接使用 JavaScript 代码来创建和管理游戏元素。这种形式更加自由,也需要你对 PE 的系统有更深的理解才能高效使用。相应的,它的灵活性也使得组织和重用游戏逻辑的模式变得更加强大。
如果你是使用 PE 的新手,这里是我们的大致建议:
- 在学习的过程中,推荐采用更易于自己理解的风格。再强调一下,大部分的核心概念在这两种风格之间都是通用的。熟悉了一种风格以后,你也能够很快地理解另一种风格。
- 在生产项目中:
- 当你不需要使用构建工具,或者打算主要在低复杂度的场景中使用 PE推荐采用模板式 API。
- 当你打算用 PE 构建完整的游戏项目,推荐采用编程式 API。
在学习阶段,你不必只固守一种风格。在接下来的文档中我们会为你提供一系列两种风格的代码供你参考。
## 还有其他问题?
请查看我们的 FAQ。
## 选择你的学习路径
不同的开发者有不同的学习方式。尽管在可能的情况下,我们推荐你通读所有内容,但你还是可以自由地选择一种自己喜欢的学习路径!

216
guide/quick-start.md Normal file
View File

@@ -0,0 +1,216 @@
# 快速上手
本指南将帮助你快速开始使用PE引擎创建你的第一个应用。
## 创建一个简单的PE应用
### 1. 安装
首先创建一个新的项目目录并初始化npm
```bash
mkdir my-pe-app
cd my-pe-app
npm init -y
```
然后安装PE引擎
```bash
npm install pe-engine
```
### 2. 创建HTML文件
在项目根目录下创建`index.html`文件:
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的PE应用</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.js"></script>
</body>
</html>
```
### 3. 创建主JavaScript文件
创建`main.js`文件:
```javascript
import PE from 'pe-engine'
// 创建应用实例
const { game } = PE.create()
// 添加一个简单的精灵
const sprite = game.create('sprite')
sprite.setPosition(100, 100)
sprite.setSize(100, 100)
// 设置精灵样式
sprite.element.style.backgroundColor = '#3498db'
sprite.element.style.borderRadius = '50%'
```
### 4. 启动开发服务器
安装Vite作为开发服务器
```bash
npm install -D vite
```
`package.json`中添加脚本:
```json
{
"scripts": {
"dev": "vite",
"build": "vite build"
}
}
```
启动开发服务器:
```bash
npm run dev
```
现在你应该能在浏览器中看到一个蓝色的圆形精灵了!
## 使用场景系统
PE引擎提供了强大的场景系统让你可以轻松管理不同的应用状态。
### 1. 创建场景目录结构
```
project/
├── index.html
├── main.js
├── scenes/
│ ├── home/
│ │ ├── index.pe
│ │ └── index.less
│ └── about/
│ ├── index.pe
│ └── index.less
└── styles/
└── base.less
```
### 2. 创建场景配置文件
创建`scenes.json`文件来定义路由:
```json
{
"scenes": [
{
"path": "/",
"title": "主页"
},
{
"path": "/about",
"title": "关于"
}
]
}
```
### 3. 创建场景文件
创建`scenes/home/index.pe`
```html
<sence>
<box class="welcome-box">欢迎来到PE引擎!</box>
<sprite class="logo"></sprite>
<box class="nav-button" @click="PE.navigateTo('/about')">关于我们</box>
</sence>
<script>
onLoad(() => {
console.log('主页加载完成')
})
onShow(() => {
console.log('主页显示')
})
</script>
```
创建`scenes/home/index.less`
```less
.welcome-box {
width: 300px;
height: 100px;
background-color: #2c3e50;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
position: absolute;
top: 100px;
left: 50%;
transform: translateX(-50%);
border-radius: 8px;
}
.logo {
width: 150px;
height: 150px;
background-color: #3498db;
border-radius: 50%;
position: absolute;
top: 250px;
left: 50%;
transform: translateX(-50%);
}
.nav-button {
width: 200px;
height: 50px;
background-color: #e74c3c;
color: white;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
border-radius: 4px;
cursor: pointer;
}
```
### 4. 更新主文件
更新`main.js`以使用场景系统:
```javascript
import './styles/base.less'
import PE from 'pe-engine'
// PE引擎会自动加载scenes.json中的路由配置
// 并根据路由显示相应的场景
```
现在你已经创建了一个基本的PE应用它使用场景系统来管理不同的视图。
## 下一步
- 阅读[基础教程](./essentials/)了解PE引擎的核心概念
- 查看[API参考](./api/)了解所有可用的API
- 探索[进阶主题](./advanced/)学习更高级的功能

12
index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pandona Engine</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.js"></script>
</body>
</html>

335
index.js
View File

@@ -1,217 +1,142 @@
class BaseElement {
constructor(elementType = 'div', engineId) {
this.element = document.createElement(elementType)
this.element.style.position = 'absolute'
this.engineId = engineId
this.x = 0
this.y = 0
this.width = 0
this.height = 0
}
// 创建框架入口文件
import Engine from './src/core/Engine.js';
import SimplifiedEngine, { PE } from './src/core/SimplifiedEngine.js';
setPosition(x = 0, y = 0) {
this.element.style.left = `${x}px`
this.element.style.top = `${y}px`
this.x = x
this.y = y
}
// 导入所有类以便外部使用
import BaseElement from './src/elements/BaseElement.js';
import Sprite from './src/elements/Sprite.js';
import Box from './src/elements/Box.js';
import TextElement from './src/elements/TextElement.js';
import HtmlElement from './src/elements/HtmlElement.js';
import SvgElement from './src/elements/SvgElement.js';
import GameObject from './src/elements/GameObject.js';
import { Component, PhysicsComponent, ColliderComponent, AnimationComponent, InputComponent, LifecycleComponent } from './src/elements/Component.js';
setSize(width = 0, height = 0) {
if (width) {
this.element.style.width = typeof width === 'number' ? `${width}px` : width
this.width = width
}
if (height) {
this.element.style.height = typeof height === 'number' ? `${height}px` : height
this.height = height
}
}
// 导入简化版元素类
import { SimplifiedSprite, SimplifiedBox, SimplifiedText, SimplifiedHtml, SimplifiedSvg } from './src/elements/SimplifiedElements.js';
import { SimplifiedGameObject, SimplifiedComponentFactory, SimplifiedGameObjectFactory } from './src/elements/SimplifiedGameObject.js';
setStyle(style) {
Object.assign(this.element.style, style)
}
import EventBus from './src/utils/EventBus.js';
import { SimplifiedEventBus, eventBus, EVENTS } from './src/utils/SimplifiedEventBus.js';
add(child) {
child && this.element.appendChild(child.element || child)
}
import ResourceManager from './src/managers/ResourceManager.js';
import SceneManager from './src/managers/SceneManager.js';
import AudioManager from './src/managers/AudioManager.js';
import Camera from './src/managers/Camera.js';
remove(child) {
child && this.element.removeChild(child.element || child)
}
// 导入简化版管理器
import { SimplifiedResourceManager } from './src/managers/SimplifiedResourceManager.js';
import { SimplifiedSceneManager } from './src/managers/SimplifiedSceneManager.js';
import { SimplifiedAudioManager } from './src/managers/SimplifiedAudioManager.js';
import { SimplifiedCamera } from './src/managers/SimplifiedCamera.js';
on(event, callback) {
this.element.addEventListener(event, callback)
}
import Tween from './src/animation/Tween.js';
import AnimationSystem from './src/animation/AnimationSystem.js';
import AnimationController from './src/animation/AnimationController.js';
import { SimplifiedAnimation } from './src/animation/SimplifiedAnimation.js';
off(event, callback) {
this.element.removeEventListener(event, callback)
}
import { Particle, ParticleEmitter, ParticleSystem } from './src/effects/ParticleSystem.js';
import { SimplifiedParticleSystem, SimplifiedParticleEmitter, SimplifiedParticleFactory } from './src/effects/SimplifiedParticleSystem.js';
hide() {
this.element.style.display = 'none'
}
import { UIElement, UIButton, UILabel, UIImage, UISlider, UIManager } from './src/ui/UI.js';
import { SimplifiedUIManager, SimplifiedButton, SimplifiedLabel, SimplifiedImage, SimplifiedSlider } from './src/ui/SimplifiedUI.js';
show() {
this.element.style.display = 'block'
}
}
import { SimplifiedComponent, SimplifiedPhysicsComponent, SimplifiedColliderComponent, SimplifiedAnimationComponent, SimplifiedInputComponent, SimplifiedLifecycleComponent, SimplifiedComponentFactory as ComponentFactory } from './src/elements/SimplifiedComponent.js';
class Sprite extends BaseElement {
constructor(img, x, y, width, height, engineId) {
super('div', engineId)
this.imgElement = document.createElement('img')
this.imgElement.src = img.src
this.imgElement.style.cssText = '-webkit-user-drag: none; user-drag: none;'
this.element.appendChild(this.imgElement)
// 导出所有类以便外部使用
export {
// 核心类
Engine,
SimplifiedEngine,
// 元素类
BaseElement,
Sprite,
Box,
TextElement,
HtmlElement,
SvgElement,
GameObject,
// 简化版元素类
SimplifiedSprite,
SimplifiedBox,
SimplifiedText,
SimplifiedHtml,
SimplifiedSvg,
// 游戏对象类
SimplifiedGameObject,
SimplifiedGameObjectFactory,
// 组件类
Component,
PhysicsComponent,
ColliderComponent,
AnimationComponent,
InputComponent,
LifecycleComponent,
// 简化版组件类
SimplifiedComponent,
SimplifiedPhysicsComponent,
SimplifiedColliderComponent,
SimplifiedAnimationComponent,
SimplifiedInputComponent,
SimplifiedLifecycleComponent,
SimplifiedComponentFactory,
ComponentFactory,
// 工具类
EventBus,
SimplifiedEventBus,
eventBus,
EVENTS,
// 管理器
ResourceManager,
SceneManager,
AudioManager,
Camera,
// 简化版管理器
SimplifiedResourceManager,
SimplifiedSceneManager,
SimplifiedAudioManager,
SimplifiedCamera,
// 动画系统
Tween,
AnimationSystem,
AnimationController,
SimplifiedAnimation,
// 特效系统
Particle,
ParticleEmitter,
ParticleSystem,
SimplifiedParticleSystem,
SimplifiedParticleEmitter,
SimplifiedParticleFactory,
// UI系统
UIElement,
UIButton,
UILabel,
UIImage,
UISlider,
UIManager,
SimplifiedUIManager,
SimplifiedButton,
SimplifiedLabel,
SimplifiedImage,
SimplifiedSlider,
// 简化版全局对象
PE
};
this.setPosition(x, y)
this.setSize(width, height)
this.element.setAttribute('data-sprite-id', engineId)
// 设置全局变量(可选)
window.PE = PE;
// 精灵特有方法
this.name = name => this.element.setAttribute('data-name', name)
this.animate = keyframes => (this.element.style.animation = keyframes)
}
}
class Box extends BaseElement {
constructor(x, y, width, height, engineId) {
super('div', engineId)
this.setPosition(x, y)
this.setSize(width, height)
this.element.setAttribute('data-box-id', engineId)
}
}
class TextElement extends BaseElement {
constructor(text, x, y, engineId) {
super('div', engineId)
this.element.innerText = text
this.setPosition(x, y)
this.element.setAttribute('data-text-id', engineId)
// 文本特有方法
this.set = text => (this.element.innerText = text)
this.setColor = color => (this.element.style.color = color)
this.setFont = font => (this.element.style.font = font)
}
}
class HtmlElement extends BaseElement {
constructor(html, x, y, engineId) {
super('div', engineId)
this.element.innerHTML = html
this.setPosition(x, y)
this.element.setAttribute('data-html-id', engineId)
// HTML特有方法
this.set = html => (this.element.innerHTML = html)
this.get = () => this.element.innerHTML
this.getSize = () => {
const rect = this.element.getBoundingClientRect()
return { width: rect.width, height: rect.height }
}
}
}
class SvgElement extends BaseElement {
constructor(x, y, width, height, content, engineId) {
super('div', engineId)
this.element.innerHTML = content
this.setPosition(x, y)
this.setSize(width, height)
this.element.setAttribute('data-svg-id', engineId)
// SVG特有方法
this.setContent = content => (this.element.innerHTML = content)
}
}
class Engine {
constructor(element = document.body, options = {}) {
const defaultOptions = {
width: 800,
height: 600,
background: 'transparent',
}
this.options = { ...defaultOptions, ...options }
this.images = []
this.width = this.options.width
this.height = this.options.height
this.element = typeof element === 'string' ? document.querySelector(element) : element
this.id = Date.now().toString(16)
this.createStage()
}
createStage() {
this.stage = document.createElement('div')
this.stage.setAttribute('data-engine-id', this.id)
Object.assign(this.stage.style, {
width: `${this.options.width}px`,
height: `${this.options.height}px`,
position: 'relative',
userSelect: 'none',
overflow: 'hidden',
background: this.options.background,
transform: 'translateZ(0)',
})
// 添加舞台操作方法
this.stage.add = child => child && this.stage.appendChild(child.element || child)
this.stage.remove = child => child && this.stage.removeChild(child.element || child)
this.element.appendChild(this.stage)
}
load(src = '') {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = src
img.onload = () => resolve(img)
img.onerror = reject
this.images.push(img)
})
}
createSprite(img, x = 0, y = 0, width = 0, height = 0) {
return new Sprite(img, x, y, width, height, this.id)
}
createBox(x = 0, y = 0, width = 0, height = 0) {
return new Box(x, y, width, height, this.id)
}
createText(text = '', x = 0, y = 0) {
return new TextElement(text, x, y, this.id)
}
createHtml(html = '', x = 0, y = 0) {
return new HtmlElement(html, x, y, this.id)
}
createSvg(x = 0, y = 0, width = 0, height = 0, content = '') {
return new SvgElement(x, y, width, height, content, this.id)
}
destroy() {
if (this.stage && this.stage.parentNode) {
this.stage.parentNode.removeChild(this.stage)
}
// 清理所有图像引用
this.images.forEach(img => {
img.onload = null
img.onerror = null
img.src = ''
})
// 清除所有引用
this.images = []
this.stage = null
this.element = null
this.options = null
}
}
export default Engine;

16
node_modules/.bin/acorn generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../acorn/bin/acorn" "$@"
else
exec node "$basedir/../acorn/bin/acorn" "$@"
fi

17
node_modules/.bin/acorn.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\acorn\bin\acorn" %*

28
node_modules/.bin/acorn.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../acorn/bin/acorn" $args
} else {
& "$basedir/node$exe" "$basedir/../acorn/bin/acorn" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../acorn/bin/acorn" $args
} else {
& "node$exe" "$basedir/../acorn/bin/acorn" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/errno generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../errno/cli.js" "$@"
else
exec node "$basedir/../errno/cli.js" "$@"
fi

17
node_modules/.bin/errno.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\errno\cli.js" %*

28
node_modules/.bin/errno.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../errno/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../errno/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../errno/cli.js" $args
} else {
& "node$exe" "$basedir/../errno/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/esbuild generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../esbuild/bin/esbuild" "$@"
else
exec node "$basedir/../esbuild/bin/esbuild" "$@"
fi

17
node_modules/.bin/esbuild.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\esbuild\bin\esbuild" %*

28
node_modules/.bin/esbuild.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
} else {
& "$basedir/node$exe" "$basedir/../esbuild/bin/esbuild" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../esbuild/bin/esbuild" $args
} else {
& "node$exe" "$basedir/../esbuild/bin/esbuild" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/image-size generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../image-size/bin/image-size.js" "$@"
else
exec node "$basedir/../image-size/bin/image-size.js" "$@"
fi

17
node_modules/.bin/image-size.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\image-size\bin\image-size.js" %*

28
node_modules/.bin/image-size.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../image-size/bin/image-size.js" $args
} else {
& "$basedir/node$exe" "$basedir/../image-size/bin/image-size.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../image-size/bin/image-size.js" $args
} else {
& "node$exe" "$basedir/../image-size/bin/image-size.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/lessc generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../less/bin/lessc" "$@"
else
exec node "$basedir/../less/bin/lessc" "$@"
fi

17
node_modules/.bin/lessc.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\less\bin\lessc" %*

28
node_modules/.bin/lessc.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../less/bin/lessc" $args
} else {
& "$basedir/node$exe" "$basedir/../less/bin/lessc" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../less/bin/lessc" $args
} else {
& "node$exe" "$basedir/../less/bin/lessc" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/mime generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../mime/cli.js" "$@"
else
exec node "$basedir/../mime/cli.js" "$@"
fi

17
node_modules/.bin/mime.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\mime\cli.js" %*

28
node_modules/.bin/mime.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../mime/cli.js" $args
} else {
& "$basedir/node$exe" "$basedir/../mime/cli.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../mime/cli.js" $args
} else {
& "node$exe" "$basedir/../mime/cli.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/nanoid generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../nanoid/bin/nanoid.cjs" "$@"
else
exec node "$basedir/../nanoid/bin/nanoid.cjs" "$@"
fi

17
node_modules/.bin/nanoid.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\nanoid\bin\nanoid.cjs" %*

28
node_modules/.bin/nanoid.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
} else {
& "$basedir/node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
} else {
& "node$exe" "$basedir/../nanoid/bin/nanoid.cjs" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/needle generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../needle/bin/needle" "$@"
else
exec node "$basedir/../needle/bin/needle" "$@"
fi

17
node_modules/.bin/needle.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\needle\bin\needle" %*

28
node_modules/.bin/needle.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../needle/bin/needle" $args
} else {
& "$basedir/node$exe" "$basedir/../needle/bin/needle" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../needle/bin/needle" $args
} else {
& "node$exe" "$basedir/../needle/bin/needle" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/rollup generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../rollup/dist/bin/rollup" "$@"
else
exec node "$basedir/../rollup/dist/bin/rollup" "$@"
fi

17
node_modules/.bin/rollup.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\rollup\dist\bin\rollup" %*

28
node_modules/.bin/rollup.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
} else {
& "$basedir/node$exe" "$basedir/../rollup/dist/bin/rollup" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
} else {
& "node$exe" "$basedir/../rollup/dist/bin/rollup" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/semver generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../semver/bin/semver" "$@"
else
exec node "$basedir/../semver/bin/semver" "$@"
fi

17
node_modules/.bin/semver.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver" %*

28
node_modules/.bin/semver.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../semver/bin/semver" $args
} else {
& "$basedir/node$exe" "$basedir/../semver/bin/semver" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../semver/bin/semver" $args
} else {
& "node$exe" "$basedir/../semver/bin/semver" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/terser generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../terser/bin/terser" "$@"
else
exec node "$basedir/../terser/bin/terser" "$@"
fi

17
node_modules/.bin/terser.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\terser\bin\terser" %*

28
node_modules/.bin/terser.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../terser/bin/terser" $args
} else {
& "$basedir/node$exe" "$basedir/../terser/bin/terser" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../terser/bin/terser" $args
} else {
& "node$exe" "$basedir/../terser/bin/terser" $args
}
$ret=$LASTEXITCODE
}
exit $ret

16
node_modules/.bin/vite generated vendored Normal file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*)
if command -v cygpath > /dev/null 2>&1; then
basedir=`cygpath -w "$basedir"`
fi
;;
esac
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@"
else
exec node "$basedir/../vite/bin/vite.js" "$@"
fi

17
node_modules/.bin/vite.cmd generated vendored Normal file
View File

@@ -0,0 +1,17 @@
@ECHO off
GOTO start
:find_dp0
SET dp0=%~dp0
EXIT /b
:start
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\vite\bin\vite.js" %*

28
node_modules/.bin/vite.ps1 generated vendored Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
} else {
& "$basedir/node$exe" "$basedir/../vite/bin/vite.js" $args
}
$ret=$LASTEXITCODE
} else {
# Support pipeline input
if ($MyInvocation.ExpectingInput) {
$input | & "node$exe" "$basedir/../vite/bin/vite.js" $args
} else {
& "node$exe" "$basedir/../vite/bin/vite.js" $args
}
$ret=$LASTEXITCODE
}
exit $ret

932
node_modules/.package-lock.json generated vendored Normal file
View File

@@ -0,0 +1,932 @@
{
"name": "pandona-engine",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
"cpu": [
"arm"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
"cpu": [
"arm64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
"cpu": [
"x64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
"cpu": [
"arm64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
"cpu": [
"x64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
"cpu": [
"arm64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
"cpu": [
"x64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
"cpu": [
"arm"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
"cpu": [
"arm64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
"cpu": [
"ia32"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
"cpu": [
"loong64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
"cpu": [
"mips64el"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
"cpu": [
"ppc64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
"cpu": [
"riscv64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
"cpu": [
"s390x"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
"cpu": [
"x64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
"cpu": [
"x64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
"cpu": [
"x64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
"cpu": [
"x64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
"cpu": [
"arm64"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
"cpu": [
"ia32"
],
"dev": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.25"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true,
"license": "MIT"
},
"node_modules/commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"license": "MIT"
},
"node_modules/copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
"integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-what": "^3.14.1"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/dotenv": {
"version": "17.2.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/errno": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"prr": "~1.0.1"
},
"bin": {
"errno": "cli.js"
}
},
"node_modules/esbuild": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.18.20",
"@esbuild/android-arm64": "0.18.20",
"@esbuild/android-x64": "0.18.20",
"@esbuild/darwin-arm64": "0.18.20",
"@esbuild/darwin-x64": "0.18.20",
"@esbuild/freebsd-arm64": "0.18.20",
"@esbuild/freebsd-x64": "0.18.20",
"@esbuild/linux-arm": "0.18.20",
"@esbuild/linux-arm64": "0.18.20",
"@esbuild/linux-ia32": "0.18.20",
"@esbuild/linux-loong64": "0.18.20",
"@esbuild/linux-mips64el": "0.18.20",
"@esbuild/linux-ppc64": "0.18.20",
"@esbuild/linux-riscv64": "0.18.20",
"@esbuild/linux-s390x": "0.18.20",
"@esbuild/linux-x64": "0.18.20",
"@esbuild/netbsd-x64": "0.18.20",
"@esbuild/openbsd-x64": "0.18.20",
"@esbuild/sunos-x64": "0.18.20",
"@esbuild/win32-arm64": "0.18.20",
"@esbuild/win32-ia32": "0.18.20",
"@esbuild/win32-x64": "0.18.20"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"ideallyInert": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
"license": "ISC",
"optional": true
},
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/image-size": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
"integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
"dev": true,
"license": "MIT",
"optional": true,
"bin": {
"image-size": "bin/image-size.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-what": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
"integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
"dev": true,
"license": "MIT"
},
"node_modules/less": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/less/-/less-4.4.1.tgz",
"integrity": "sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"copy-anything": "^2.0.1",
"parse-node-version": "^1.0.1",
"tslib": "^2.3.0"
},
"bin": {
"lessc": "bin/lessc"
},
"engines": {
"node": ">=14"
},
"optionalDependencies": {
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"needle": "^3.1.0",
"source-map": "~0.6.0"
}
},
"node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
"integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"pify": "^4.0.1",
"semver": "^5.6.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"dev": true,
"license": "MIT",
"optional": true,
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/needle": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz",
"integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"iconv-lite": "^0.6.3",
"sax": "^1.2.4"
},
"bin": {
"needle": "bin/needle"
},
"engines": {
"node": ">= 4.4.x"
}
},
"node_modules/parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/pify": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
"node": ">=6"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/rollup": {
"version": "3.29.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz",
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true,
"license": "MIT",
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=14.18.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/sax": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"dev": true,
"license": "ISC",
"optional": true
},
"node_modules/semver": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"license": "ISC",
"optional": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/terser": {
"version": "5.44.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
"integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
"bin": {
"terser": "bin/terser"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/vite": {
"version": "4.5.14",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz",
"integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.18.10",
"postcss": "^8.4.27",
"rollup": "^3.27.1"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"@types/node": ">= 14",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

3
node_modules/.vite/deps_temp_010fc2f4/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_18b76d8e/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_2e8f7c60/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_395e6c76/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_5ae8cc47/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_6ce8b2a3/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_747eaad1/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_83a3f12a/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_95c788c0/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_db29f874/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_eea5b4f0/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_f0bd8d06/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/.vite/deps_temp_f4e5b100/package.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

3
node_modules/@esbuild/win32-x64/README.md generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# esbuild
This is the Windows 64-bit binary for esbuild, a JavaScript bundler and minifier. See https://github.com/evanw/esbuild for details.

BIN
node_modules/@esbuild/win32-x64/esbuild.exe generated vendored Normal file

Binary file not shown.

17
node_modules/@esbuild/win32-x64/package.json generated vendored Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "@esbuild/win32-x64",
"version": "0.18.20",
"description": "The Windows 64-bit binary for esbuild, a JavaScript bundler.",
"repository": "https://github.com/evanw/esbuild",
"license": "MIT",
"preferUnplugged": true,
"engines": {
"node": ">=12"
},
"os": [
"win32"
],
"cpu": [
"x64"
]
}

19
node_modules/@jridgewell/gen-mapping/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright 2024 Justin Ridgewell <justin@ridgewell.name>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

227
node_modules/@jridgewell/gen-mapping/README.md generated vendored Normal file
View File

@@ -0,0 +1,227 @@
# @jridgewell/gen-mapping
> Generate source maps
`gen-mapping` allows you to generate a source map during transpilation or minification.
With a source map, you're able to trace the original location in the source file, either in Chrome's
DevTools or using a library like [`@jridgewell/trace-mapping`][trace-mapping].
You may already be familiar with the [`source-map`][source-map] package's `SourceMapGenerator`. This
provides the same `addMapping` and `setSourceContent` API.
## Installation
```sh
npm install @jridgewell/gen-mapping
```
## Usage
```typescript
import { GenMapping, addMapping, setSourceContent, toEncodedMap, toDecodedMap } from '@jridgewell/gen-mapping';
const map = new GenMapping({
file: 'output.js',
sourceRoot: 'https://example.com/',
});
setSourceContent(map, 'input.js', `function foo() {}`);
addMapping(map, {
// Lines start at line 1, columns at column 0.
generated: { line: 1, column: 0 },
source: 'input.js',
original: { line: 1, column: 0 },
});
addMapping(map, {
generated: { line: 1, column: 9 },
source: 'input.js',
original: { line: 1, column: 9 },
name: 'foo',
});
assert.deepEqual(toDecodedMap(map), {
version: 3,
file: 'output.js',
names: ['foo'],
sourceRoot: 'https://example.com/',
sources: ['input.js'],
sourcesContent: ['function foo() {}'],
mappings: [
[ [0, 0, 0, 0], [9, 0, 0, 9, 0] ]
],
});
assert.deepEqual(toEncodedMap(map), {
version: 3,
file: 'output.js',
names: ['foo'],
sourceRoot: 'https://example.com/',
sources: ['input.js'],
sourcesContent: ['function foo() {}'],
mappings: 'AAAA,SAASA',
});
```
### Smaller Sourcemaps
Not everything needs to be added to a sourcemap, and needless markings can cause signficantly
larger file sizes. `gen-mapping` exposes `maybeAddSegment`/`maybeAddMapping` APIs that will
intelligently determine if this marking adds useful information. If not, the marking will be
skipped.
```typescript
import { maybeAddMapping } from '@jridgewell/gen-mapping';
const map = new GenMapping();
// Adding a sourceless marking at the beginning of a line isn't useful.
maybeAddMapping(map, {
generated: { line: 1, column: 0 },
});
// Adding a new source marking is useful.
maybeAddMapping(map, {
generated: { line: 1, column: 0 },
source: 'input.js',
original: { line: 1, column: 0 },
});
// But adding another marking pointing to the exact same original location isn't, even if the
// generated column changed.
maybeAddMapping(map, {
generated: { line: 1, column: 9 },
source: 'input.js',
original: { line: 1, column: 0 },
});
assert.deepEqual(toEncodedMap(map), {
version: 3,
names: [],
sources: ['input.js'],
sourcesContent: [null],
mappings: 'AAAA',
});
```
## Benchmarks
```
node v18.0.0
amp.js.map
Memory Usage:
gen-mapping: addSegment 5852872 bytes
gen-mapping: addMapping 7716042 bytes
source-map-js 6143250 bytes
source-map-0.6.1 6124102 bytes
source-map-0.8.0 6121173 bytes
Smallest memory usage is gen-mapping: addSegment
Adding speed:
gen-mapping: addSegment x 441 ops/sec ±2.07% (90 runs sampled)
gen-mapping: addMapping x 350 ops/sec ±2.40% (86 runs sampled)
source-map-js: addMapping x 169 ops/sec ±2.42% (80 runs sampled)
source-map-0.6.1: addMapping x 167 ops/sec ±2.56% (80 runs sampled)
source-map-0.8.0: addMapping x 168 ops/sec ±2.52% (80 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 150,824,370 ops/sec ±0.07% (102 runs sampled)
gen-mapping: encoded output x 663 ops/sec ±0.22% (98 runs sampled)
source-map-js: encoded output x 197 ops/sec ±0.45% (84 runs sampled)
source-map-0.6.1: encoded output x 198 ops/sec ±0.33% (85 runs sampled)
source-map-0.8.0: encoded output x 197 ops/sec ±0.06% (93 runs sampled)
Fastest is gen-mapping: decoded output
***
babel.min.js.map
Memory Usage:
gen-mapping: addSegment 37578063 bytes
gen-mapping: addMapping 37212897 bytes
source-map-js 47638527 bytes
source-map-0.6.1 47690503 bytes
source-map-0.8.0 47470188 bytes
Smallest memory usage is gen-mapping: addMapping
Adding speed:
gen-mapping: addSegment x 31.05 ops/sec ±8.31% (43 runs sampled)
gen-mapping: addMapping x 29.83 ops/sec ±7.36% (51 runs sampled)
source-map-js: addMapping x 20.73 ops/sec ±6.22% (38 runs sampled)
source-map-0.6.1: addMapping x 20.03 ops/sec ±10.51% (38 runs sampled)
source-map-0.8.0: addMapping x 19.30 ops/sec ±8.27% (37 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 381,379,234 ops/sec ±0.29% (96 runs sampled)
gen-mapping: encoded output x 95.15 ops/sec ±2.98% (72 runs sampled)
source-map-js: encoded output x 15.20 ops/sec ±7.41% (33 runs sampled)
source-map-0.6.1: encoded output x 16.36 ops/sec ±10.46% (31 runs sampled)
source-map-0.8.0: encoded output x 16.06 ops/sec ±6.45% (31 runs sampled)
Fastest is gen-mapping: decoded output
***
preact.js.map
Memory Usage:
gen-mapping: addSegment 416247 bytes
gen-mapping: addMapping 419824 bytes
source-map-js 1024619 bytes
source-map-0.6.1 1146004 bytes
source-map-0.8.0 1113250 bytes
Smallest memory usage is gen-mapping: addSegment
Adding speed:
gen-mapping: addSegment x 13,755 ops/sec ±0.15% (98 runs sampled)
gen-mapping: addMapping x 13,013 ops/sec ±0.11% (101 runs sampled)
source-map-js: addMapping x 4,564 ops/sec ±0.21% (98 runs sampled)
source-map-0.6.1: addMapping x 4,562 ops/sec ±0.11% (99 runs sampled)
source-map-0.8.0: addMapping x 4,593 ops/sec ±0.11% (100 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 379,864,020 ops/sec ±0.23% (93 runs sampled)
gen-mapping: encoded output x 14,368 ops/sec ±4.07% (82 runs sampled)
source-map-js: encoded output x 5,261 ops/sec ±0.21% (99 runs sampled)
source-map-0.6.1: encoded output x 5,124 ops/sec ±0.58% (99 runs sampled)
source-map-0.8.0: encoded output x 5,434 ops/sec ±0.33% (96 runs sampled)
Fastest is gen-mapping: decoded output
***
react.js.map
Memory Usage:
gen-mapping: addSegment 975096 bytes
gen-mapping: addMapping 1102981 bytes
source-map-js 2918836 bytes
source-map-0.6.1 2885435 bytes
source-map-0.8.0 2874336 bytes
Smallest memory usage is gen-mapping: addSegment
Adding speed:
gen-mapping: addSegment x 4,772 ops/sec ±0.15% (100 runs sampled)
gen-mapping: addMapping x 4,456 ops/sec ±0.13% (97 runs sampled)
source-map-js: addMapping x 1,618 ops/sec ±0.24% (97 runs sampled)
source-map-0.6.1: addMapping x 1,622 ops/sec ±0.12% (99 runs sampled)
source-map-0.8.0: addMapping x 1,631 ops/sec ±0.12% (100 runs sampled)
Fastest is gen-mapping: addSegment
Generate speed:
gen-mapping: decoded output x 379,107,695 ops/sec ±0.07% (99 runs sampled)
gen-mapping: encoded output x 5,421 ops/sec ±1.60% (89 runs sampled)
source-map-js: encoded output x 2,113 ops/sec ±1.81% (98 runs sampled)
source-map-0.6.1: encoded output x 2,126 ops/sec ±0.10% (100 runs sampled)
source-map-0.8.0: encoded output x 2,176 ops/sec ±0.39% (98 runs sampled)
Fastest is gen-mapping: decoded output
```
[source-map]: https://www.npmjs.com/package/source-map
[trace-mapping]: https://github.com/jridgewell/sourcemaps/tree/main/packages/trace-mapping

View File

@@ -0,0 +1,292 @@
// src/set-array.ts
var SetArray = class {
constructor() {
this._indexes = { __proto__: null };
this.array = [];
}
};
function cast(set) {
return set;
}
function get(setarr, key) {
return cast(setarr)._indexes[key];
}
function put(setarr, key) {
const index = get(setarr, key);
if (index !== void 0) return index;
const { array, _indexes: indexes } = cast(setarr);
const length = array.push(key);
return indexes[key] = length - 1;
}
function remove(setarr, key) {
const index = get(setarr, key);
if (index === void 0) return;
const { array, _indexes: indexes } = cast(setarr);
for (let i = index + 1; i < array.length; i++) {
const k = array[i];
array[i - 1] = k;
indexes[k]--;
}
indexes[key] = void 0;
array.pop();
}
// src/gen-mapping.ts
import {
encode
} from "@jridgewell/sourcemap-codec";
import { TraceMap, decodedMappings } from "@jridgewell/trace-mapping";
// src/sourcemap-segment.ts
var COLUMN = 0;
var SOURCES_INDEX = 1;
var SOURCE_LINE = 2;
var SOURCE_COLUMN = 3;
var NAMES_INDEX = 4;
// src/gen-mapping.ts
var NO_NAME = -1;
var GenMapping = class {
constructor({ file, sourceRoot } = {}) {
this._names = new SetArray();
this._sources = new SetArray();
this._sourcesContent = [];
this._mappings = [];
this.file = file;
this.sourceRoot = sourceRoot;
this._ignoreList = new SetArray();
}
};
function cast2(map) {
return map;
}
function addSegment(map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
return addSegmentInternal(
false,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
}
function addMapping(map, mapping) {
return addMappingInternal(false, map, mapping);
}
var maybeAddSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
return addSegmentInternal(
true,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
};
var maybeAddMapping = (map, mapping) => {
return addMappingInternal(true, map, mapping);
};
function setSourceContent(map, source, content) {
const {
_sources: sources,
_sourcesContent: sourcesContent
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
sourcesContent[index] = content;
}
function setIgnore(map, source, ignore = true) {
const {
_sources: sources,
_sourcesContent: sourcesContent,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (ignore) put(ignoreList, index);
else remove(ignoreList, index);
}
function toDecodedMap(map) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
// _generatedRanges: generatedRanges,
} = cast2(map);
removeEmptyFinalLines(mappings);
return {
version: 3,
file: map.file || void 0,
names: names.array,
sourceRoot: map.sourceRoot || void 0,
sources: sources.array,
sourcesContent,
mappings,
// originalScopes,
// generatedRanges,
ignoreList: ignoreList.array
};
}
function toEncodedMap(map) {
const decoded = toDecodedMap(map);
return Object.assign({}, decoded, {
// originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
// generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
mappings: encode(decoded.mappings)
});
}
function fromMap(input) {
const map = new TraceMap(input);
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
putAll(cast2(gen)._names, map.names);
putAll(cast2(gen)._sources, map.sources);
cast2(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
cast2(gen)._mappings = decodedMappings(map);
if (map.ignoreList) putAll(cast2(gen)._ignoreList, map.ignoreList);
return gen;
}
function allMappings(map) {
const out = [];
const { _mappings: mappings, _sources: sources, _names: names } = cast2(map);
for (let i = 0; i < mappings.length; i++) {
const line = mappings[i];
for (let j = 0; j < line.length; j++) {
const seg = line[j];
const generated = { line: i + 1, column: seg[COLUMN] };
let source = void 0;
let original = void 0;
let name = void 0;
if (seg.length !== 1) {
source = sources.array[seg[SOURCES_INDEX]];
original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
if (seg.length === 5) name = names.array[seg[NAMES_INDEX]];
}
out.push({ generated, source, original, name });
}
}
return out;
}
function addSegmentInternal(skipable, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names
// _originalScopes: originalScopes,
} = cast2(map);
const line = getIndex(mappings, genLine);
const index = getColumnIndex(line, genColumn);
if (!source) {
if (skipable && skipSourceless(line, index)) return;
return insert(line, index, [genColumn]);
}
assert(sourceLine);
assert(sourceColumn);
const sourcesIndex = put(sources, source);
const namesIndex = name ? put(names, name) : NO_NAME;
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content != null ? content : null;
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
return;
}
return insert(
line,
index,
name ? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex] : [genColumn, sourcesIndex, sourceLine, sourceColumn]
);
}
function assert(_val) {
}
function getIndex(arr, index) {
for (let i = arr.length; i <= index; i++) {
arr[i] = [];
}
return arr[index];
}
function getColumnIndex(line, genColumn) {
let index = line.length;
for (let i = index - 1; i >= 0; index = i--) {
const current = line[i];
if (genColumn >= current[COLUMN]) break;
}
return index;
}
function insert(array, index, value) {
for (let i = array.length; i > index; i--) {
array[i] = array[i - 1];
}
array[index] = value;
}
function removeEmptyFinalLines(mappings) {
const { length } = mappings;
let len = length;
for (let i = len - 1; i >= 0; len = i, i--) {
if (mappings[i].length > 0) break;
}
if (len < length) mappings.length = len;
}
function putAll(setarr, array) {
for (let i = 0; i < array.length; i++) put(setarr, array[i]);
}
function skipSourceless(line, index) {
if (index === 0) return true;
const prev = line[index - 1];
return prev.length === 1;
}
function skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex) {
if (index === 0) return false;
const prev = line[index - 1];
if (prev.length === 1) return false;
return sourcesIndex === prev[SOURCES_INDEX] && sourceLine === prev[SOURCE_LINE] && sourceColumn === prev[SOURCE_COLUMN] && namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME);
}
function addMappingInternal(skipable, map, mapping) {
const { generated, source, original, name, content } = mapping;
if (!source) {
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
null,
null,
null,
null,
null
);
}
assert(original);
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
source,
original.line - 1,
original.column,
name,
content
);
}
export {
GenMapping,
addMapping,
addSegment,
allMappings,
fromMap,
maybeAddMapping,
maybeAddSegment,
setIgnore,
setSourceContent,
toDecodedMap,
toEncodedMap
};
//# sourceMappingURL=gen-mapping.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,358 @@
(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
factory(module, require('@jridgewell/sourcemap-codec'), require('@jridgewell/trace-mapping'));
module.exports = def(module);
} else if (typeof define === 'function' && define.amd) {
define(['module', '@jridgewell/sourcemap-codec', '@jridgewell/trace-mapping'], function(mod) {
factory.apply(this, arguments);
mod.exports = def(mod);
});
} else {
const mod = { exports: {} };
factory(mod, global.sourcemapCodec, global.traceMapping);
global = typeof globalThis !== 'undefined' ? globalThis : global || self;
global.genMapping = def(mod);
}
function def(m) { return 'default' in m.exports ? m.exports.default : m.exports; }
})(this, (function (module, require_sourcemapCodec, require_traceMapping) {
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// umd:@jridgewell/sourcemap-codec
var require_sourcemap_codec = __commonJS({
"umd:@jridgewell/sourcemap-codec"(exports, module2) {
module2.exports = require_sourcemapCodec;
}
});
// umd:@jridgewell/trace-mapping
var require_trace_mapping = __commonJS({
"umd:@jridgewell/trace-mapping"(exports, module2) {
module2.exports = require_traceMapping;
}
});
// src/gen-mapping.ts
var gen_mapping_exports = {};
__export(gen_mapping_exports, {
GenMapping: () => GenMapping,
addMapping: () => addMapping,
addSegment: () => addSegment,
allMappings: () => allMappings,
fromMap: () => fromMap,
maybeAddMapping: () => maybeAddMapping,
maybeAddSegment: () => maybeAddSegment,
setIgnore: () => setIgnore,
setSourceContent: () => setSourceContent,
toDecodedMap: () => toDecodedMap,
toEncodedMap: () => toEncodedMap
});
module.exports = __toCommonJS(gen_mapping_exports);
// src/set-array.ts
var SetArray = class {
constructor() {
this._indexes = { __proto__: null };
this.array = [];
}
};
function cast(set) {
return set;
}
function get(setarr, key) {
return cast(setarr)._indexes[key];
}
function put(setarr, key) {
const index = get(setarr, key);
if (index !== void 0) return index;
const { array, _indexes: indexes } = cast(setarr);
const length = array.push(key);
return indexes[key] = length - 1;
}
function remove(setarr, key) {
const index = get(setarr, key);
if (index === void 0) return;
const { array, _indexes: indexes } = cast(setarr);
for (let i = index + 1; i < array.length; i++) {
const k = array[i];
array[i - 1] = k;
indexes[k]--;
}
indexes[key] = void 0;
array.pop();
}
// src/gen-mapping.ts
var import_sourcemap_codec = __toESM(require_sourcemap_codec());
var import_trace_mapping = __toESM(require_trace_mapping());
// src/sourcemap-segment.ts
var COLUMN = 0;
var SOURCES_INDEX = 1;
var SOURCE_LINE = 2;
var SOURCE_COLUMN = 3;
var NAMES_INDEX = 4;
// src/gen-mapping.ts
var NO_NAME = -1;
var GenMapping = class {
constructor({ file, sourceRoot } = {}) {
this._names = new SetArray();
this._sources = new SetArray();
this._sourcesContent = [];
this._mappings = [];
this.file = file;
this.sourceRoot = sourceRoot;
this._ignoreList = new SetArray();
}
};
function cast2(map) {
return map;
}
function addSegment(map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
return addSegmentInternal(
false,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
}
function addMapping(map, mapping) {
return addMappingInternal(false, map, mapping);
}
var maybeAddSegment = (map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) => {
return addSegmentInternal(
true,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content
);
};
var maybeAddMapping = (map, mapping) => {
return addMappingInternal(true, map, mapping);
};
function setSourceContent(map, source, content) {
const {
_sources: sources,
_sourcesContent: sourcesContent
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
sourcesContent[index] = content;
}
function setIgnore(map, source, ignore = true) {
const {
_sources: sources,
_sourcesContent: sourcesContent,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
} = cast2(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (ignore) put(ignoreList, index);
else remove(ignoreList, index);
}
function toDecodedMap(map) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
_ignoreList: ignoreList
// _originalScopes: originalScopes,
// _generatedRanges: generatedRanges,
} = cast2(map);
removeEmptyFinalLines(mappings);
return {
version: 3,
file: map.file || void 0,
names: names.array,
sourceRoot: map.sourceRoot || void 0,
sources: sources.array,
sourcesContent,
mappings,
// originalScopes,
// generatedRanges,
ignoreList: ignoreList.array
};
}
function toEncodedMap(map) {
const decoded = toDecodedMap(map);
return Object.assign({}, decoded, {
// originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
// generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
mappings: (0, import_sourcemap_codec.encode)(decoded.mappings)
});
}
function fromMap(input) {
const map = new import_trace_mapping.TraceMap(input);
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
putAll(cast2(gen)._names, map.names);
putAll(cast2(gen)._sources, map.sources);
cast2(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
cast2(gen)._mappings = (0, import_trace_mapping.decodedMappings)(map);
if (map.ignoreList) putAll(cast2(gen)._ignoreList, map.ignoreList);
return gen;
}
function allMappings(map) {
const out = [];
const { _mappings: mappings, _sources: sources, _names: names } = cast2(map);
for (let i = 0; i < mappings.length; i++) {
const line = mappings[i];
for (let j = 0; j < line.length; j++) {
const seg = line[j];
const generated = { line: i + 1, column: seg[COLUMN] };
let source = void 0;
let original = void 0;
let name = void 0;
if (seg.length !== 1) {
source = sources.array[seg[SOURCES_INDEX]];
original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
if (seg.length === 5) name = names.array[seg[NAMES_INDEX]];
}
out.push({ generated, source, original, name });
}
}
return out;
}
function addSegmentInternal(skipable, map, genLine, genColumn, source, sourceLine, sourceColumn, name, content) {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names
// _originalScopes: originalScopes,
} = cast2(map);
const line = getIndex(mappings, genLine);
const index = getColumnIndex(line, genColumn);
if (!source) {
if (skipable && skipSourceless(line, index)) return;
return insert(line, index, [genColumn]);
}
assert(sourceLine);
assert(sourceColumn);
const sourcesIndex = put(sources, source);
const namesIndex = name ? put(names, name) : NO_NAME;
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content != null ? content : null;
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
return;
}
return insert(
line,
index,
name ? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex] : [genColumn, sourcesIndex, sourceLine, sourceColumn]
);
}
function assert(_val) {
}
function getIndex(arr, index) {
for (let i = arr.length; i <= index; i++) {
arr[i] = [];
}
return arr[index];
}
function getColumnIndex(line, genColumn) {
let index = line.length;
for (let i = index - 1; i >= 0; index = i--) {
const current = line[i];
if (genColumn >= current[COLUMN]) break;
}
return index;
}
function insert(array, index, value) {
for (let i = array.length; i > index; i--) {
array[i] = array[i - 1];
}
array[index] = value;
}
function removeEmptyFinalLines(mappings) {
const { length } = mappings;
let len = length;
for (let i = len - 1; i >= 0; len = i, i--) {
if (mappings[i].length > 0) break;
}
if (len < length) mappings.length = len;
}
function putAll(setarr, array) {
for (let i = 0; i < array.length; i++) put(setarr, array[i]);
}
function skipSourceless(line, index) {
if (index === 0) return true;
const prev = line[index - 1];
return prev.length === 1;
}
function skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex) {
if (index === 0) return false;
const prev = line[index - 1];
if (prev.length === 1) return false;
return sourcesIndex === prev[SOURCES_INDEX] && sourceLine === prev[SOURCE_LINE] && sourceColumn === prev[SOURCE_COLUMN] && namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME);
}
function addMappingInternal(skipable, map, mapping) {
const { generated, source, original, name, content } = mapping;
if (!source) {
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
null,
null,
null,
null,
null
);
}
assert(original);
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
source,
original.line - 1,
original.column,
name,
content
);
}
}));
//# sourceMappingURL=gen-mapping.umd.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,88 @@
import type { SourceMapInput } from '@jridgewell/trace-mapping';
import type { DecodedSourceMap, EncodedSourceMap, Pos, Mapping } from './types';
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
export type Options = {
file?: string | null;
sourceRoot?: string | null;
};
/**
* Provides the state to generate a sourcemap.
*/
export declare class GenMapping {
private _names;
private _sources;
private _sourcesContent;
private _mappings;
private _ignoreList;
file: string | null | undefined;
sourceRoot: string | null | undefined;
constructor({ file, sourceRoot }?: Options);
}
/**
* A low-level API to associate a generated position with an original source position. Line and
* column here are 0-based, unlike `addMapping`.
*/
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source?: null, sourceLine?: null, sourceColumn?: null, name?: null, content?: null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name?: null, content?: string | null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name: string, content?: string | null): void;
/**
* A high-level API to associate a generated position with an original source position. Line is
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
*/
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source?: null;
original?: null;
name?: null;
content?: null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name?: null;
content?: string | null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name: string;
content?: string | null;
}): void;
/**
* Same as `addSegment`, but will only add the segment if it generates useful information in the
* resulting map. This only works correctly if segments are added **in order**, meaning you should
* not add a segment with a lower generated line/column than one that came before.
*/
export declare const maybeAddSegment: typeof addSegment;
/**
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
* not add a mapping with a lower generated line/column than one that came before.
*/
export declare const maybeAddMapping: typeof addMapping;
/**
* Adds/removes the content of the source file to the source map.
*/
export declare function setSourceContent(map: GenMapping, source: string, content: string | null): void;
export declare function setIgnore(map: GenMapping, source: string, ignore?: boolean): void;
/**
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toDecodedMap(map: GenMapping): DecodedSourceMap;
/**
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toEncodedMap(map: GenMapping): EncodedSourceMap;
/**
* Constructs a new GenMapping, using the already present mappings of the input.
*/
export declare function fromMap(input: SourceMapInput): GenMapping;
/**
* Returns an array of high-level mapping objects for every recorded segment, which could then be
* passed to the `source-map` library.
*/
export declare function allMappings(map: GenMapping): Mapping[];

View File

@@ -0,0 +1,32 @@
type Key = string | number | symbol;
/**
* SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the
* index of the `key` in the backing array.
*
* This is designed to allow synchronizing a second array with the contents of the backing array,
* like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`,
* and there are never duplicates.
*/
export declare class SetArray<T extends Key = Key> {
private _indexes;
array: readonly T[];
constructor();
}
/**
* Gets the index associated with `key` in the backing array, if it is already present.
*/
export declare function get<T extends Key>(setarr: SetArray<T>, key: T): number | undefined;
/**
* Puts `key` into the backing array, if it is not already present. Returns
* the index of the `key` in the backing array.
*/
export declare function put<T extends Key>(setarr: SetArray<T>, key: T): number;
/**
* Pops the last added item out of the SetArray.
*/
export declare function pop<T extends Key>(setarr: SetArray<T>): void;
/**
* Removes the key, if it exists in the set.
*/
export declare function remove<T extends Key>(setarr: SetArray<T>, key: T): void;
export {};

View File

@@ -0,0 +1,12 @@
type GeneratedColumn = number;
type SourcesIndex = number;
type SourceLine = number;
type SourceColumn = number;
type NamesIndex = number;
export type SourceMapSegment = [GeneratedColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn] | [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex];
export declare const COLUMN = 0;
export declare const SOURCES_INDEX = 1;
export declare const SOURCE_LINE = 2;
export declare const SOURCE_COLUMN = 3;
export declare const NAMES_INDEX = 4;
export {};

View File

@@ -0,0 +1,43 @@
import type { SourceMapSegment } from './sourcemap-segment';
export interface SourceMapV3 {
file?: string | null;
names: readonly string[];
sourceRoot?: string;
sources: readonly (string | null)[];
sourcesContent?: readonly (string | null)[];
version: 3;
ignoreList?: readonly number[];
}
export interface EncodedSourceMap extends SourceMapV3 {
mappings: string;
}
export interface DecodedSourceMap extends SourceMapV3 {
mappings: readonly SourceMapSegment[][];
}
export interface Pos {
line: number;
column: number;
}
export interface OriginalPos extends Pos {
source: string;
}
export interface BindingExpressionRange {
start: Pos;
expression: string;
}
export type Mapping = {
generated: Pos;
source: undefined;
original: undefined;
name: undefined;
} | {
generated: Pos;
source: string;
original: Pos;
name: string;
} | {
generated: Pos;
source: string;
original: Pos;
name: undefined;
};

67
node_modules/@jridgewell/gen-mapping/package.json generated vendored Normal file
View File

@@ -0,0 +1,67 @@
{
"name": "@jridgewell/gen-mapping",
"version": "0.3.13",
"description": "Generate source maps",
"keywords": [
"source",
"map"
],
"main": "dist/gen-mapping.umd.js",
"module": "dist/gen-mapping.mjs",
"types": "types/gen-mapping.d.cts",
"files": [
"dist",
"src",
"types"
],
"exports": {
".": [
{
"import": {
"types": "./types/gen-mapping.d.mts",
"default": "./dist/gen-mapping.mjs"
},
"default": {
"types": "./types/gen-mapping.d.cts",
"default": "./dist/gen-mapping.umd.js"
}
},
"./dist/gen-mapping.umd.js"
],
"./package.json": "./package.json"
},
"scripts": {
"benchmark": "run-s build:code benchmark:*",
"benchmark:install": "cd benchmark && npm install",
"benchmark:only": "node --expose-gc benchmark/index.js",
"build": "run-s -n build:code build:types",
"build:code": "node ../../esbuild.mjs gen-mapping.ts",
"build:types": "run-s build:types:force build:types:emit build:types:mts",
"build:types:force": "rimraf tsconfig.build.tsbuildinfo",
"build:types:emit": "tsc --project tsconfig.build.json",
"build:types:mts": "node ../../mts-types.mjs",
"clean": "run-s -n clean:code clean:types",
"clean:code": "tsc --build --clean tsconfig.build.json",
"clean:types": "rimraf dist types",
"test": "run-s -n test:types test:only test:format",
"test:format": "prettier --check '{src,test}/**/*.ts'",
"test:only": "mocha",
"test:types": "eslint '{src,test}/**/*.ts'",
"lint": "run-s -n lint:types lint:format",
"lint:format": "npm run test:format -- --write",
"lint:types": "npm run test:types -- --fix",
"prepublishOnly": "npm run-s -n build test"
},
"homepage": "https://github.com/jridgewell/sourcemaps/tree/main/packages/gen-mapping",
"repository": {
"type": "git",
"url": "git+https://github.com/jridgewell/sourcemaps.git",
"directory": "packages/gen-mapping"
},
"author": "Justin Ridgewell <justin@ridgewell.name>",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
}

614
node_modules/@jridgewell/gen-mapping/src/gen-mapping.ts generated vendored Normal file
View File

@@ -0,0 +1,614 @@
import { SetArray, put, remove } from './set-array';
import {
encode,
// encodeGeneratedRanges,
// encodeOriginalScopes
} from '@jridgewell/sourcemap-codec';
import { TraceMap, decodedMappings } from '@jridgewell/trace-mapping';
import {
COLUMN,
SOURCES_INDEX,
SOURCE_LINE,
SOURCE_COLUMN,
NAMES_INDEX,
} from './sourcemap-segment';
import type { SourceMapInput } from '@jridgewell/trace-mapping';
// import type { OriginalScope, GeneratedRange } from '@jridgewell/sourcemap-codec';
import type { SourceMapSegment } from './sourcemap-segment';
import type {
DecodedSourceMap,
EncodedSourceMap,
Pos,
Mapping,
// BindingExpressionRange,
// OriginalPos,
// OriginalScopeInfo,
// GeneratedRangeInfo,
} from './types';
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
export type Options = {
file?: string | null;
sourceRoot?: string | null;
};
const NO_NAME = -1;
/**
* Provides the state to generate a sourcemap.
*/
export class GenMapping {
declare private _names: SetArray<string>;
declare private _sources: SetArray<string>;
declare private _sourcesContent: (string | null)[];
declare private _mappings: SourceMapSegment[][];
// private declare _originalScopes: OriginalScope[][];
// private declare _generatedRanges: GeneratedRange[];
declare private _ignoreList: SetArray<number>;
declare file: string | null | undefined;
declare sourceRoot: string | null | undefined;
constructor({ file, sourceRoot }: Options = {}) {
this._names = new SetArray();
this._sources = new SetArray();
this._sourcesContent = [];
this._mappings = [];
// this._originalScopes = [];
// this._generatedRanges = [];
this.file = file;
this.sourceRoot = sourceRoot;
this._ignoreList = new SetArray();
}
}
interface PublicMap {
_names: GenMapping['_names'];
_sources: GenMapping['_sources'];
_sourcesContent: GenMapping['_sourcesContent'];
_mappings: GenMapping['_mappings'];
// _originalScopes: GenMapping['_originalScopes'];
// _generatedRanges: GenMapping['_generatedRanges'];
_ignoreList: GenMapping['_ignoreList'];
}
/**
* Typescript doesn't allow friend access to private fields, so this just casts the map into a type
* with public access modifiers.
*/
function cast(map: unknown): PublicMap {
return map as any;
}
/**
* A low-level API to associate a generated position with an original source position. Line and
* column here are 0-based, unlike `addMapping`.
*/
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source?: null,
sourceLine?: null,
sourceColumn?: null,
name?: null,
content?: null,
): void;
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source: string,
sourceLine: number,
sourceColumn: number,
name?: null,
content?: string | null,
): void;
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source: string,
sourceLine: number,
sourceColumn: number,
name: string,
content?: string | null,
): void;
export function addSegment(
map: GenMapping,
genLine: number,
genColumn: number,
source?: string | null,
sourceLine?: number | null,
sourceColumn?: number | null,
name?: string | null,
content?: string | null,
): void {
return addSegmentInternal(
false,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content,
);
}
/**
* A high-level API to associate a generated position with an original source position. Line is
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
*/
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source?: null;
original?: null;
name?: null;
content?: null;
},
): void;
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source: string;
original: Pos;
name?: null;
content?: string | null;
},
): void;
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source: string;
original: Pos;
name: string;
content?: string | null;
},
): void;
export function addMapping(
map: GenMapping,
mapping: {
generated: Pos;
source?: string | null;
original?: Pos | null;
name?: string | null;
content?: string | null;
},
): void {
return addMappingInternal(false, map, mapping as Parameters<typeof addMappingInternal>[2]);
}
/**
* Same as `addSegment`, but will only add the segment if it generates useful information in the
* resulting map. This only works correctly if segments are added **in order**, meaning you should
* not add a segment with a lower generated line/column than one that came before.
*/
export const maybeAddSegment: typeof addSegment = (
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content,
) => {
return addSegmentInternal(
true,
map,
genLine,
genColumn,
source,
sourceLine,
sourceColumn,
name,
content,
);
};
/**
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
* not add a mapping with a lower generated line/column than one that came before.
*/
export const maybeAddMapping: typeof addMapping = (map, mapping) => {
return addMappingInternal(true, map, mapping as Parameters<typeof addMappingInternal>[2]);
};
/**
* Adds/removes the content of the source file to the source map.
*/
export function setSourceContent(map: GenMapping, source: string, content: string | null): void {
const {
_sources: sources,
_sourcesContent: sourcesContent,
// _originalScopes: originalScopes,
} = cast(map);
const index = put(sources, source);
sourcesContent[index] = content;
// if (index === originalScopes.length) originalScopes[index] = [];
}
export function setIgnore(map: GenMapping, source: string, ignore = true) {
const {
_sources: sources,
_sourcesContent: sourcesContent,
_ignoreList: ignoreList,
// _originalScopes: originalScopes,
} = cast(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
// if (index === originalScopes.length) originalScopes[index] = [];
if (ignore) put(ignoreList, index);
else remove(ignoreList, index);
}
/**
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export function toDecodedMap(map: GenMapping): DecodedSourceMap {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
_ignoreList: ignoreList,
// _originalScopes: originalScopes,
// _generatedRanges: generatedRanges,
} = cast(map);
removeEmptyFinalLines(mappings);
return {
version: 3,
file: map.file || undefined,
names: names.array,
sourceRoot: map.sourceRoot || undefined,
sources: sources.array,
sourcesContent,
mappings,
// originalScopes,
// generatedRanges,
ignoreList: ignoreList.array,
};
}
/**
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export function toEncodedMap(map: GenMapping): EncodedSourceMap {
const decoded = toDecodedMap(map);
return Object.assign({}, decoded, {
// originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
// generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
mappings: encode(decoded.mappings as SourceMapSegment[][]),
});
}
/**
* Constructs a new GenMapping, using the already present mappings of the input.
*/
export function fromMap(input: SourceMapInput): GenMapping {
const map = new TraceMap(input);
const gen = new GenMapping({ file: map.file, sourceRoot: map.sourceRoot });
putAll(cast(gen)._names, map.names);
putAll(cast(gen)._sources, map.sources as string[]);
cast(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
cast(gen)._mappings = decodedMappings(map) as GenMapping['_mappings'];
// TODO: implement originalScopes/generatedRanges
if (map.ignoreList) putAll(cast(gen)._ignoreList, map.ignoreList);
return gen;
}
/**
* Returns an array of high-level mapping objects for every recorded segment, which could then be
* passed to the `source-map` library.
*/
export function allMappings(map: GenMapping): Mapping[] {
const out: Mapping[] = [];
const { _mappings: mappings, _sources: sources, _names: names } = cast(map);
for (let i = 0; i < mappings.length; i++) {
const line = mappings[i];
for (let j = 0; j < line.length; j++) {
const seg = line[j];
const generated = { line: i + 1, column: seg[COLUMN] };
let source: string | undefined = undefined;
let original: Pos | undefined = undefined;
let name: string | undefined = undefined;
if (seg.length !== 1) {
source = sources.array[seg[SOURCES_INDEX]];
original = { line: seg[SOURCE_LINE] + 1, column: seg[SOURCE_COLUMN] };
if (seg.length === 5) name = names.array[seg[NAMES_INDEX]];
}
out.push({ generated, source, original, name } as Mapping);
}
}
return out;
}
// This split declaration is only so that terser can elminiate the static initialization block.
function addSegmentInternal<S extends string | null | undefined>(
skipable: boolean,
map: GenMapping,
genLine: number,
genColumn: number,
source: S,
sourceLine: S extends string ? number : null | undefined,
sourceColumn: S extends string ? number : null | undefined,
name: S extends string ? string | null | undefined : null | undefined,
content: S extends string ? string | null | undefined : null | undefined,
): void {
const {
_mappings: mappings,
_sources: sources,
_sourcesContent: sourcesContent,
_names: names,
// _originalScopes: originalScopes,
} = cast(map);
const line = getIndex(mappings, genLine);
const index = getColumnIndex(line, genColumn);
if (!source) {
if (skipable && skipSourceless(line, index)) return;
return insert(line, index, [genColumn]);
}
// Sigh, TypeScript can't figure out sourceLine and sourceColumn aren't nullish if source
// isn't nullish.
assert<number>(sourceLine);
assert<number>(sourceColumn);
const sourcesIndex = put(sources, source);
const namesIndex = name ? put(names, name) : NO_NAME;
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content ?? null;
// if (sourcesIndex === originalScopes.length) originalScopes[sourcesIndex] = [];
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
return;
}
return insert(
line,
index,
name
? [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex]
: [genColumn, sourcesIndex, sourceLine, sourceColumn],
);
}
function assert<T>(_val: unknown): asserts _val is T {
// noop.
}
function getIndex<T>(arr: T[][], index: number): T[] {
for (let i = arr.length; i <= index; i++) {
arr[i] = [];
}
return arr[index];
}
function getColumnIndex(line: SourceMapSegment[], genColumn: number): number {
let index = line.length;
for (let i = index - 1; i >= 0; index = i--) {
const current = line[i];
if (genColumn >= current[COLUMN]) break;
}
return index;
}
function insert<T>(array: T[], index: number, value: T) {
for (let i = array.length; i > index; i--) {
array[i] = array[i - 1];
}
array[index] = value;
}
function removeEmptyFinalLines(mappings: SourceMapSegment[][]) {
const { length } = mappings;
let len = length;
for (let i = len - 1; i >= 0; len = i, i--) {
if (mappings[i].length > 0) break;
}
if (len < length) mappings.length = len;
}
function putAll<T extends string | number>(setarr: SetArray<T>, array: T[]) {
for (let i = 0; i < array.length; i++) put(setarr, array[i]);
}
function skipSourceless(line: SourceMapSegment[], index: number): boolean {
// The start of a line is already sourceless, so adding a sourceless segment to the beginning
// doesn't generate any useful information.
if (index === 0) return true;
const prev = line[index - 1];
// If the previous segment is also sourceless, then adding another sourceless segment doesn't
// genrate any new information. Else, this segment will end the source/named segment and point to
// a sourceless position, which is useful.
return prev.length === 1;
}
function skipSource(
line: SourceMapSegment[],
index: number,
sourcesIndex: number,
sourceLine: number,
sourceColumn: number,
namesIndex: number,
): boolean {
// A source/named segment at the start of a line gives position at that genColumn
if (index === 0) return false;
const prev = line[index - 1];
// If the previous segment is sourceless, then we're transitioning to a source.
if (prev.length === 1) return false;
// If the previous segment maps to the exact same source position, then this segment doesn't
// provide any new position information.
return (
sourcesIndex === prev[SOURCES_INDEX] &&
sourceLine === prev[SOURCE_LINE] &&
sourceColumn === prev[SOURCE_COLUMN] &&
namesIndex === (prev.length === 5 ? prev[NAMES_INDEX] : NO_NAME)
);
}
function addMappingInternal<S extends string | null | undefined>(
skipable: boolean,
map: GenMapping,
mapping: {
generated: Pos;
source: S;
original: S extends string ? Pos : null | undefined;
name: S extends string ? string | null | undefined : null | undefined;
content: S extends string ? string | null | undefined : null | undefined;
},
) {
const { generated, source, original, name, content } = mapping;
if (!source) {
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
null,
null,
null,
null,
null,
);
}
assert<Pos>(original);
return addSegmentInternal(
skipable,
map,
generated.line - 1,
generated.column,
source as string,
original.line - 1,
original.column,
name,
content,
);
}
/*
export function addOriginalScope(
map: GenMapping,
data: {
start: Pos;
end: Pos;
source: string;
kind: string;
name?: string;
variables?: string[];
},
): OriginalScopeInfo {
const { start, end, source, kind, name, variables } = data;
const {
_sources: sources,
_sourcesContent: sourcesContent,
_originalScopes: originalScopes,
_names: names,
} = cast(map);
const index = put(sources, source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (index === originalScopes.length) originalScopes[index] = [];
const kindIndex = put(names, kind);
const scope: OriginalScope = name
? [start.line - 1, start.column, end.line - 1, end.column, kindIndex, put(names, name)]
: [start.line - 1, start.column, end.line - 1, end.column, kindIndex];
if (variables) {
scope.vars = variables.map((v) => put(names, v));
}
const len = originalScopes[index].push(scope);
return [index, len - 1, variables];
}
*/
// Generated Ranges
/*
export function addGeneratedRange(
map: GenMapping,
data: {
start: Pos;
isScope: boolean;
originalScope?: OriginalScopeInfo;
callsite?: OriginalPos;
},
): GeneratedRangeInfo {
const { start, isScope, originalScope, callsite } = data;
const {
_originalScopes: originalScopes,
_sources: sources,
_sourcesContent: sourcesContent,
_generatedRanges: generatedRanges,
} = cast(map);
const range: GeneratedRange = [
start.line - 1,
start.column,
0,
0,
originalScope ? originalScope[0] : -1,
originalScope ? originalScope[1] : -1,
];
if (originalScope?.[2]) {
range.bindings = originalScope[2].map(() => [[-1]]);
}
if (callsite) {
const index = put(sources, callsite.source);
if (index === sourcesContent.length) sourcesContent[index] = null;
if (index === originalScopes.length) originalScopes[index] = [];
range.callsite = [index, callsite.line - 1, callsite.column];
}
if (isScope) range.isScope = true;
generatedRanges.push(range);
return [range, originalScope?.[2]];
}
export function setEndPosition(range: GeneratedRangeInfo, pos: Pos) {
range[0][2] = pos.line - 1;
range[0][3] = pos.column;
}
export function addBinding(
map: GenMapping,
range: GeneratedRangeInfo,
variable: string,
expression: string | BindingExpressionRange,
) {
const { _names: names } = cast(map);
const bindings = (range[0].bindings ||= []);
const vars = range[1];
const index = vars!.indexOf(variable);
const binding = getIndex(bindings, index);
if (typeof expression === 'string') binding[0] = [put(names, expression)];
else {
const { start } = expression;
binding.push([put(names, expression.expression), start.line - 1, start.column]);
}
}
*/

82
node_modules/@jridgewell/gen-mapping/src/set-array.ts generated vendored Normal file
View File

@@ -0,0 +1,82 @@
type Key = string | number | symbol;
/**
* SetArray acts like a `Set` (allowing only one occurrence of a string `key`), but provides the
* index of the `key` in the backing array.
*
* This is designed to allow synchronizing a second array with the contents of the backing array,
* like how in a sourcemap `sourcesContent[i]` is the source content associated with `source[i]`,
* and there are never duplicates.
*/
export class SetArray<T extends Key = Key> {
declare private _indexes: Record<T, number | undefined>;
declare array: readonly T[];
constructor() {
this._indexes = { __proto__: null } as any;
this.array = [];
}
}
interface PublicSet<T extends Key> {
array: T[];
_indexes: SetArray<T>['_indexes'];
}
/**
* Typescript doesn't allow friend access to private fields, so this just casts the set into a type
* with public access modifiers.
*/
function cast<T extends Key>(set: SetArray<T>): PublicSet<T> {
return set as any;
}
/**
* Gets the index associated with `key` in the backing array, if it is already present.
*/
export function get<T extends Key>(setarr: SetArray<T>, key: T): number | undefined {
return cast(setarr)._indexes[key];
}
/**
* Puts `key` into the backing array, if it is not already present. Returns
* the index of the `key` in the backing array.
*/
export function put<T extends Key>(setarr: SetArray<T>, key: T): number {
// The key may or may not be present. If it is present, it's a number.
const index = get(setarr, key);
if (index !== undefined) return index;
const { array, _indexes: indexes } = cast(setarr);
const length = array.push(key);
return (indexes[key] = length - 1);
}
/**
* Pops the last added item out of the SetArray.
*/
export function pop<T extends Key>(setarr: SetArray<T>): void {
const { array, _indexes: indexes } = cast(setarr);
if (array.length === 0) return;
const last = array.pop()!;
indexes[last] = undefined;
}
/**
* Removes the key, if it exists in the set.
*/
export function remove<T extends Key>(setarr: SetArray<T>, key: T): void {
const index = get(setarr, key);
if (index === undefined) return;
const { array, _indexes: indexes } = cast(setarr);
for (let i = index + 1; i < array.length; i++) {
const k = array[i];
array[i - 1] = k;
indexes[k]!--;
}
indexes[key] = undefined;
array.pop();
}

View File

@@ -0,0 +1,16 @@
type GeneratedColumn = number;
type SourcesIndex = number;
type SourceLine = number;
type SourceColumn = number;
type NamesIndex = number;
export type SourceMapSegment =
| [GeneratedColumn]
| [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn]
| [GeneratedColumn, SourcesIndex, SourceLine, SourceColumn, NamesIndex];
export const COLUMN = 0;
export const SOURCES_INDEX = 1;
export const SOURCE_LINE = 2;
export const SOURCE_COLUMN = 3;
export const NAMES_INDEX = 4;

61
node_modules/@jridgewell/gen-mapping/src/types.ts generated vendored Normal file
View File

@@ -0,0 +1,61 @@
// import type { GeneratedRange, OriginalScope } from '@jridgewell/sourcemap-codec';
import type { SourceMapSegment } from './sourcemap-segment';
export interface SourceMapV3 {
file?: string | null;
names: readonly string[];
sourceRoot?: string;
sources: readonly (string | null)[];
sourcesContent?: readonly (string | null)[];
version: 3;
ignoreList?: readonly number[];
}
export interface EncodedSourceMap extends SourceMapV3 {
mappings: string;
// originalScopes: string[];
// generatedRanges: string;
}
export interface DecodedSourceMap extends SourceMapV3 {
mappings: readonly SourceMapSegment[][];
// originalScopes: readonly OriginalScope[][];
// generatedRanges: readonly GeneratedRange[];
}
export interface Pos {
line: number; // 1-based
column: number; // 0-based
}
export interface OriginalPos extends Pos {
source: string;
}
export interface BindingExpressionRange {
start: Pos;
expression: string;
}
// export type OriginalScopeInfo = [number, number, string[] | undefined];
// export type GeneratedRangeInfo = [GeneratedRange, string[] | undefined];
export type Mapping =
| {
generated: Pos;
source: undefined;
original: undefined;
name: undefined;
}
| {
generated: Pos;
source: string;
original: Pos;
name: string;
}
| {
generated: Pos;
source: string;
original: Pos;
name: undefined;
};

View File

@@ -0,0 +1,89 @@
import type { SourceMapInput } from '@jridgewell/trace-mapping';
import type { DecodedSourceMap, EncodedSourceMap, Pos, Mapping } from './types.cts';
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
export type Options = {
file?: string | null;
sourceRoot?: string | null;
};
/**
* Provides the state to generate a sourcemap.
*/
export declare class GenMapping {
private _names;
private _sources;
private _sourcesContent;
private _mappings;
private _ignoreList;
file: string | null | undefined;
sourceRoot: string | null | undefined;
constructor({ file, sourceRoot }?: Options);
}
/**
* A low-level API to associate a generated position with an original source position. Line and
* column here are 0-based, unlike `addMapping`.
*/
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source?: null, sourceLine?: null, sourceColumn?: null, name?: null, content?: null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name?: null, content?: string | null): void;
export declare function addSegment(map: GenMapping, genLine: number, genColumn: number, source: string, sourceLine: number, sourceColumn: number, name: string, content?: string | null): void;
/**
* A high-level API to associate a generated position with an original source position. Line is
* 1-based, but column is 0-based, due to legacy behavior in `source-map` library.
*/
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source?: null;
original?: null;
name?: null;
content?: null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name?: null;
content?: string | null;
}): void;
export declare function addMapping(map: GenMapping, mapping: {
generated: Pos;
source: string;
original: Pos;
name: string;
content?: string | null;
}): void;
/**
* Same as `addSegment`, but will only add the segment if it generates useful information in the
* resulting map. This only works correctly if segments are added **in order**, meaning you should
* not add a segment with a lower generated line/column than one that came before.
*/
export declare const maybeAddSegment: typeof addSegment;
/**
* Same as `addMapping`, but will only add the mapping if it generates useful information in the
* resulting map. This only works correctly if mappings are added **in order**, meaning you should
* not add a mapping with a lower generated line/column than one that came before.
*/
export declare const maybeAddMapping: typeof addMapping;
/**
* Adds/removes the content of the source file to the source map.
*/
export declare function setSourceContent(map: GenMapping, source: string, content: string | null): void;
export declare function setIgnore(map: GenMapping, source: string, ignore?: boolean): void;
/**
* Returns a sourcemap object (with decoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toDecodedMap(map: GenMapping): DecodedSourceMap;
/**
* Returns a sourcemap object (with encoded mappings) suitable for passing to a library that expects
* a sourcemap, or to JSON.stringify.
*/
export declare function toEncodedMap(map: GenMapping): EncodedSourceMap;
/**
* Constructs a new GenMapping, using the already present mappings of the input.
*/
export declare function fromMap(input: SourceMapInput): GenMapping;
/**
* Returns an array of high-level mapping objects for every recorded segment, which could then be
* passed to the `source-map` library.
*/
export declare function allMappings(map: GenMapping): Mapping[];
//# sourceMappingURL=gen-mapping.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"gen-mapping.d.ts","sourceRoot":"","sources":["../src/gen-mapping.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,GAAG,EACH,OAAO,EAKR,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;AAE5D,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAIF;;GAEG;AACH,qBAAa,UAAU;IACrB,QAAgB,MAAM,CAAmB;IACzC,QAAgB,QAAQ,CAAmB;IAC3C,QAAgB,eAAe,CAAoB;IACnD,QAAgB,SAAS,CAAuB;IAGhD,QAAgB,WAAW,CAAmB;IACtC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;gBAElC,EAAE,IAAI,EAAE,UAAU,EAAE,GAAE,OAAY;CAW/C;AAoBD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,IAAI,EACb,UAAU,CAAC,EAAE,IAAI,EACjB,YAAY,CAAC,EAAE,IAAI,EACnB,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,CAAC,EAAE,IAAI,GACb,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI,CAAC;AAwBR;;;GAGG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,IAAI,CAAC;CAChB,GACA,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,IAAI,CAAC;AACR,wBAAgB,UAAU,CACxB,GAAG,EAAE,UAAU,EACf,OAAO,EAAE;IACP,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,GACA,IAAI,CAAC;AAcR;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,UAqBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,UAEpC,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAS9F;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAO,QAYvE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,gBAAgB,CAwB9D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,gBAAgB,CAO9D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,CAYzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,EAAE,CA0BtD"}

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