@fenglimg/cocos-state-controller 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +287 -0
- package/assets/script/controller/Capability.ts +100 -0
- package/assets/script/controller/Capability.ts.meta +10 -0
- package/assets/script/controller/CapabilityRegistry.ts +116 -0
- package/assets/script/controller/CapabilityRegistry.ts.meta +10 -0
- package/assets/script/controller/EnumPropRefMap.ts +232 -0
- package/assets/script/controller/EnumPropRefMap.ts.meta +10 -0
- package/assets/script/controller/NestedCtrlData.ts +199 -0
- package/assets/script/controller/NestedCtrlData.ts.meta +10 -0
- package/assets/script/controller/PrefabIntrospection.ts +151 -0
- package/assets/script/controller/PrefabIntrospection.ts.meta +10 -0
- package/assets/script/controller/Props.meta +13 -0
- package/assets/script/controller/StateControllerV2.ts +1957 -0
- package/assets/script/controller/StateControllerV2.ts.meta +10 -0
- package/assets/script/controller/StateEnumV2.ts +165 -0
- package/assets/script/controller/StateEnumV2.ts.meta +10 -0
- package/assets/script/controller/StateErrorManagerV2.ts +217 -0
- package/assets/script/controller/StateErrorManagerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropHandlerV2.ts +316 -0
- package/assets/script/controller/StatePropHandlerV2.ts.meta +10 -0
- package/assets/script/controller/StatePropertyControlService.ts +148 -0
- package/assets/script/controller/StatePropertyControlService.ts.meta +10 -0
- package/assets/script/controller/StateSelectV2.ts +4542 -0
- package/assets/script/controller/StateSelectV2.ts.meta +10 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts +30 -0
- package/assets/script/controller/capabilities/AutoSyncCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/EventCapability.ts +144 -0
- package/assets/script/controller/capabilities/EventCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts +94 -0
- package/assets/script/controller/capabilities/MigrationCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts +157 -0
- package/assets/script/controller/capabilities/MultiCtrlBindingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts +124 -0
- package/assets/script/controller/capabilities/PropertyControlCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts +69 -0
- package/assets/script/controller/capabilities/RecordingCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts +88 -0
- package/assets/script/controller/capabilities/SelectedPageIdCapability.ts.meta +10 -0
- package/assets/script/controller/capabilities.meta +13 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts +138 -0
- package/assets/script/controller/props/CtrlInspectorGroups.ts.meta +10 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts +104 -0
- package/assets/script/controller/props/SelectInspectorGroups.ts.meta +10 -0
- package/bin/csc.js +286 -0
- package/package.json +60 -0
- package/packages/state-controller-v2-panel/README.md +80 -0
- package/packages/state-controller-v2-panel/inspector-inject.js +917 -0
- package/packages/state-controller-v2-panel/inspector-probe.json +3767 -0
- package/packages/state-controller-v2-panel/lib/handlers.js +534 -0
- package/packages/state-controller-v2-panel/main.js +149 -0
- package/packages/state-controller-v2-panel/package.json +32 -0
- package/packages/state-controller-v2-panel/panel/build.js +23 -0
- package/packages/state-controller-v2-panel/panel/logic.js +1207 -0
- package/packages/state-controller-v2-panel/panel/styles.css +454 -0
- package/packages/state-controller-v2-panel/panel/template.html +296 -0
- package/packages/state-controller-v2-panel/scene-accessor.js +657 -0
- package/skills/cocos-state-controller/SKILL.md +28 -0
- package/skills/cocos-state-controller/refs/cli-usage.md +78 -0
- package/skills/cocos-state-controller/refs/editor-guide.md +127 -0
- package/skills/cocos-state-controller/refs/migrate.md +106 -0
- package/skills/cocos-state-controller/refs/upstream-pr.md +66 -0
- package/tools/migration/migrate-prefab-v1-to-v2.js +608 -0
- package/tools/state-controller-sync-manifest.json +33 -0
package/bin/csc.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* csc — @fenglimg/cocos-state-controller 分发/同步 CLI 入口。
|
|
6
|
+
*
|
|
7
|
+
* P0: 命令路由骨架。各子命令在后续阶段填充:
|
|
8
|
+
* install / diff / doctor → P3 update → P4 sync → P5
|
|
9
|
+
* migrate / skill → P6 核心数据层 / 三方引擎 → P1 / P2
|
|
10
|
+
*
|
|
11
|
+
* 设计:入口只做 argv 路由 + help/version;命令实现落 lib/commands/*(P1 起建)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const installMod = require('../lib/commands/install');
|
|
17
|
+
const diffMod = require('../lib/commands/diff');
|
|
18
|
+
const doctorMod = require('../lib/commands/doctor');
|
|
19
|
+
const updateMod = require('../lib/commands/update');
|
|
20
|
+
const syncMod = require('../lib/commands/sync');
|
|
21
|
+
const migrateMod = require('../lib/commands/migrate');
|
|
22
|
+
const skillMod = require('../lib/commands/skill');
|
|
23
|
+
const lockMod = require('../lib/lock');
|
|
24
|
+
const baselineMod = require('../lib/baseline');
|
|
25
|
+
|
|
26
|
+
/** @typedef {(args: string[]) => (number|void|Promise<number|void>)} CommandHandler */
|
|
27
|
+
|
|
28
|
+
/** 极简 flag 解析:--key value / --flag。 */
|
|
29
|
+
function parseFlags(args) {
|
|
30
|
+
const flags = {};
|
|
31
|
+
const positional = [];
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
const a = args[i];
|
|
34
|
+
if (a.startsWith('--')) {
|
|
35
|
+
const key = a.slice(2);
|
|
36
|
+
const next = args[i + 1];
|
|
37
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
38
|
+
flags[key] = next;
|
|
39
|
+
i++;
|
|
40
|
+
} else {
|
|
41
|
+
flags[key] = true;
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
positional.push(a);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { flags, positional };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 后续阶段把这些占位换成 require('../lib/commands/<cmd>')。 */
|
|
51
|
+
function notYetImplemented(name, phase) {
|
|
52
|
+
return function () {
|
|
53
|
+
console.error(`csc ${name}: 尚未实现(${phase})`);
|
|
54
|
+
return 2;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function cmdInstall(args) {
|
|
59
|
+
const { flags } = parseFlags(args);
|
|
60
|
+
const payloadRoot = path.join(__dirname, '..');
|
|
61
|
+
const targetRoot = process.cwd();
|
|
62
|
+
const pkg = require(path.join(payloadRoot, 'package.json'));
|
|
63
|
+
const installPaths = { ...installMod.DEFAULT_INSTALL_PATHS };
|
|
64
|
+
if (flags['runtime-path']) installPaths.runtime = flags['runtime-path'];
|
|
65
|
+
if (flags['panel-path']) installPaths.panel = flags['panel-path'];
|
|
66
|
+
if (flags.version) console.error('注意:--version 跨版本安装自 P4 起支持,当前用 CLI 自带版本净荷。');
|
|
67
|
+
|
|
68
|
+
const r = installMod.install({ payloadRoot, targetRoot, packageVersion: pkg.version, installPaths });
|
|
69
|
+
if (r.collisions.length) {
|
|
70
|
+
console.error('✗ uuid 撞车,已中止安装(未改任何文件):');
|
|
71
|
+
for (const c of r.collisions) {
|
|
72
|
+
console.error(` ${c.uuid}\n 包→ ${c.packageDest}\n 冲突← ${c.consumerFiles.join(', ')}`);
|
|
73
|
+
}
|
|
74
|
+
console.error('请重生成本地冲突方的 uuid(绝不动包的),再重试。');
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
console.log(`✓ 安装 ${r.copied.length} 个文件 → ${targetRoot}`);
|
|
78
|
+
if (!r.cocos) console.warn('⚠ 未探测到 Cocos 版本(project.json)');
|
|
79
|
+
else if (!r.cocosSupported) console.warn(`⚠ Cocos ${r.cocos.version} 不在 2.4.x 支持范围(仅警告)`);
|
|
80
|
+
else console.log(` Cocos ${r.cocos.version} ✓`);
|
|
81
|
+
console.log('→ 重启 Cocos 编辑器以加载面板');
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cmdDiff() {
|
|
86
|
+
let r;
|
|
87
|
+
try {
|
|
88
|
+
r = diffMod.diffInstalled(process.cwd());
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.error('✗ ' + e.message);
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
const total = r.added.length + r.removed.length + r.modified.length;
|
|
94
|
+
if (!total) {
|
|
95
|
+
console.log('无差异(与装的版本一致)');
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
r.added.forEach((p) => console.log(`+ ${p}`));
|
|
99
|
+
r.removed.forEach((p) => console.log(`- ${p}`));
|
|
100
|
+
r.modified.forEach((p) => console.log(`~ ${p}`));
|
|
101
|
+
console.log(`\n共 ${total} 项(+${r.added.length} -${r.removed.length} ~${r.modified.length})`);
|
|
102
|
+
return 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function cmdDoctor() {
|
|
106
|
+
const r = doctorMod.doctor(process.cwd());
|
|
107
|
+
const icon = { ok: '✓', warn: '⚠', fail: '✗', skipped: '·' };
|
|
108
|
+
for (const c of r.checks) console.log(`${icon[c.status] || '?'} ${c.name}: ${c.detail}`);
|
|
109
|
+
console.log(`\n体检${r.ok ? '通过' : '发现问题'}`);
|
|
110
|
+
return r.ok ? 0 : 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function cmdUpdate(args) {
|
|
114
|
+
const { flags } = parseFlags(args);
|
|
115
|
+
const targetRoot = process.cwd();
|
|
116
|
+
const lock = lockMod.readLock(targetRoot);
|
|
117
|
+
if (!lock) {
|
|
118
|
+
console.error('✗ 未安装:先 csc install');
|
|
119
|
+
return 1;
|
|
120
|
+
}
|
|
121
|
+
const pkg = require(path.join(__dirname, '..', 'package.json')).name;
|
|
122
|
+
let basePayloadRoot;
|
|
123
|
+
let newPayloadRoot;
|
|
124
|
+
try {
|
|
125
|
+
// base = lock 记录的 vX pristine;new = 目标版本(默认 latest)。本地 dogfood 可用 --base-payload/--new-payload 覆盖。
|
|
126
|
+
basePayloadRoot = flags['base-payload'] || baselineMod.reconstructBaseline({ pkg, version: lock.packageVersion });
|
|
127
|
+
newPayloadRoot = flags['new-payload'] || baselineMod.reconstructBaseline({ pkg, version: flags.version || 'latest' });
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.error('✗ 取净荷失败(跨版本 update 需包已发布到 npm):' + e.message);
|
|
130
|
+
return 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const r = updateMod.update({ targetRoot, basePayloadRoot, newPayloadRoot, newVersion: flags.version });
|
|
134
|
+
const R = r.results;
|
|
135
|
+
console.log(
|
|
136
|
+
`更新到 v${r.packageVersion}:clean ${R.clean.length} / merged ${R.merged.length} / ` +
|
|
137
|
+
`conflict ${R.conflict.length} / added ${R.added.length} / removed ${R.removed.length}`
|
|
138
|
+
);
|
|
139
|
+
if (r.hasConflict) {
|
|
140
|
+
console.warn('⚠ 冲突文件(含 <<<< 标记),需人工/AI 解:');
|
|
141
|
+
R.conflict.forEach((p) => console.warn(' ' + p));
|
|
142
|
+
return 1;
|
|
143
|
+
}
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function cmdSync(args) {
|
|
148
|
+
const { flags } = parseFlags(args);
|
|
149
|
+
if (!flags.upstream) {
|
|
150
|
+
console.error('用法: csc sync --upstream [--output patch.diff]');
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
const targetRoot = process.cwd();
|
|
154
|
+
const lock = lockMod.readLock(targetRoot);
|
|
155
|
+
if (!lock) {
|
|
156
|
+
console.error('✗ 未安装:先 csc install');
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
const pkg = require(path.join(__dirname, '..', 'package.json')).name;
|
|
160
|
+
let basePayloadRoot;
|
|
161
|
+
try {
|
|
162
|
+
basePayloadRoot = flags['base-payload'] || baselineMod.reconstructBaseline({ pkg, version: lock.packageVersion });
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.error('✗ 取基线失败(需包已发布到 npm):' + e.message);
|
|
165
|
+
return 1;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const r = syncMod.sync({ targetRoot, basePayloadRoot });
|
|
169
|
+
if (!r.hasChanges) {
|
|
170
|
+
console.log('无上行改动(与装的版本一致)');
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
if (flags.output && typeof flags.output === 'string') {
|
|
174
|
+
fs.writeFileSync(flags.output, r.patch);
|
|
175
|
+
console.log(`patch 写入 ${flags.output}`);
|
|
176
|
+
} else {
|
|
177
|
+
process.stdout.write(r.patch);
|
|
178
|
+
}
|
|
179
|
+
console.error(
|
|
180
|
+
`\n// 改动 modified ${r.summary.modified} / added ${r.summary.added} / removed ${r.summary.removed}` +
|
|
181
|
+
'(交上行 PR skill 据 §4 漂移政策取舍 + 开 GitHub PR)'
|
|
182
|
+
);
|
|
183
|
+
return 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function cmdMigrate(args) {
|
|
187
|
+
const { flags, positional } = parseFlags(args);
|
|
188
|
+
if (!positional.length) {
|
|
189
|
+
console.error('用法: csc migrate <prefab|dir...> [--write] [--backup] [--allow-remote]');
|
|
190
|
+
return 1;
|
|
191
|
+
}
|
|
192
|
+
const packageRoot = path.join(__dirname, '..');
|
|
193
|
+
const r = migrateMod.migrate({
|
|
194
|
+
targets: positional,
|
|
195
|
+
projectRoot: process.cwd(),
|
|
196
|
+
packageRoot,
|
|
197
|
+
write: !!flags.write,
|
|
198
|
+
backup: !!flags.backup,
|
|
199
|
+
allowRemote: !!flags['allow-remote'],
|
|
200
|
+
});
|
|
201
|
+
if (!r.ok) {
|
|
202
|
+
console.error('✗ remote bundle 默认拒绝迁移(迁 V2-cid 会崩仅 V1 runtime 的老客户端):');
|
|
203
|
+
r.blocked.forEach((b) => console.error(` ${b.file} — ${b.reason}`));
|
|
204
|
+
console.error('确认全客户端已铺 V1+V2 共存 runtime 后,加 --allow-remote 重试。');
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
if (r.output) process.stdout.write(r.output);
|
|
208
|
+
if (r.blocked.length) console.warn(`⚠ ${r.blocked.length} 个 remote bundle 文件已放行(--allow-remote)`);
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function cmdSkill(args) {
|
|
213
|
+
const [sub, ...rest] = args;
|
|
214
|
+
if (sub !== 'install') {
|
|
215
|
+
console.error('用法: csc skill install [--target claude|codex|all]');
|
|
216
|
+
return 1;
|
|
217
|
+
}
|
|
218
|
+
const { flags } = parseFlags(rest);
|
|
219
|
+
const packageRoot = path.join(__dirname, '..');
|
|
220
|
+
const r = skillMod.skillInstall({ packageRoot, projectRoot: process.cwd(), target: flags.target || 'all' });
|
|
221
|
+
console.log(`✓ 安装 ${r.installed.length} 个 skill 目标:`);
|
|
222
|
+
r.installed.forEach((t) => console.log(' ' + t));
|
|
223
|
+
return 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** @type {Record<string, { phase: string, summary: string, handler: CommandHandler }>} */
|
|
227
|
+
const COMMANDS = {
|
|
228
|
+
install: { phase: 'P3', summary: '拷净荷 + 写 .meta + 写 .csc/lock.json + uuid 撞车预检 + 提示重启编辑器',
|
|
229
|
+
handler: cmdInstall },
|
|
230
|
+
update: { phase: 'P4', summary: '取新版净荷:未改的覆盖 / 改过的三方合并;更新 lock',
|
|
231
|
+
handler: cmdUpdate },
|
|
232
|
+
diff: { phase: 'P3', summary: 'consumer 当前 vs 装的版本 pristine(归一化后)列 新增/删除/修改',
|
|
233
|
+
handler: cmdDiff },
|
|
234
|
+
doctor: { phase: 'P3', summary: '体检:文件齐全 / .meta uuid / uuid 撞车 / V1 cid 残留 / Cocos 版本 / lock 一致',
|
|
235
|
+
handler: cmdDoctor },
|
|
236
|
+
migrate: { phase: 'P6', summary: '确定性 prefab V1→V2 迁移引擎(remote bundle 默认拒绝)',
|
|
237
|
+
handler: cmdMigrate },
|
|
238
|
+
sync: { phase: 'P5', summary: '重建 vX 基线 + 反归一化 + 三方 diff → 输出 patch(交 AI Skill 开 PR)',
|
|
239
|
+
handler: cmdSync },
|
|
240
|
+
skill: { phase: 'P6', summary: '分发 skills 到 .claude/.codex',
|
|
241
|
+
handler: cmdSkill },
|
|
242
|
+
uninstall: { phase: 'P3', summary: '按 lock 移除 managed 文件 + .csc/(回退)',
|
|
243
|
+
handler: notYetImplemented('uninstall', 'P3') }
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
function printVersion() {
|
|
247
|
+
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
248
|
+
console.log(`${pkg.name} ${pkg.version}`);
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function printHelp() {
|
|
253
|
+
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
254
|
+
console.log(`${pkg.name} — csc CLI\n`);
|
|
255
|
+
console.log('用法: csc <command> [options]\n');
|
|
256
|
+
console.log('命令:');
|
|
257
|
+
const width = Math.max(...Object.keys(COMMANDS).map((k) => k.length));
|
|
258
|
+
for (const [name, { phase, summary }] of Object.entries(COMMANDS)) {
|
|
259
|
+
console.log(` ${name.padEnd(width)} [${phase}] ${summary}`);
|
|
260
|
+
}
|
|
261
|
+
console.log('\n --help, -h 显示此帮助');
|
|
262
|
+
console.log(' --version, -v 显示版本');
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function main(argv) {
|
|
267
|
+
const [cmd, ...rest] = argv;
|
|
268
|
+
if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') return printHelp();
|
|
269
|
+
if (cmd === '--version' || cmd === '-v') return printVersion();
|
|
270
|
+
|
|
271
|
+
const entry = COMMANDS[cmd];
|
|
272
|
+
if (!entry) {
|
|
273
|
+
console.error(`未知命令: ${cmd}\n`);
|
|
274
|
+
printHelp();
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
const code = await entry.handler(rest);
|
|
278
|
+
return typeof code === 'number' ? code : 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
main(process.argv.slice(2))
|
|
282
|
+
.then((code) => { process.exitCode = code; })
|
|
283
|
+
.catch((err) => {
|
|
284
|
+
console.error('csc 致命错误:', err && err.stack ? err.stack : err);
|
|
285
|
+
process.exitCode = 1;
|
|
286
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fenglimg/cocos-state-controller",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cocos Creator State Controller — 状态控制器组件 + 分发/同步 CLI (csc)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "fenglimg <fenglimg90@gmail.com>",
|
|
7
|
+
"homepage": "https://github.com/fenglimg/cocos-state-controller#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/fenglimg/cocos-state-controller.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/fenglimg/cocos-state-controller/issues"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cocos",
|
|
20
|
+
"cocos-creator",
|
|
21
|
+
"state-controller",
|
|
22
|
+
"state-machine",
|
|
23
|
+
"csc",
|
|
24
|
+
"cli"
|
|
25
|
+
],
|
|
26
|
+
"bin": {
|
|
27
|
+
"csc": "bin/csc.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin/",
|
|
31
|
+
"assets/script/controller/",
|
|
32
|
+
"packages/state-controller-v2-panel/",
|
|
33
|
+
"tools/migration/",
|
|
34
|
+
"tools/state-controller-sync-manifest.json",
|
|
35
|
+
"skills/cocos-state-controller/",
|
|
36
|
+
"skills/cocos-scv2-migrate/"
|
|
37
|
+
],
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"lint": "eslint assets/ --ext .ts,.js",
|
|
43
|
+
"lint:fix": "eslint assets/ --ext .ts,.js --fix",
|
|
44
|
+
"migrate:v1-to-v2": "node tools/migration/migrate-prefab-v1-to-v2.js",
|
|
45
|
+
"test": "cd tests && npx jest",
|
|
46
|
+
"prepare": "husky"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
50
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
51
|
+
"eslint": "^8.57.0",
|
|
52
|
+
"husky": "^9.0.0",
|
|
53
|
+
"lint-staged": "^15.0.0"
|
|
54
|
+
},
|
|
55
|
+
"lint-staged": {
|
|
56
|
+
"assets/**/*.ts": [
|
|
57
|
+
"eslint --fix"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# State Controller Panel — Wave 3 scaffold (本期)
|
|
2
|
+
|
|
3
|
+
> 状态: 骨架就绪 (Claude), Panel UI 待 Gemini 接入
|
|
4
|
+
> 设计文档: `.workflow/active/wave3-runtime-capability/PANEL-BRIEF-v0.2.md`
|
|
5
|
+
|
|
6
|
+
## 目录结构
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
packages/state-controller-v2-panel/
|
|
10
|
+
├── package.json Cocos 2.x 插件清单 (dockable panel 配置)
|
|
11
|
+
├── main.js 入口, 响应 :open / :close
|
|
12
|
+
├── scene-accessor.js scene-script, IPC 路由层, 调 lib/handlers.js
|
|
13
|
+
├── lib/handlers.js 纯函数业务层, 单测覆盖 (tests/panel/handlers.test.ts)
|
|
14
|
+
└── panel/ ⬅ Gemini 接 UI 在这里
|
|
15
|
+
├── build.js cocos 面板生命周期 (Polymer-style register)
|
|
16
|
+
├── template.html panel HTML
|
|
17
|
+
├── styles.css 样式
|
|
18
|
+
└── logic.js UI 交互逻辑
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## IPC 契约 (panel → scene-accessor)
|
|
22
|
+
|
|
23
|
+
panel/build.js 用 `Editor.Ipc.sendToPanel('scene', 'state-controller-v2-panel:<msg>', payload, callback)`
|
|
24
|
+
向 scene-script 发请求, 路由名见下表 (与 scene-accessor.js 的 message handler 一一对应):
|
|
25
|
+
|
|
26
|
+
| 消息名 | payload | 返回 (result) | 作用 |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| `state-controller-v2-panel:list-ctrls` | (空) | `[{uuid, ctrlId, ctrlName}]` | Panel 打开时, 列场景所有 StateController |
|
|
29
|
+
| `state-controller-v2-panel:get-ctrl-snapshot` | `{uuid}` | `CtrlSnapshot` | 拉某 ctrl 完整数据 (states 列表 + 当前 index + recording 等) |
|
|
30
|
+
| `state-controller-v2-panel:set-selected-index` | `{uuid, index}` | `boolean` | 切预览中的 state |
|
|
31
|
+
| `state-controller-v2-panel:set-state-by-id` | `{uuid, stateId}` | `boolean` | 用稳定 stateId 切换 |
|
|
32
|
+
| `state-controller-v2-panel:set-home-page` | `{uuid, stateIdOrName}` | `boolean` | 设默认启动状态 (-1 清除) |
|
|
33
|
+
| `state-controller-v2-panel:set-recording` | `{uuid, isRecording}` | `boolean` | 开/关录制 |
|
|
34
|
+
| `state-controller-v2-panel:add-state` | `{uuid, name}` | `stateId number, -1 失败` | 新增 state |
|
|
35
|
+
| `state-controller-v2-panel:remove-state` | `{uuid, index}` | `boolean` | 删 state (至少保留 1 个) |
|
|
36
|
+
| `state-controller-v2-panel:add-property` | `{ctrlUuid, selectUuid, propType}` | `boolean` | 手动加 prop |
|
|
37
|
+
| `state-controller-v2-panel:dispose-all-bridges` | (空) | `true` | Panel 关闭时调, 解所有广播桥 |
|
|
38
|
+
|
|
39
|
+
## 广播事件 (scene-accessor → panel)
|
|
40
|
+
|
|
41
|
+
scene-accessor 收到 capability 事件后, 经 `Editor.Ipc.sendToPanel('state-controller-v2-panel', '<event>', payload)`:
|
|
42
|
+
|
|
43
|
+
| 事件名 | payload | 触发时机 |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `state-controller-v2-panel:on-state-changed` | `{ctrlId, fromState, toState, fromName, toName}` | EventCapability stateChanged |
|
|
46
|
+
| `state-controller-v2-panel:on-recording-changed` | `{ctrlId, isRecording}` | startRecording / stopRecording |
|
|
47
|
+
| `state-controller-v2-panel:on-data-changed` | `{ctrlId}` | add/remove state, add prop 等本期所有数据变 |
|
|
48
|
+
|
|
49
|
+
## CtrlSnapshot 结构
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
{
|
|
53
|
+
ctrlId: number,
|
|
54
|
+
ctrlName: string,
|
|
55
|
+
selectedIndex: number,
|
|
56
|
+
selectedStateId: number,
|
|
57
|
+
homePageStateId: number, // -1 = 未设
|
|
58
|
+
isRecording: boolean,
|
|
59
|
+
states: [
|
|
60
|
+
{ index: number, stateId: number, name: string },
|
|
61
|
+
// ...
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 给 Gemini 的实装要点
|
|
67
|
+
|
|
68
|
+
1. **不要碰 lib/handlers.js / scene-accessor.js**. 这两层是 Claude 维护的 IPC + 业务函数, 已有
|
|
69
|
+
测试覆盖 (tests/panel/handlers.test.ts 15 case + sceneAccessor.smoke.test.ts 2 case).
|
|
70
|
+
2. **新建 panel/ 子目录** (template.html / styles.css / logic.js / build.js).
|
|
71
|
+
build.js 是 cocos 2.x Polymer-style 入口, 参考 `packages/ccc-smart-component-manager/panel/`
|
|
72
|
+
实现风格.
|
|
73
|
+
3. **UI 布局参照** `PANEL-BRIEF-v0.2.md` §1 wireframe + §4 槽位映射表.
|
|
74
|
+
4. **缩略图实装** 参照 brief §5 (RenderTexture + LRU 缓存). 本期可仅留占位 (灰底 + 状态名),
|
|
75
|
+
不实装真截图; 用 `// TODO Wave 3 后期: 实装缩略图` 标记位置.
|
|
76
|
+
5. **W4/W5 扩展位置** 全部 `// TODO Wave 4` / `// TODO Wave 5` 占位, 不写死布局.
|
|
77
|
+
6. **不引入 React/Vue/带 bundler 框架**. Polymer-style webcomponent 或 vanilla DOM 都行.
|
|
78
|
+
7. **测试**: UI 层 Claude 不写测试, 由 Gemini 自行在编辑器内实测. 但 IPC 调用要走
|
|
79
|
+
scene-accessor 的固定路由名, 不要绕开 (绕开就丢了 broadcast 桥).
|
|
80
|
+
8. **Panel 关闭** 时 build.js 必须调 `dispose-all-bridges`, 否则会泄漏 capability 注册.
|