@easytwin/devkit 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,14 +13,24 @@ description: 当用户要开始使用 EasyTwin DevKit、配置应用级凭证(Ap
13
13
  {
14
14
  "appId": "必填",
15
15
  "appSecret": "必填",
16
- "baseUrl": "可选,缺省官方固定域名"
16
+ "env": "可选,prod=正式 / test=测试,缺省 prod",
17
+ "baseUrl": "可选,缺省按 env 选官方域名",
18
+ "ossUrl": "可选,缺省官方 OSS",
19
+ "opAccountId": "可选,过渡期网关头 op-account-id;env=test 缺省 25",
20
+ "opUserId": "可选,过渡期网关头 op-user-id;env=test 缺省 25",
21
+ "spaceId": "可选,过渡期网关头 space-id;env=test 缺省 54",
22
+ "scenes": "可选,由 easytwin scene list 写入的场景摘要,不含 JSON 本体"
17
23
  }
18
24
  ```
19
25
 
20
- - 环境变量 `EASYTWIN_APP_ID` / `EASYTWIN_APP_SECRET` / `EASYTWIN_BASE_URL` 优先于文件(CI 与不落盘场景)。
26
+ - 环境变量 `EASYTWIN_APP_ID` / `EASYTWIN_APP_SECRET` / `EASYTWIN_BASE_URL` / `EASYTWIN_ENV` / `EASYTWIN_OSS_URL` 优先于文件(CI 与不落盘场景)。过渡期另有 `EASYTWIN_OP_ACCOUNT_ID` / `EASYTWIN_OP_USER_ID` / `EASYTWIN_SPACE_ID`(当前测试后端走 OP 网关,免网关接口到位后删除)
27
+ - 官方 API:正式 `http://saas-twin.k8s.dtstack.cn/`、测试 `http://172.16.125.3:10100/`;缺省按 env 选择,`EASYTWIN_BASE_URL` 始终优先。
28
+ - 官方 OSS:`https://dt-easyv-test.oss-cn-hangzhou.aliyuncs.com/`,供 twin runtime 加载 webp wasm / draco / 组件脚本;`EASYTWIN_OSS_URL` 始终优先。
21
29
  - 该文件含密钥,**永不入库**:`init` 已负责 gitignore,不要把它提交进版本库。
30
+ - `scenes` 由 `easytwin scene list`(或插件刷新场景树)写入,下次 list 会全量覆盖;`init` 重写配置时不会保留旧列表。
31
+ - **本地测试**:`appId` 与 `appSecret` 都填 `test` 时进入本地测试模式,scene/upload/pull 走本地 mock、不发网络请求(见 easytwin-scene / easytwin-upload)。仅限本地联调,严禁用假凭据碰真实服务端。
22
32
 
23
33
  ## 步骤
24
34
 
25
35
  1. 若项目还没有 `easytwin.config.json`,运行 `easytwin init` 并按提示输入。
26
- 2. 校验:`easytwin scene list` 能列出场景即凭据有效;报错则检查 appId / appSecret 或环境变量覆盖。
36
+ 2. 校验:`easytwin scene list` 能列出场景即凭据有效,并会把场景 id/名称写入 `easytwin.config.json.scenes`;报错则检查 appId / appSecret 或环境变量覆盖。
@@ -5,7 +5,7 @@ description: 当用户要理解 EasyTwin 引擎内核(类层级、RuntimeEngine
5
5
 
6
6
  # 引擎内核(easytwin-runtime core)
7
7
 
8
- 对应 npm 包 `@easytwin/runtime` 的 `src/core/` 目录:引擎本体、场景、组件基类与基础设施。写渲染功能的主流程见 `easytwin-render`;本技能是引擎核心的完整 API 参考。
8
+ 对应 `@easytwin/runtime` 的 `src/core/` 目录:引擎本体、场景、组件基类与基础设施。第三方应用入口是 TwinApp 子类,主流程见 `easytwin-render`;本技能是引擎内部 API 参考。
9
9
 
10
10
  ## 核心关系速览
11
11
 
@@ -22,4 +22,4 @@ description: 当用户要理解 EasyTwin 引擎内核(类层级、RuntimeEngine
22
22
  - 需要"相机体系(Camera/SceneCamera/CameraControls/focus)"时读 `references/camera.md`
23
23
  - 需要"物理体系(PhysicsManager/PhysicsScene/物理开关)"时读 `references/physics.md`
24
24
 
25
- 组件生命周期钩子、状态/数据 API 与序列化结构分别在 `easytwin-render` 的 `references/lifecycle-events.md` 与 `references/scene-and-assets.md`,本技能不重复(虚拟子要素的序列化结构见本技能 `references/virtual-components.md`)。
25
+ 组件生命周期钩子、状态/数据 API 与序列化结构分别在 `easytwin-render` 的 `references/lifecycle-events.md` 与 `references/scene-and-assets.md`,本技能不重复(虚拟子要素的序列化结构见本技能 `references/virtual-components.md`)。
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 概览
4
4
 
5
- 一个 `RuntimeEngine` 对应一个 DOM 容器(`engine.container`):
5
+ TwinApp `ctx.engine` 取用本对象。一个 `RuntimeEngine` 对应一个 DOM 容器(`engine.container`):
6
6
 
7
7
  - 注册并初始化五个 manager:`ResourceManager` / `SceneManager` / `CustomComponentManager` / `EditorActionsManager` / `PhysicsManager`;
8
8
  - 启动 `requestAnimationFrame` 渲染循环,每帧 `Time.update()` → `SceneManager.updateScene(deltaTime)`;
@@ -10,12 +10,12 @@
10
10
 
11
11
  ## create 与 EngineConfiguration
12
12
 
13
- 唯一入口 `RuntimeEngine.create(config)`(静态、异步):
13
+ 宿主创建入口 `RuntimeEngine.create(config)`(静态、异步)。TwinApp 用 `ctx.engine`,不必再调:
14
14
 
15
15
  ```ts
16
16
  const engine = await RuntimeEngine.create({
17
17
  containerId: "twin-root",
18
- baseOSSUrl: "https://assets.easyv.cloud",
18
+ baseOSSUrl: "https://dt-easyv-test.oss-cn-hangzhou.aliyuncs.com/",
19
19
  customComponentDeps: { "@easytwin/runtime": Runtime, react: FakeReact },
20
20
  sceneResources: [],
21
21
  componentRels: [],
@@ -25,7 +25,7 @@ const engine = await RuntimeEngine.create({
25
25
  | 字段 | 必填 | 说明 |
26
26
  | --- | --- | --- |
27
27
  | `containerId` | 是 | 容器元素 id;找不到即抛 `cannot find dom with id '<id>'` |
28
- | `baseOSSUrl` | 是 | 服务端资产根地址;draco/ktx2/webp 等系统库 URL 由此拼出 |
28
+ | `baseOSSUrl` | 是 | 官方 OSS 资产根;draco/ktx2/webp custom component script URL 由此拼出 |
29
29
  | `customComponentDeps` | 是 | 自定义组件脚本 import 的依赖表(键=模块名,值=实现) |
30
30
  | `localOSSUrl` | 否 | 本地资产根地址;非空时自定义组件脚本优先读本地 |
31
31
  | `sceneResources` | 否 | 场景资源清单 `ListSceneResourceVo[]`,默认 [] |
@@ -85,4 +85,4 @@ const engine = await RuntimeEngine.create({
85
85
  ## SystemInfo / StatsTool
86
86
 
87
87
  - `SystemInfo`(静态):`platform`(`Platform` 枚举:Android / IPhone / IPad / Mac / Unknown)、`operatingSystem`、`devicePixelRatio`。
88
- - `StatsTool`(场景性能统计):`triangleCount` / `textureRAM` / `textureVRAM` / `lightCount` / `componentCount` / `modelCount` / `materialCount` / `textureMap` / `geometryMap` / `materialMap`,`update()` 重算。
88
+ - `StatsTool`(场景性能统计):`triangleCount` / `textureRAM` / `textureVRAM` / `lightCount` / `componentCount` / `modelCount` / `materialCount` / `textureMap` / `geometryMap` / `materialMap`,`update()` 重算。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: easytwin-develop
3
- description: 当用户开始 EasyTwin 第三方应用开发、需要了解整体开发工作流(配置凭证→拉场景→写渲染→上传)、或询问"接下来该做什么/从哪开始"时使用。其余 EasyTwin 技能的总纲。
3
+ description: 当用户开始 EasyTwin 第三方应用开发、需要了解整体开发工作流(配置凭证→拉工作区代码→拉场景→写 TwinApp 子类→上传)、或询问"接下来该做什么/从哪开始"时使用。其余 EasyTwin 技能的总纲。
4
4
  ---
5
5
 
6
6
  # EasyTwin 开发工作流
@@ -8,9 +8,10 @@ description: 当用户开始 EasyTwin 第三方应用开发、需要了解整体
8
8
  EasyTwin DevKit 是 EasyTwin 第三方应用开发工具集。典型工作流:
9
9
 
10
10
  1. **配置凭证**:运行 `easytwin init` 生成项目级 `easytwin.config.json`(App ID / App Secret)。详见 `easytwin-bootstrap`。
11
- 2. **拉取场景**:`easytwin scene list` 列出场景,`easytwin scene pull <id>` 拉取场景 JSON 到本地,作为开发参考与预览输入。详见 `easytwin-scene`。
12
- 3. **编写渲染功能**:用 twin runtime(npm 包 `@easytwin/runtime`)开发渲染代码;引擎内核(基类/Time/相机/物理)见 `easytwin-core`。详见 `easytwin-render`(主力技能)。
13
- 4. **上传产物**:`easytwin upload <dir>` 全量覆盖上传到服务端,不可逆。详见 `easytwin-upload`。
11
+ 2. **拉取工作区代码**:`easytwin pull` 从服务端下载代码;远端为空则写入默认 TwinApp `src/main.ts`;本地已有文件时先 diff,默认只补缺失。详见 `easytwin-upload`。
12
+ 3. **拉取场景**:`easytwin scene list` 列出场景,`easytwin scene pull <id>` 拉取场景 JSON 到本地,作为开发参考与预览输入。详见 `easytwin-scene`。
13
+ 4. **编写渲染功能**:在工作区 `src/main.ts` 写 TwinApp 子类(`import { TwinApp } from "@easytwin/apps"`,`export default class App extends TwinApp { init; onSceneLoaded; onUpdate; ... }`)。类型由 `easytwin skills sync` 分发。编译用 `easytwin bundle`,运行用插件预览页底部 Run。可上传子集只用 runtime 五件套导出;整包 API 仅本地预览。引擎内核见 `easytwin-core`。详见 `easytwin-render`(主力技能)。
14
+ 5. **上传产物**:`easytwin upload <dir>` 全量覆盖上传到服务端,不可逆。详见 `easytwin-upload`。
14
15
 
15
16
  ## 何时读哪个技能
16
17
 
@@ -20,6 +21,10 @@ EasyTwin DevKit 是 EasyTwin 第三方应用开发工具集。典型工作流:
20
21
  | 列场景、拉场景 JSON | easytwin-scene |
21
22
  | 用 twin runtime 写渲染 | easytwin-render |
22
23
  | 理解引擎内核 / 基类 / 相机 / 物理 | easytwin-core |
23
- | 上传开发产物 | easytwin-upload |
24
+ | 上传 / 拉取工作区代码 | easytwin-upload |
24
25
 
25
- > 场景 JSON 具体格式尚未定稿,相关说明在 easytwin-scene 中为占位。
26
+ > 场景 JSON 格式以 easytwin-runtime 的 `SceneJson` 为准,详见 easytwin-scene
27
+
28
+ ## 本地测试
29
+
30
+ 没有凭据或不想动服务端时,把 `appId` / `appSecret` 都设为 `test` 即进入本地测试模式:`scene list` / `scene pull` 读 devkit 包内的 `scene.example.json`,`upload` 只做本地空跑,`pull` 按远端为空写入默认 `src/main.ts`(已有则不覆盖),这三条不发服务端请求。插件里的场景预览仍是三维场景页(打包的 twin runtime + 本地示例 JSON),系统库(draco/basis/webp)与组件脚本按官方 OSS(=ossUrl)在线加载。预览页 Run 在 mock 下同样可用。
@@ -1,40 +1,80 @@
1
1
  ---
2
2
  name: easytwin-render
3
- description: 当用户要用 EasyTwin twin runtime 编写渲染功能(安装/引入 @easytwin/runtime、初始化引擎、加载场景、开发自定义组件、调用渲染 API)时使用。这是 EasyTwin 渲染开发的唯一入口技能。
3
+ description: 当用户要用 EasyTwin twin runtime 编写渲染功能( src/main.ts TwinApp 子类、class extends TwinApp、预览页 Run、easytwin bundle、加载场景、开发自定义组件、调用渲染 API)时使用。这是 EasyTwin 渲染开发的唯一入口技能。
4
4
  ---
5
5
 
6
6
  # 用 twin runtime 开发渲染功能
7
7
 
8
- twin runtime npm `@easytwin/runtime`(ESM,peer 依赖 `three@0.183.0`):在已存在的 DOM 容器上创建 `RuntimeEngine`,加载场景 JSON,通过组件生命周期与渲染 API 开发功能。
8
+ `@easytwin/runtime` 是商业包,公网 npm 不存在,类型由 `easytwin skills sync` 写入 `.easytwin/types/`。`TwinApp` 从 `@easytwin/apps` 引入(同样由 sync 分发)。运行走预览页底部 **Run**,或 `easytwin bundle` 做编译校验。
9
9
 
10
- ## 快速上手
10
+ ## 编写 TwinApp 子类(可上传子集)
11
11
 
12
- 1. 安装:`npm i @easytwin/runtime`(项目需自装 peer 依赖 `three@0.183.0`)。
13
- 2. 页面里先放好容器 `<div id="twin-root"></div>` ——容器必须已存在于 DOM,`RuntimeEngine.create` 找不到会抛错。
14
- 3. 初始化并加载场景:
12
+ 工作区固定入口 `src/main.ts`。依赖只允许 `@easytwin/runtime` `@easytwin/apps`(相对路径本地 TS 可以)。`export default` 必须是 `TwinApp` 子类:
15
13
 
16
14
  ```ts
17
- import { RuntimeEngine, SceneManager, LoadSceneMode } from "@easytwin/runtime";
18
-
19
- const engine = await RuntimeEngine.create({
20
- containerId: "twin-root",
21
- baseOSSUrl: "https://assets.easyv.cloud",
22
- customComponentDeps: { "@easytwin/runtime": Runtime, react: FakeReact },
23
- sceneResources: [],
24
- componentRels: [],
25
- });
26
- await engine.getManager(SceneManager).loadScene(sceneJson, "publish", LoadSceneMode.Single);
15
+ import { THREE } from "@easytwin/runtime";
16
+ import { TwinApp, type TwinAppContext } from "@easytwin/apps";
17
+
18
+ export default class App extends TwinApp {
19
+ async init(ctx: TwinAppContext) {
20
+ // engine / container 已有;scene / camera / runtimeScene 为 null
21
+ void ctx;
22
+ }
23
+
24
+ async onSceneLoaded(ctx: TwinAppContext) {
25
+ const mesh = new THREE.Mesh(
26
+ new THREE.BoxGeometry(1, 1, 1),
27
+ new THREE.MeshBasicMaterial({ color: 0x44aa88 }),
28
+ );
29
+ ctx.scene?.add(mesh);
30
+ ctx.sceneCleanup(() => {
31
+ ctx.scene?.remove(mesh);
32
+ });
33
+ }
34
+
35
+ onUpdate(ctx: TwinAppContext, delta: number, elapsed: number) {
36
+ void ctx;
37
+ void delta;
38
+ void elapsed;
39
+ }
40
+
41
+ onBeforeSceneUnload(ctx: TwinAppContext) {
42
+ void ctx;
43
+ }
44
+
45
+ onDispose(ctx: TwinAppContext) {
46
+ void ctx;
47
+ }
48
+ }
27
49
  ```
28
50
 
29
- 4. 在组件生命周期/事件里写渲染逻辑;自定义组件继承 `Component` / `VirtualRootComponent`,见 `references/lifecycle-events.md`。
51
+ 等价对象写法:`export default defineApp({ init, onSceneLoaded, onUpdate, ... })`。宿主只认这两种;函数导出(旧 `main`)会报错。
52
+
53
+ 可上传到在线 TwinApp 的 runtime 导出名只有:`THREE`、`RuntimeEngine`、`SceneManager`、`LoadSceneMode`、`convertObjToComponentJson`。超出这些名字本地能编过并 warning,在线会失败。
54
+
55
+ 生命周期由宿主驱动。引擎与场景已在预览页创建;`init` 里场景字段为 null,物体与事件写在 `onSceneLoaded`,用 `ctx.sceneCleanup` 对称拆除。
56
+
57
+ | 钩子 | 时机 |
58
+ | --- | --- |
59
+ | `init` | 引擎 / 容器已有;场景字段为 null |
60
+ | `onSceneLoaded` | 宿主 `loadScene` 之后 |
61
+ | `onUpdate` | 引擎时钟每帧(加载中不调);仅此钩子 throw 走 `onError`(返回 `true` 继续) |
62
+ | `onBeforeSceneUnload` | 再 Run / 卸场景前 |
63
+ | `onDispose` | 应用拆除;引擎不 destroy,下次 Run 复用 |
64
+
65
+ 本地预览只有当前这一份场景,`ctx.sceneManager.loadScene` 会 reject。`ctx.assets.text/json` 读工作区相对路径。从 `ctx.engine` 取引擎,不要再 `RuntimeEngine.create`。
66
+
67
+ 1. 若还没有类型:运行 `easytwin skills sync`(无 tsconfig 会生成最小配置,含 `@easytwin/runtime` 与 `@easytwin/apps` 的 paths;已有 tsconfig 时按输出片段自行添加)。
68
+ 2. 编译校验:`easytwin bundle`。lodash 等外部依赖会报错。超出在线 stub 的 runtime import 只 warning。
69
+ 3. 运行:打开插件场景预览(先看到三维场景),点底部 **Run**。再点 Run 会先 `onBeforeSceneUnload` / `onDispose`,复用同一台引擎。编译失败不打断已渲染场景;mock 下同样可用。
30
70
 
31
- > TODO(外部输入): 官方"script 引入全局对象"的加载方式尚未确认。当前以 npm 包 `@easytwin/runtime` + `RuntimeEngine.create` 为准;若官方后续给出 script 方式,在此补充并同步更新 references
71
+ `Component` / `VirtualRootComponent` 等整包 API 本地预览可用,但**不是**可上传子集;需要组件生命周期细节时读 `references/lifecycle-events.md`。
32
72
 
33
73
  ## 参考(references/)
34
74
 
35
75
  API 细节按域拆分,按需加载,不要一次全读:
36
76
 
37
- - 需要"安装、初始化引擎、engine/scene/manager 关系、销毁"时读 `references/intro.md`
77
+ - 需要"初始化引擎、engine/scene/manager 关系、销毁"时读 `references/intro.md`
38
78
  - 需要"场景加载/序列化结构/资产加载"时读 `references/scene-and-assets.md`
39
79
  - 需要"组件生命周期、状态/数据、事件"时读 `references/lifecycle-events.md`
40
80
  - 需要"相机/坐标、2D、动画、物理、地理、工具函数"时读 `references/rendering-apis.md`
@@ -1,21 +1,24 @@
1
1
  # 引入与初始化
2
2
 
3
+ ## TwinApp 里如何拿到引擎
4
+
5
+ 用户代码入口是 `src/main.ts` 的 `export default class App extends TwinApp`。预览页 / 在线环境已经创建引擎并加载场景;钩子里用 `ctx.engine` / `ctx.container` / `ctx.scene`。可上传子集只用 `THREE` / `RuntimeEngine` / `SceneManager` / `LoadSceneMode` / `convertObjToComponentJson`。
6
+
3
7
  ## 包信息
4
8
 
5
- - npm 包:`@easytwin/runtime`(ESM 模块)。
6
- - peer 依赖:`three@0.183.0`(需自行安装);内部依赖 rapier3d、@tweenjs/tween.js、mobx、localforage、proj4、stats-gl 等,由包自身携带。
7
- - 根导出:`manager`、`core`、`loader`、`utils`、`packages`、`server`、`type`、`three-extension`、`constants`、`module` 命名空间;另有 `THREE`、`Mobx`、`TweenJs`、`Semver` 命名空间与若干直接导出(详见 rendering-apis.md)。
9
+ - npm 包名:`@easytwin/runtime`(商业包,公网不存在)。类型由 `easytwin skills sync` 写入 `.easytwin/types/`,运行走预览页 Run 或 `easytwin bundle`。
10
+ - `TwinApp` / `defineApp` / `TwinAppContext` 从 `@easytwin/apps` 引入(同样由 sync 分发)。
8
11
 
9
- ## 初始化 RuntimeEngine
12
+ ## RuntimeEngine.create(宿主调用)
10
13
 
11
- 唯一入口:`RuntimeEngine.create(config)`(静态、异步)。
14
+ 工厂方法:`RuntimeEngine.create(config)`(静态、异步)。TwinApp 从 `ctx.engine` 取用已创建的实例。
12
15
 
13
16
  ```ts
14
17
  import { RuntimeEngine, SceneManager, LoadSceneMode } from "@easytwin/runtime";
15
18
 
16
19
  const engine = await RuntimeEngine.create({
17
20
  containerId: "twin-root", // 必填:容器 id,须已存在于 DOM
18
- baseOSSUrl: "https://assets.easyv.cloud", // 必填:资产根地址
21
+ baseOSSUrl: "https://dt-easyv-test.oss-cn-hangzhou.aliyuncs.com/", // 必填:官方 OSS 资产根
19
22
  customComponentDeps: { /* 见下 */ }, // 必填:注入给自定义组件的依赖
20
23
  sceneResources: [], // 可选:场景资源清单,默认 []
21
24
  componentRels: [], // 可选:组件引用关系,默认 []
@@ -29,7 +32,7 @@ const engine = await RuntimeEngine.create({
29
32
  | 字段 | 必填 | 说明 |
30
33
  | --- | --- | --- |
31
34
  | `containerId` | 是 | 容器元素 id;`document.getElementById(containerId)` 找不到即抛错 |
32
- | `baseOSSUrl` | 是 | 服务端资产根地址;draco / ktx2 / webp / LCC 等系统库 URL 由此拼出 |
35
+ | `baseOSSUrl` | 是 | 官方 OSS 资产根;draco / ktx2 / webp / 组件脚本 URL 由此拼出(`{baseOSSUrl}/easytwin/system/libs/…`、`{baseOSSUrl}/easytwin/system/components/custom/…`) |
33
36
  | `customComponentDeps` | 是 | 自定义组件脚本 import 的依赖表,键为模块名,值为实现 |
34
37
  | `localOSSUrl` | 否 | 本地资产根地址;非空时自定义组件脚本优先读 `${localOSSUrl}/${type}@${version}.js` |
35
38
  | `sceneResources` | 否 | 场景资源清单(ListSceneResourceVo[]) |
@@ -78,4 +81,4 @@ RuntimeSceneMode = { Editor: "editor", Preview: "preview", EasyV: "easyV", Publi
78
81
 
79
82
  ## 参考源码
80
83
 
81
- - `src/core/RuntimeEngine.ts`、`src/core/RuntimeScene.ts`、`src/core/Time.ts`。
84
+ - `src/core/RuntimeEngine.ts`、`src/core/RuntimeScene.ts`、`src/core/Time.ts`。
@@ -1,6 +1,6 @@
1
1
  # 场景加载与资产
2
2
 
3
- > 说明:本节讲的是 **runtime 内部**的场景 JSON(组件树序列化结构,`SceneJson`)与资产加载;`easytwin scene pull` 拉的是**服务端场景 JSON**,两者出处不同。可用 `SceneManager.importScene` `loadScene` 把本地 JSON 喂给 runtime
3
+ > 说明:本节讲的是 runtime 的场景 JSON(组件树序列化结构,`SceneJson`,定义见 `src/core/interface.ts`)与资产加载。`easytwin scene pull` 拉取的服务端场景 JSON 同样以该结构为准(见 easytwin-scene)。TwinApp 场景由宿主加载;本地预览里 `ctx.sceneManager.loadScene` reject。下面是 SceneManager 内部 API
4
4
 
5
5
  ## 场景管理(SceneManager)
6
6
 
@@ -74,7 +74,8 @@ interface SceneJson { id: string; name: string; sceneComponent: ComponentJson; }
74
74
  - `loadModelAsync(address, onProgress?)` → `GLTFLoadResult`(带缓存;失败 fallback 默认模型)。
75
75
  - `getModelInitData(url, onProgress?)` → `{ animation, sceneGraph, materialSlots, materialMap }`;animation 项:`autoPlay / loop("repeat"|"once"|"pingpong") / stopAtEnd / timeScale / id / name`。
76
76
  - `loadTextureAsync(address, onProgress?)`;`loadVideoTextureAsync(address)`(自动建 video:`muted`、`loop`、`playsInline`、`crossOrigin: "anonymous"`,mp4 source);`loadHDRTextureAsync(address, onProgress?)`(`.exr` 走 EXRLoader,否则 RGBELoader,自动 `EquirectangularReflectionMapping`)。
77
- - `loadCustomComponentScript(type, version, externalDeps)`:localOSSUrl 非空优先读 `${localOSSUrl}/${type}@${version}.js`,否则 `${baseOSSUrl}/easytwin/system/components/custom/${type}/${version}/script.js`。
77
+ - `loadCustomComponentScript(type, version, externalDeps)`:localOSSUrl 非空优先读 `${localOSSUrl}/${type}@${version}.js`,否则 `${baseOSSUrl}/easytwin/system/components/custom/${type}/${version}/script.js`(例:`https://dt-easyv-test.oss-cn-hangzhou.aliyuncs.com/easytwin/system/components/custom/Postprocessing/0.0.9/script.js`)。
78
+ - 系统库同样由 `baseOSSUrl` 拼出:webp wasm 为 `${baseOSSUrl}/easytwin/system/libs/webp-wasm.wasm`,draco 为 `${baseOSSUrl}/easytwin/system/libs/draco/`。
78
79
  - `cacheModelByFileAsync(file, address)`;`instantiateModel(model)`(同 url 共享 geometry/纹理源);`getResourceUrl(relativeUrl)`。
79
80
 
80
81
  **资源路径约定(README)**:data 和 state 中**以 `$R` 结尾的字段**会被判定为资源路径,要求 value 是 string,参与导入导出(如 `url$R`)。
@@ -91,4 +92,4 @@ options = {
91
92
  ```
92
93
 
93
94
  - 默认 `useEnv` / `useIndexDB` / `useLoadingEffect` 为 `true`,`enableCollision` 为 `false`;返回实例 id,失败 `null`。
94
- - `scene.unloadLCC(id)` / `scene.unloadAllLCC()`;`scene.clearLCC()` 只清实例,保留 `window.LCCRender` 便于复用。
95
+ - `scene.unloadLCC(id)` / `scene.unloadAllLCC()`;`scene.clearLCC()` 只清实例,保留 `window.LCCRender` 便于复用。
@@ -5,15 +5,43 @@ description: 当用户要列出场景、查看有哪些场景、或把某个场
5
5
 
6
6
  # 场景管理
7
7
 
8
- - `easytwin scene list`:列出凭据可见的场景。
9
- - `easytwin scene pull <id> [--out <path>]`:拉取场景 JSON 到本地,缺省输出 `./<id>.scene.json`。
8
+ - `easytwin scene list`:列出 SDK 应用已关联的场景(id 为 Scene Key),并把摘要(id / name / linkedSceneId / snapshotUrl / defaultLoading)写入项目 `easytwin.config.json` 的 `scenes` 数组(全量覆盖,不含场景 JSON)。
9
+ - `easytwin scene pull <id> [--out <path>]`:按 Scene Key(或关联记录 id)拉取场景快照 JSON 到本地,缺省输出 `./<id>.scene.json`。
10
10
 
11
11
  ## 场景 JSON 格式
12
12
 
13
- <!-- TODO(外部输入): 场景 JSON 具体格式尚未定稿。格式到位后在此补充字段说明,并同步更新 scene/upload 实现与场景预览。 -->
14
- 场景 JSON 的字段结构待定(向 EasyTwin 维护者索取)。当前拉取结果按服务端原样保存,字段以实际返回为准。
13
+ 场景 JSON easytwin-runtime `SceneJson` 为准(`packages/easytwin-runtime/src/core/interface.ts`),即组件树序列化结构:
15
14
 
16
- > 注意:此处是**服务端场景 JSON**;runtime 内部用于 loadScene 的 SceneJson(组件树序列化结构)见 easytwin-render 的 references/scene-and-assets.md,两者出处不同。
15
+ ```ts
16
+ type StateJson<T = any> = { id: string; name: string; config: T; using: boolean; rank: number };
17
+
18
+ type ComponentJson<S = any, D = any> = {
19
+ id: string; // 实体 id,仅用于层级关系
20
+ active: boolean; // 世界大纲 config 修改
21
+ lock: boolean; // 仅编辑器生效(是否被选中)
22
+ collapsed: boolean; // 世界大纲 config 修改
23
+ componentId: string; // 组件 id,兼容后端绑定关系
24
+ name: string;
25
+ type: string;
26
+ version: string;
27
+ states?: StateJson<S>[];
28
+ data?: D;
29
+ children: ComponentJson[];
30
+ parentId: string | null; // 引擎不直接存储 parentId
31
+ };
32
+
33
+ interface SceneJson { id: string; name: string; sceneComponent: ComponentJson; }
34
+ ```
35
+
36
+ `easytwin scene pull` 按服务端原样保存;该结构可直接作为渲染开发参考与本地预览输入(runtime 的 `SceneManager.loadScene` / `importScene` 接受它,详见 easytwin-render 的 references/scene-and-assets.md)。
37
+
38
+ ## 本地测试模式(mock)
39
+
40
+ 当 `appId` 与 `appSecret` 均为 `test` 时,`scene list` / `scene pull` 不再请求服务端,改读 devkit 包内 `scene.example.json`:
41
+
42
+ - 场景 id 取 `objs[].sceneId` 首个非空值(当前示例为 `sceJTHH9yoqFyRyS9`),场景名固定为「本地示例场景」;
43
+ - `scene pull` 的 id 必须与示例 id 一致,否则报错;
44
+ - 命令输出会标注「本地测试模式」。该模式仅用于本地联调,不发网络请求。
17
45
 
18
46
  ## 典型用法
19
47
 
@@ -22,5 +50,5 @@ description: 当用户要列出场景、查看有哪些场景、或把某个场
22
50
  ```bash
23
51
  easytwin scene list
24
52
  easytwin scene pull scene-123 --out ./scenes/scene-123.json
25
- ```
26
-
53
+ ```
54
+
@@ -1,21 +1,38 @@
1
1
  ---
2
2
  name: easytwin-upload
3
- description: 当用户要把本地开发产物目录上传回 EasyTwin、全量覆盖服务端内容、或询问上传是否可逆/会覆盖什么时使用。
3
+ description: 当用户要把本地开发产物目录上传回 EasyTwin、从服务端拉取工作区代码、远端没有代码时创建默认文件、或询问上传是否可逆/与本地 diff 如何同步时使用。
4
4
  ---
5
5
 
6
- # 全量上传(覆盖不可逆)
6
+ # 工作区代码:拉取与全量上传
7
7
 
8
- `easytwin upload <dir>` 把 `<dir>` 下的全部文件上传,按 App ID 决定的目标空间**全量覆盖**。
8
+ 上传与拉取共用同一套工作区代码接口,方向相反。
9
9
 
10
- > ⚠️ **覆盖不可逆**:上传会覆盖服务端同名内容,没有本地清单与回滚机制。上传前务必确认目录内容与目标空间。
10
+ ## 拉取:`easytwin pull [dir]`
11
+
12
+ 从服务端下载 App ID 对应的工作区文件到 `[dir]`(缺省当前目录)。
13
+
14
+ - 远端为空:本地没有 `src/main.ts` 时写入默认 TwinApp 子类入口;本地已有入口则不覆盖。
15
+ - 远端有文件:与本地做 diff。`identical` 跳过,`remote-only` 写入,`local-only` 保留,**内容冲突默认不覆盖**。
16
+ - `--force` 才用远端内容覆盖冲突文件;即使 `--force` 也不删除仅本地文件。
17
+ - `--dry-run` 只打印 diff,不写盘。
18
+ - 本地测试模式(`appId`/`appSecret` 均为 `test`):不发请求,按远端为空处理。
19
+
20
+ 有冲突时先看摘要,确认要用远端覆盖再加 `--force`。插件里可对冲突文件打开「远程 ↔ 本地」diff。
11
21
 
12
- ## 行为
22
+ ## 上传:`easytwin upload <dir>`
23
+
24
+ 把 `<dir>` 下的全部文件上传,按 App ID 决定的目标空间**全量覆盖**。
25
+
26
+ > ⚠️ **覆盖不可逆**:上传会覆盖服务端同名内容,没有本地清单与回滚机制。上传前务必确认目录内容与目标空间。
13
27
 
14
28
  - 递归收集 `<dir>` 下所有文件(默认跳过 `.git`)。
15
- - 逐文件 multipart 上传,带进度输出。
16
- - 上传目标空间由凭据(App ID)决定;pull 场景与 upload 目录是两条独立能力,互不引用。
29
+ - 按工作区代码文件接口全量覆盖:先拉远端文件列表与约束(扩展名/数量/目录深度),再删除远端多余文件、批量更新变更、逐个新建缺失文件。
30
+ - 上传目标空间由凭据(App ID)决定;场景 pull 与工作区 pull/upload 是独立能力,互不引用。
31
+
32
+ 当 `appId` 与 `appSecret` 均为 `test` 时,`easytwin upload <dir>` 退化为本地空跑(dry-run):只收集与统计文件、不上传,进度照常上报,输出标注「本地测试模式」。
17
33
 
18
34
  ## 步骤
19
35
 
20
- 1. 确认目标空间(App ID)与目录内容。
21
- 2. `easytwin upload <dir>`,观察进度与完成摘要。
36
+ 1. 新项目或本地还没有代码:`easytwin pull`(远端空则得到默认 `src/main.ts`)
37
+ 2. 本地已有改动且可能与远端不同:`easytwin pull --dry-run` 看 diff,再决定是否 `--force`。
38
+ 3. 确认目标空间(App ID)与目录内容后,`easytwin upload <dir>` 上传(不可逆)。