@combos-fun/plugin-development-tool 0.0.33 → 0.0.34
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/agent-skill.md +16 -50
- package/dist/plugin-development-tool.cjs.js +178 -38
- package/dist/plugin-development-tool.cjs.js.map +1 -1
- package/dist/plugin-development-tool.cjs.prod.js +1 -1
- package/dist/plugin-development-tool.d.ts +27 -1
- package/dist/plugin-development-tool.esm.js +178 -38
- package/dist/plugin-development-tool.esm.js.map +1 -1
- package/package.json +6 -5
|
@@ -3,6 +3,7 @@ import { Component, System, OBSERVER_TYPE, GameObject, decorators } from '@combo
|
|
|
3
3
|
import { RendererSystem } from '@combos-fun/plugin-renderer';
|
|
4
4
|
import { Graphics } from '@combos-fun/plugin-renderer-graphics';
|
|
5
5
|
import { Event, HIT_AREA_TYPE } from '@combos-fun/plugin-renderer-event';
|
|
6
|
+
import { IDE_PROPERTY_METADATA } from '@combos-fun/inspector-decorator';
|
|
6
7
|
|
|
7
8
|
/** Toggle development-tool selection / parent postMessage. Payload: `{ enabled: boolean }`. */
|
|
8
9
|
const COMBOS_DEVELOPMENT_TOOL_SET = 'combos-development-tool:set';
|
|
@@ -199,30 +200,17 @@ function isSceneSourceAnchor(v) {
|
|
|
199
200
|
typeof v.file === 'string');
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
'
|
|
204
|
-
|
|
205
|
-
'
|
|
206
|
-
|
|
207
|
-
'
|
|
208
|
-
|
|
209
|
-
'
|
|
210
|
-
|
|
211
|
-
'
|
|
212
|
-
|
|
213
|
-
const COMPONENT_SKIP_KEYS = {
|
|
214
|
-
Graphics: new Set(['graphics']),
|
|
215
|
-
Physics: new Set([
|
|
216
|
-
'body',
|
|
217
|
-
'Body',
|
|
218
|
-
'PhysicsEngine',
|
|
219
|
-
'World',
|
|
220
|
-
'Constraint',
|
|
221
|
-
'mouseConstraint',
|
|
222
|
-
'bodyParams',
|
|
223
|
-
]),
|
|
224
|
-
Event: new Set(['hitArea']),
|
|
225
|
-
};
|
|
203
|
+
function resolveValueType(value) {
|
|
204
|
+
if (typeof value === 'string')
|
|
205
|
+
return 'string';
|
|
206
|
+
if (typeof value === 'number')
|
|
207
|
+
return 'number';
|
|
208
|
+
if (typeof value === 'boolean')
|
|
209
|
+
return 'boolean';
|
|
210
|
+
if (value !== null && typeof value === 'object')
|
|
211
|
+
return 'object';
|
|
212
|
+
return 'unsupported';
|
|
213
|
+
}
|
|
226
214
|
function cloneJsonSafe(value, depth = 0) {
|
|
227
215
|
if (value === null || value === undefined)
|
|
228
216
|
return value;
|
|
@@ -247,20 +235,171 @@ function cloneJsonSafe(value, depth = 0) {
|
|
|
247
235
|
}
|
|
248
236
|
return undefined;
|
|
249
237
|
}
|
|
238
|
+
function readFieldReflectSchema(ctor) {
|
|
239
|
+
const properties = Reflect.getMetadata(IDE_PROPERTY_METADATA, ctor) ||
|
|
240
|
+
{};
|
|
241
|
+
return Object.keys(properties).map(key => ({
|
|
242
|
+
key,
|
|
243
|
+
...properties[key],
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
/** Legacy `@type` / `@step` write `constructor.IDEProps` as an object keyed by property name. */
|
|
247
|
+
function readLegacyIdePropsSchema(ctor) {
|
|
248
|
+
const raw = ctor.IDEProps;
|
|
249
|
+
if (!raw || Array.isArray(raw) || typeof raw !== 'object') {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
return Object.keys(raw).map(key => {
|
|
253
|
+
const entry = raw[key] || {};
|
|
254
|
+
return {
|
|
255
|
+
key,
|
|
256
|
+
type: typeof entry.type === 'string' ? entry.type : undefined,
|
|
257
|
+
step: typeof entry.step === 'number' ? entry.step : undefined,
|
|
258
|
+
min: typeof entry.min === 'number' ? entry.min : undefined,
|
|
259
|
+
max: typeof entry.max === 'number' ? entry.max : undefined,
|
|
260
|
+
label: typeof entry.label === 'string' ? entry.label : undefined,
|
|
261
|
+
description: typeof entry.description === 'string' ? entry.description : undefined,
|
|
262
|
+
group: typeof entry.group === 'string' ? entry.group : undefined,
|
|
263
|
+
editor: entry.editor,
|
|
264
|
+
enumOptions: Array.isArray(entry.enumOptions) ? entry.enumOptions : undefined,
|
|
265
|
+
unit: typeof entry.unit === 'string' ? entry.unit : undefined,
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function collectSchemaProps(comp) {
|
|
270
|
+
const ctor = comp.constructor;
|
|
271
|
+
const fromField = readFieldReflectSchema(ctor);
|
|
272
|
+
if (fromField.length > 0) {
|
|
273
|
+
return fromField;
|
|
274
|
+
}
|
|
275
|
+
return readLegacyIdePropsSchema(ctor);
|
|
276
|
+
}
|
|
277
|
+
function mapEditorKind(options, valueType) {
|
|
278
|
+
if (options.editor) {
|
|
279
|
+
return options.editor;
|
|
280
|
+
}
|
|
281
|
+
if (valueType === 'boolean')
|
|
282
|
+
return 'toggle';
|
|
283
|
+
if (options.enumOptions?.length && valueType === 'string')
|
|
284
|
+
return 'enum';
|
|
285
|
+
if (valueType === 'number') {
|
|
286
|
+
if (options.min !== undefined && options.max !== undefined) {
|
|
287
|
+
return 'number-slider';
|
|
288
|
+
}
|
|
289
|
+
return 'number-stepper';
|
|
290
|
+
}
|
|
291
|
+
if (valueType === 'string')
|
|
292
|
+
return 'text';
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
function buildNumberMeta(options) {
|
|
296
|
+
if (options.min === undefined &&
|
|
297
|
+
options.max === undefined &&
|
|
298
|
+
options.step === undefined &&
|
|
299
|
+
options.unit === undefined) {
|
|
300
|
+
return undefined;
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
min: options.min,
|
|
304
|
+
max: options.max,
|
|
305
|
+
step: options.step,
|
|
306
|
+
unit: options.unit,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function titleCaseLeaf(leaf) {
|
|
310
|
+
return leaf
|
|
311
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
312
|
+
.replace(/_/g, ' ')
|
|
313
|
+
.replace(/^./, char => char.toUpperCase());
|
|
314
|
+
}
|
|
315
|
+
function resolveLabel(options, path) {
|
|
316
|
+
if (options.label?.trim()) {
|
|
317
|
+
return options.label.trim();
|
|
318
|
+
}
|
|
319
|
+
const leaf = path[path.length - 1];
|
|
320
|
+
return leaf ? titleCaseLeaf(leaf) : undefined;
|
|
321
|
+
}
|
|
322
|
+
function leafDescriptorsFromValue(componentName, options, path, value) {
|
|
323
|
+
const description = options.description?.trim();
|
|
324
|
+
if (!description) {
|
|
325
|
+
// Scene Edit only exposes fields that include a human description.
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const valueType = resolveValueType(value);
|
|
329
|
+
if (valueType === 'unsupported') {
|
|
330
|
+
return [];
|
|
331
|
+
}
|
|
332
|
+
const typeHint = options.type;
|
|
333
|
+
const isVector = typeHint === 'vector2' ||
|
|
334
|
+
(valueType === 'object' &&
|
|
335
|
+
value !== null &&
|
|
336
|
+
'x' in value &&
|
|
337
|
+
'y' in value);
|
|
338
|
+
const isSize = typeHint === 'size' ||
|
|
339
|
+
(valueType === 'object' &&
|
|
340
|
+
value !== null &&
|
|
341
|
+
'width' in value &&
|
|
342
|
+
'height' in value);
|
|
343
|
+
if (isVector || isSize) {
|
|
344
|
+
const record = value;
|
|
345
|
+
const axes = isSize ? ['width', 'height'] : ['x', 'y'];
|
|
346
|
+
const baseLabel = resolveLabel(options, path) ?? componentName;
|
|
347
|
+
const out = [];
|
|
348
|
+
for (const axis of axes) {
|
|
349
|
+
const axisValue = record[axis];
|
|
350
|
+
const axisType = resolveValueType(axisValue);
|
|
351
|
+
if (axisType === 'unsupported' || axisType === 'object')
|
|
352
|
+
continue;
|
|
353
|
+
const axisPath = [...path, axis];
|
|
354
|
+
out.push({
|
|
355
|
+
path: axisPath,
|
|
356
|
+
value: axisValue,
|
|
357
|
+
valueType: axisType,
|
|
358
|
+
label: `${baseLabel} ${axis.toUpperCase()}`,
|
|
359
|
+
description,
|
|
360
|
+
group: options.group ?? componentName,
|
|
361
|
+
editorKind: mapEditorKind(options, axisType),
|
|
362
|
+
numberMeta: buildNumberMeta(options),
|
|
363
|
+
persistTarget: componentName === 'Transform' ? 'gameObjectConstructor' : 'component',
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
return out;
|
|
367
|
+
}
|
|
368
|
+
if (valueType === 'object') {
|
|
369
|
+
return [];
|
|
370
|
+
}
|
|
371
|
+
const label = resolveLabel(options, path);
|
|
372
|
+
if (!label) {
|
|
373
|
+
return [];
|
|
374
|
+
}
|
|
375
|
+
return [
|
|
376
|
+
{
|
|
377
|
+
path,
|
|
378
|
+
value: cloneJsonSafe(value),
|
|
379
|
+
valueType,
|
|
380
|
+
label,
|
|
381
|
+
description,
|
|
382
|
+
group: options.group ?? componentName,
|
|
383
|
+
editorKind: mapEditorKind(options, valueType),
|
|
384
|
+
enumOptions: options.enumOptions,
|
|
385
|
+
numberMeta: buildNumberMeta(options),
|
|
386
|
+
persistTarget: componentName === 'Transform' ? 'gameObjectConstructor' : 'component',
|
|
387
|
+
},
|
|
388
|
+
];
|
|
389
|
+
}
|
|
250
390
|
function serializeComponent(comp) {
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
391
|
+
const componentName = comp.name;
|
|
392
|
+
const schema = collectSchemaProps(comp);
|
|
393
|
+
if (schema.length === 0) {
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
396
|
+
const out = [];
|
|
397
|
+
const record = comp;
|
|
398
|
+
for (const prop of schema) {
|
|
399
|
+
const value = record[prop.key];
|
|
259
400
|
if (typeof value === 'function')
|
|
260
401
|
continue;
|
|
261
|
-
|
|
262
|
-
if (cloned !== undefined)
|
|
263
|
-
out[key] = cloned;
|
|
402
|
+
out.push(...leafDescriptorsFromValue(componentName, prop, [prop.key], value));
|
|
264
403
|
}
|
|
265
404
|
return out;
|
|
266
405
|
}
|
|
@@ -279,8 +418,9 @@ function buildGameObjectSnapshot(go) {
|
|
|
279
418
|
.filter(c => c.name !== 'CombosDevelopmentToolTarget')
|
|
280
419
|
.map(c => ({
|
|
281
420
|
componentName: c.name,
|
|
282
|
-
|
|
283
|
-
}))
|
|
421
|
+
fieldDescriptors: serializeComponent(c),
|
|
422
|
+
}))
|
|
423
|
+
.filter(c => c.fieldDescriptors.length > 0);
|
|
284
424
|
return {
|
|
285
425
|
id: go.id,
|
|
286
426
|
name: go.name,
|
|
@@ -1233,7 +1373,7 @@ var CombosDevelopmentToolSystem$1 = CombosDevelopmentToolSystem;
|
|
|
1233
1373
|
/** Auto-generated by scripts/build-package.mjs — do not edit. */
|
|
1234
1374
|
Object.assign(CombosDevelopmentToolSystem$1, {
|
|
1235
1375
|
packageName: "@combos-fun/plugin-development-tool",
|
|
1236
|
-
packageVersion: "0.0.
|
|
1376
|
+
packageVersion: "0.0.34",
|
|
1237
1377
|
});
|
|
1238
1378
|
|
|
1239
1379
|
export { COMBOS_DEVELOPMENT_TOOL_APPLY_PROPERTY, COMBOS_DEVELOPMENT_TOOL_CLEAR_SELECTION, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_DESELECTED, COMBOS_DEVELOPMENT_TOOL_GAMEOBJECT_SELECTED, COMBOS_DEVELOPMENT_TOOL_MARKER_OVERLAY_SUCCESS, COMBOS_DEVELOPMENT_TOOL_READY, COMBOS_DEVELOPMENT_TOOL_REFRESH, COMBOS_DEVELOPMENT_TOOL_SET, COMBOS_DEVELOPMENT_TOOL_SET_MARKER_OVERLAY, COMBOS_DEVELOPMENT_TOOL_SET_MUTED, COMBOS_DEVELOPMENT_TOOL_SET_SUCCESS, COMBOS_DEVELOPMENT_TOOL_STATE_CHANGED, CombosDevelopmentToolSystem$1 as CombosDevelopmentToolSystem, CombosDevelopmentToolTarget, DEFAULT_ALLOWED_MESSAGE_HOST_SUFFIXES, MARKER_COMPONENTS, MARKER_ICON_SIZE, applyPropertyValue, applyPropertyWithHooks, buildGameObjectDeselectedPayload, buildGameObjectSnapshot, drawMarkerIcon, isAllowedMessageOrigin, isClearSelectionMessage, mergeAllowedMessageOrigins, readPropertyValue, readSourceAnchor, resolveMarkerDef, shouldNotifyGameObjectDeselected };
|