@neocompose/cli 0.64.1 → 0.65.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/CHANGELOG.md +7 -0
- package/dist/neo.mjs +65 -25
- package/dist/test-worker.mjs +26 -14
- package/package.json +1 -1
- package/skills/neocompose-cli/SKILL.md +1 -1
- package/skills/neocompose-cli/references/animation-and-world-authoring.md +31 -0
- package/skills/neocompose-cli/references/cli-development.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.65.0] - 2026-10-03
|
|
4
|
+
|
|
5
|
+
- Move `Collider` from `NeoObject` to `NeoObjectBase`, so sprite objects and layer groups can carry a box collider too. Unity codegen implements `INeoColliderSource` on `NeoObjectBase`.
|
|
6
|
+
- Segment tracks may target a Class member, such as `NeoObjectBase.Collider`. Each frame plays a whole value or `null`. A segment whose class does not fit the target member reports which class it plays. A segment of nullable values cannot target a required member.
|
|
7
|
+
- Constructor arguments and object initializers typed by a class's generic parameter take the site's type argument. `new NeoAnimationSegmentFrame<NeoCollider?>(value: null)` and `{ Value = null }` now compile.
|
|
8
|
+
- Authoring `null` over a member that held a class instance drops the instance's class and constructor metadata. Previously the row kept them, and push rejected it as an invalid value record.
|
|
9
|
+
|
|
3
10
|
## [0.64.1] - 2026-10-02
|
|
4
11
|
|
|
5
12
|
- Reserve `OnChanged` as a member name. Every generated Unity class declares the SDK's `OnChanged` change subscription, so a member by that name now reports `reserved-project-name` instead of pushing the SDK method aside as `OnChanged_2`.
|
package/dist/neo.mjs
CHANGED
|
@@ -14192,11 +14192,18 @@ function validateExpression(expression, expected, scope, environment, uri, range
|
|
|
14192
14192
|
siteArgumentNames,
|
|
14193
14193
|
siteInitializerNames
|
|
14194
14194
|
);
|
|
14195
|
+
const siteTypeArguments = expression.typeArguments?.map(semanticAstType) ?? (expression.className === null ? expected?.arguments : void 0) ?? [];
|
|
14196
|
+
const siteBindings = new Map(
|
|
14197
|
+
declaration.genericParameters.flatMap((parameter4, index) => {
|
|
14198
|
+
const argument2 = siteTypeArguments[index];
|
|
14199
|
+
return argument2 ? [[parameter4.name, argument2]] : [];
|
|
14200
|
+
})
|
|
14201
|
+
);
|
|
14195
14202
|
for (const diagnostic of validateNeoConstructionSite(
|
|
14196
14203
|
environment.construction,
|
|
14197
14204
|
{
|
|
14198
14205
|
className: declaration.name,
|
|
14199
|
-
typeArguments:
|
|
14206
|
+
typeArguments: siteTypeArguments,
|
|
14200
14207
|
argumentNames: variantSite?.argumentNames ?? siteArgumentNames,
|
|
14201
14208
|
initializerNames: variantSite?.initializerNames ?? siteInitializerNames
|
|
14202
14209
|
}
|
|
@@ -14232,7 +14239,7 @@ function validateExpression(expression, expected, scope, environment, uri, range
|
|
|
14232
14239
|
(candidate) => candidate.name === (variantMember ?? named)
|
|
14233
14240
|
) : fields[index];
|
|
14234
14241
|
const expectedType = parameter4?.type ?? field?.type;
|
|
14235
|
-
const expectedSemanticType = declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME && named === "collectionValue" ? { name: "string" } : expectedType ? semanticType(expectedType) : void 0;
|
|
14242
|
+
const expectedSemanticType = declaration.name === NEO_LOOKUP_VARIANT_FOLDER_TYPE_NAME && named === "collectionValue" ? { name: "string" } : expectedType ? substituteSemanticType(semanticType(expectedType), siteBindings) : void 0;
|
|
14236
14243
|
validateExpression(
|
|
14237
14244
|
argument2,
|
|
14238
14245
|
expectedSemanticType,
|
|
@@ -14253,8 +14260,11 @@ function validateExpression(expression, expected, scope, environment, uri, range
|
|
|
14253
14260
|
validateExpression(
|
|
14254
14261
|
entry.value,
|
|
14255
14262
|
field ? substituteSemanticType(
|
|
14256
|
-
|
|
14257
|
-
|
|
14263
|
+
substituteSemanticType(
|
|
14264
|
+
semanticType(field.member.type),
|
|
14265
|
+
field.bindings
|
|
14266
|
+
),
|
|
14267
|
+
siteBindings
|
|
14258
14268
|
) : void 0,
|
|
14259
14269
|
scope,
|
|
14260
14270
|
environment,
|
|
@@ -14469,7 +14479,7 @@ function sourceClassMemberByName(declaration, name, environment, initialBindings
|
|
|
14469
14479
|
function substituteSemanticType(type, bindings) {
|
|
14470
14480
|
const bound = bindings.get(type.name);
|
|
14471
14481
|
if (bound !== void 0 && (type.arguments ?? []).length === 0) {
|
|
14472
|
-
return type.nullable ===
|
|
14482
|
+
return type.nullable === true ? { ...bound, nullable: true } : bound;
|
|
14473
14483
|
}
|
|
14474
14484
|
const argumentsList2 = (type.arguments ?? []).map(
|
|
14475
14485
|
(argument2) => substituteSemanticType(argument2, bindings)
|
|
@@ -22778,7 +22788,12 @@ var init_strict_resolver = __esm({
|
|
|
22778
22788
|
};
|
|
22779
22789
|
});
|
|
22780
22790
|
fields.push(
|
|
22781
|
-
...this.resolveCallSiteInitializerFields(
|
|
22791
|
+
...this.resolveCallSiteInitializerFields(
|
|
22792
|
+
schemaClass2,
|
|
22793
|
+
targetType,
|
|
22794
|
+
expression,
|
|
22795
|
+
scope
|
|
22796
|
+
)
|
|
22782
22797
|
);
|
|
22783
22798
|
const schemaClassInfo = toWireType(targetType, this.project);
|
|
22784
22799
|
if (schemaClassInfo.type !== 7 /* Class */) {
|
|
@@ -22904,6 +22919,7 @@ var init_strict_resolver = __esm({
|
|
|
22904
22919
|
) : [];
|
|
22905
22920
|
const fields = this.resolveCallSiteInitializerFields(
|
|
22906
22921
|
schemaClass2,
|
|
22922
|
+
targetType,
|
|
22907
22923
|
expression,
|
|
22908
22924
|
scope
|
|
22909
22925
|
);
|
|
@@ -23018,7 +23034,7 @@ var init_strict_resolver = __esm({
|
|
|
23018
23034
|
* the class's own schema exactly as a `classConstructor` field is, and
|
|
23019
23035
|
* applied after the body so an explicit assignment always wins.
|
|
23020
23036
|
*/
|
|
23021
|
-
resolveCallSiteInitializerFields(schemaClass2, expression, scope) {
|
|
23037
|
+
resolveCallSiteInitializerFields(schemaClass2, targetType, expression, scope) {
|
|
23022
23038
|
const entries = expression.initializer ?? [];
|
|
23023
23039
|
const seen = /* @__PURE__ */ new Set();
|
|
23024
23040
|
return entries.map((entry) => {
|
|
@@ -23063,10 +23079,15 @@ var init_strict_resolver = __esm({
|
|
|
23063
23079
|
entry.pos
|
|
23064
23080
|
);
|
|
23065
23081
|
}
|
|
23066
|
-
const
|
|
23067
|
-
|
|
23082
|
+
const memberType2 = substituteTypeParameters(
|
|
23083
|
+
member.type,
|
|
23084
|
+
targetType,
|
|
23085
|
+
schemaClass2
|
|
23086
|
+
);
|
|
23087
|
+
const value = this.resolveExpression(entry.value, scope, memberType2);
|
|
23088
|
+
if (!isNeoScriptTypeAssignable(value.type, memberType2, this.project)) {
|
|
23068
23089
|
throw new CompileError(
|
|
23069
|
-
`Object initializer '${member.name}' expected ${this.describe(
|
|
23090
|
+
`Object initializer '${member.name}' expected ${this.describe(memberType2)} but got ${this.describe(value.type)}.`,
|
|
23070
23091
|
entry.pos
|
|
23071
23092
|
);
|
|
23072
23093
|
}
|
|
@@ -29934,7 +29955,7 @@ var init_registry = __esm({
|
|
|
29934
29955
|
"../packages/manifest/schema-contract/registry.mjs"() {
|
|
29935
29956
|
"use strict";
|
|
29936
29957
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
29937
|
-
cliVersion: "0.
|
|
29958
|
+
cliVersion: "0.65.0",
|
|
29938
29959
|
projectFileUploadBatchSize: 32,
|
|
29939
29960
|
documentRecords: {
|
|
29940
29961
|
member: {
|
|
@@ -43302,6 +43323,9 @@ function createMergeMemo() {
|
|
|
43302
43323
|
function resolveInheritanceChain(classId, classes, draft) {
|
|
43303
43324
|
return [...indexClasses(classes).inheritanceChain(classId, draft)];
|
|
43304
43325
|
}
|
|
43326
|
+
function resolveInheritanceChainFromIndex(classId, classIndex, draft) {
|
|
43327
|
+
return resolveChainFromIndex(classId, classIndex, draft);
|
|
43328
|
+
}
|
|
43305
43329
|
function resolveChainFromIndex(classId, index, draft) {
|
|
43306
43330
|
const chain = [];
|
|
43307
43331
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -44374,6 +44398,14 @@ var init_world_system_classes_generated = __esm({
|
|
|
44374
44398
|
docsText: "When false, this object and its children are neither rendered nor collided with. The value stays live: member writes still apply and a running animation clip keeps playing. Disabling an object hides its whole subtree regardless of each child's own value, and re-enabling it restores exactly what was there.",
|
|
44375
44399
|
requirement: 1 /* Required */,
|
|
44376
44400
|
schemaKey: "Enabled"
|
|
44401
|
+
},
|
|
44402
|
+
{
|
|
44403
|
+
memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
|
|
44404
|
+
memberKind: "class",
|
|
44405
|
+
defaultValue: null,
|
|
44406
|
+
docsText: "The object's box collider, or null for none. Any world object can carry one: placed objects, nested children, and sprites alike.",
|
|
44407
|
+
schemaClassWorldKind: "objectCollider",
|
|
44408
|
+
schemaKey: "Collider"
|
|
44377
44409
|
}
|
|
44378
44410
|
],
|
|
44379
44411
|
worldKind: "objectBase"
|
|
@@ -44583,13 +44615,6 @@ var init_world_system_classes_generated = __esm({
|
|
|
44583
44615
|
requirement: 1 /* Required */,
|
|
44584
44616
|
schemaKey: "Children"
|
|
44585
44617
|
},
|
|
44586
|
-
{
|
|
44587
|
-
memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
|
|
44588
|
-
memberKind: "class",
|
|
44589
|
-
defaultValue: null,
|
|
44590
|
-
schemaClassWorldKind: "objectCollider",
|
|
44591
|
-
schemaKey: "Collider"
|
|
44592
|
-
},
|
|
44593
44618
|
{
|
|
44594
44619
|
memberId: "system_c609fed3-53e4-5147-b493-e4aa23031d79",
|
|
44595
44620
|
memberKind: "computed",
|
|
@@ -61100,7 +61125,7 @@ function resolveMemberDeclaredTypeInfoInternal(member, members, seen) {
|
|
|
61100
61125
|
members,
|
|
61101
61126
|
nextSeen
|
|
61102
61127
|
);
|
|
61103
|
-
return argumentType === null ? [] : [[genericParamId,
|
|
61128
|
+
return argumentType === null ? [] : [[genericParamId, argumentType]];
|
|
61104
61129
|
}
|
|
61105
61130
|
)
|
|
61106
61131
|
);
|
|
@@ -73507,7 +73532,7 @@ function runtimeTypeCheck(value, checkType, ctx, seenCollections = /* @__PURE__
|
|
|
73507
73532
|
function runtimeValueMatchesType(value, typeInfo, ctx, seenCollections = /* @__PURE__ */ new Set()) {
|
|
73508
73533
|
if (typeInfo.type === NS_TYPE_UNKNOWN) return true;
|
|
73509
73534
|
if (value === null || value === void 0) {
|
|
73510
|
-
return typeInfo.type === 0 /* Null */ || !typeInfo.required;
|
|
73535
|
+
return typeInfo.type === 0 /* Null */ || typeInfo.type === 21 /* Generic */ || !typeInfo.required;
|
|
73511
73536
|
}
|
|
73512
73537
|
return runtimeTypeCheck(value, typeInfo, ctx, seenCollections);
|
|
73513
73538
|
}
|
|
@@ -103116,11 +103141,26 @@ var init_animation_clips = __esm({
|
|
|
103116
103141
|
`${args.label} targets "${boundChildName}.${targetEntry.schemaKey}" of kind ${MemberKind[targetMember.kind]}, but its segment plays ${MemberKind[valueBindingMember.kind]} values.`
|
|
103117
103142
|
);
|
|
103118
103143
|
}
|
|
103119
|
-
if (!
|
|
103120
|
-
|
|
103144
|
+
if (isMemberClass(targetMember) && isMemberClass(valueBindingMember) && !resolveInheritanceChainFromIndex(
|
|
103145
|
+
valueBindingMember.classId,
|
|
103146
|
+
this.classById
|
|
103147
|
+
).some((current) => current.id === targetMember.classId)) {
|
|
103148
|
+
const targetClassName = this.classById.get(targetMember.classId)?.name ?? targetMember.classId;
|
|
103149
|
+
const valueClassName2 = this.classById.get(valueBindingMember.classId)?.name ?? valueBindingMember.classId;
|
|
103150
|
+
throw new Error(
|
|
103151
|
+
`${args.label} targets "${boundChildName}.${targetEntry.schemaKey}" of class ${targetClassName}, but its segment plays ${valueClassName2} values.`
|
|
103152
|
+
);
|
|
103153
|
+
}
|
|
103154
|
+
if (isRequiredMember2(targetMember) && !isRequiredMember2(valueBindingMember)) {
|
|
103155
|
+
throw new Error(
|
|
103156
|
+
`${args.label} targets "${boundChildName}.${targetEntry.schemaKey}", which is required, but its segment plays nullable values.`
|
|
103157
|
+
);
|
|
103158
|
+
}
|
|
103159
|
+
if (animationTrackStorage(
|
|
103121
103160
|
[targetMember.id],
|
|
103161
|
+
this.memberById,
|
|
103122
103162
|
args.childStoragePath
|
|
103123
|
-
)) {
|
|
103163
|
+
) === null) {
|
|
103124
103164
|
throw new Error(
|
|
103125
103165
|
`${args.label} targets "${boundChildName}.${targetEntry.schemaKey}", which is not a writable Save, Session, or Writable override leaf.`
|
|
103126
103166
|
);
|
|
@@ -124540,7 +124580,7 @@ function lowerValueBody(context, member, expression, base, source, environment,
|
|
|
124540
124580
|
if (member.required) {
|
|
124541
124581
|
throw new Error(`Required member ${member.name} cannot store null.`);
|
|
124542
124582
|
}
|
|
124543
|
-
return
|
|
124583
|
+
return overlayLiteralValueContent(next, { value: null });
|
|
124544
124584
|
}
|
|
124545
124585
|
switch (member.kind) {
|
|
124546
124586
|
case "null":
|
|
@@ -156165,7 +156205,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
156165
156205
|
async function main() {
|
|
156166
156206
|
const args = parseArgs(process.argv.slice(2));
|
|
156167
156207
|
if (args.command === "--version") {
|
|
156168
|
-
console.log("0.
|
|
156208
|
+
console.log("0.65.0");
|
|
156169
156209
|
return;
|
|
156170
156210
|
}
|
|
156171
156211
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|
package/dist/test-worker.mjs
CHANGED
|
@@ -6058,6 +6058,14 @@ var WORLD_SYSTEM_CLASS_DEFINITIONS = [
|
|
|
6058
6058
|
docsText: "When false, this object and its children are neither rendered nor collided with. The value stays live: member writes still apply and a running animation clip keeps playing. Disabling an object hides its whole subtree regardless of each child's own value, and re-enabling it restores exactly what was there.",
|
|
6059
6059
|
requirement: 1 /* Required */,
|
|
6060
6060
|
schemaKey: "Enabled"
|
|
6061
|
+
},
|
|
6062
|
+
{
|
|
6063
|
+
memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
|
|
6064
|
+
memberKind: "class",
|
|
6065
|
+
defaultValue: null,
|
|
6066
|
+
docsText: "The object's box collider, or null for none. Any world object can carry one: placed objects, nested children, and sprites alike.",
|
|
6067
|
+
schemaClassWorldKind: "objectCollider",
|
|
6068
|
+
schemaKey: "Collider"
|
|
6061
6069
|
}
|
|
6062
6070
|
],
|
|
6063
6071
|
worldKind: "objectBase"
|
|
@@ -6267,13 +6275,6 @@ var WORLD_SYSTEM_CLASS_DEFINITIONS = [
|
|
|
6267
6275
|
requirement: 1 /* Required */,
|
|
6268
6276
|
schemaKey: "Children"
|
|
6269
6277
|
},
|
|
6270
|
-
{
|
|
6271
|
-
memberId: "system_a0083c92-72f7-405f-8863-ff86f995d36d",
|
|
6272
|
-
memberKind: "class",
|
|
6273
|
-
defaultValue: null,
|
|
6274
|
-
schemaClassWorldKind: "objectCollider",
|
|
6275
|
-
schemaKey: "Collider"
|
|
6276
|
-
},
|
|
6277
6278
|
{
|
|
6278
6279
|
memberId: "system_c609fed3-53e4-5147-b493-e4aa23031d79",
|
|
6279
6280
|
memberKind: "computed",
|
|
@@ -18651,7 +18652,7 @@ function runtimeTypeCheck(value, checkType, ctx, seenCollections = /* @__PURE__
|
|
|
18651
18652
|
function runtimeValueMatchesType(value, typeInfo, ctx, seenCollections = /* @__PURE__ */ new Set()) {
|
|
18652
18653
|
if (typeInfo.type === NS_TYPE_UNKNOWN) return true;
|
|
18653
18654
|
if (value === null || value === void 0) {
|
|
18654
|
-
return typeInfo.type === 0 /* Null */ || !typeInfo.required;
|
|
18655
|
+
return typeInfo.type === 0 /* Null */ || typeInfo.type === 21 /* Generic */ || !typeInfo.required;
|
|
18655
18656
|
}
|
|
18656
18657
|
return runtimeTypeCheck(value, typeInfo, ctx, seenCollections);
|
|
18657
18658
|
}
|
|
@@ -24779,7 +24780,7 @@ function resolveMemberDeclaredTypeInfoInternal(member, members, seen) {
|
|
|
24779
24780
|
members,
|
|
24780
24781
|
nextSeen
|
|
24781
24782
|
);
|
|
24782
|
-
return argumentType === null ? [] : [[genericParamId,
|
|
24783
|
+
return argumentType === null ? [] : [[genericParamId, argumentType]];
|
|
24783
24784
|
}
|
|
24784
24785
|
)
|
|
24785
24786
|
);
|
|
@@ -35277,7 +35278,12 @@ var StrictNeoScriptResolver = class {
|
|
|
35277
35278
|
};
|
|
35278
35279
|
});
|
|
35279
35280
|
fields.push(
|
|
35280
|
-
...this.resolveCallSiteInitializerFields(
|
|
35281
|
+
...this.resolveCallSiteInitializerFields(
|
|
35282
|
+
schemaClass,
|
|
35283
|
+
targetType,
|
|
35284
|
+
expression,
|
|
35285
|
+
scope
|
|
35286
|
+
)
|
|
35281
35287
|
);
|
|
35282
35288
|
const schemaClassInfo = toWireType(targetType, this.project);
|
|
35283
35289
|
if (schemaClassInfo.type !== 7 /* Class */) {
|
|
@@ -35403,6 +35409,7 @@ var StrictNeoScriptResolver = class {
|
|
|
35403
35409
|
) : [];
|
|
35404
35410
|
const fields = this.resolveCallSiteInitializerFields(
|
|
35405
35411
|
schemaClass,
|
|
35412
|
+
targetType,
|
|
35406
35413
|
expression,
|
|
35407
35414
|
scope
|
|
35408
35415
|
);
|
|
@@ -35517,7 +35524,7 @@ var StrictNeoScriptResolver = class {
|
|
|
35517
35524
|
* the class's own schema exactly as a `classConstructor` field is, and
|
|
35518
35525
|
* applied after the body so an explicit assignment always wins.
|
|
35519
35526
|
*/
|
|
35520
|
-
resolveCallSiteInitializerFields(schemaClass, expression, scope) {
|
|
35527
|
+
resolveCallSiteInitializerFields(schemaClass, targetType, expression, scope) {
|
|
35521
35528
|
const entries = expression.initializer ?? [];
|
|
35522
35529
|
const seen = /* @__PURE__ */ new Set();
|
|
35523
35530
|
return entries.map((entry) => {
|
|
@@ -35562,10 +35569,15 @@ var StrictNeoScriptResolver = class {
|
|
|
35562
35569
|
entry.pos
|
|
35563
35570
|
);
|
|
35564
35571
|
}
|
|
35565
|
-
const
|
|
35566
|
-
|
|
35572
|
+
const memberType = substituteTypeParameters(
|
|
35573
|
+
member.type,
|
|
35574
|
+
targetType,
|
|
35575
|
+
schemaClass
|
|
35576
|
+
);
|
|
35577
|
+
const value = this.resolveExpression(entry.value, scope, memberType);
|
|
35578
|
+
if (!isNeoScriptTypeAssignable(value.type, memberType, this.project)) {
|
|
35567
35579
|
throw new CompileError(
|
|
35568
|
-
`Object initializer '${member.name}' expected ${this.describe(
|
|
35580
|
+
`Object initializer '${member.name}' expected ${this.describe(memberType)} but got ${this.describe(value.type)}.`,
|
|
35569
35581
|
entry.pos
|
|
35570
35582
|
);
|
|
35571
35583
|
}
|
package/package.json
CHANGED
|
@@ -388,6 +388,37 @@ frame after a watched dependency changes.
|
|
|
388
388
|
When multiple tracks write the same member in one frame, apply list order and
|
|
389
389
|
let the last write win.
|
|
390
390
|
|
|
391
|
+
A track can target a Class member. Each frame writes its whole value, and a
|
|
392
|
+
`null` frame clears the member. Every `NeoObjectBase`, sprites included,
|
|
393
|
+
declares `Collider`, so a per-frame trigger such as an attack hit box is a
|
|
394
|
+
Session `Collider` override on the child plus a track of `NeoCollider?`
|
|
395
|
+
frames:
|
|
396
|
+
|
|
397
|
+
```neo
|
|
398
|
+
class ColliderAnimationSegment : NeoAnimationSegment<NeoCollider?> {
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
@settings(target: NeoObjectBase.Collider)
|
|
402
|
+
class SwingColliderTrack(NeoDelegate<SwordSprite> selector)
|
|
403
|
+
: NeoAnimationSegmentTrack<SwordSprite, NeoCollider?>(selector: selector) {
|
|
404
|
+
public override ColliderAnimationSegment Segment = new ColliderAnimationSegment() {
|
|
405
|
+
Duration = 3,
|
|
406
|
+
Frames = [
|
|
407
|
+
new NeoAnimationSegmentFrame<NeoCollider?>(value: new NeoCollider() {
|
|
408
|
+
Size = new(1, 1),
|
|
409
|
+
Offset = new(1, 1),
|
|
410
|
+
IsTrigger = true
|
|
411
|
+
}) { Index = 1 },
|
|
412
|
+
new NeoAnimationSegmentFrame<NeoCollider?>(value: null) { Index = 2 }
|
|
413
|
+
]
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
The segment's class must fit the target member's class. Like any track target,
|
|
419
|
+
the member must be a writable Save, Session, or Writable leaf on the child, for
|
|
420
|
+
example `@storage(allowed: .Session) public override NeoCollider? Collider;`.
|
|
421
|
+
|
|
391
422
|
## NeoFlow dialogues
|
|
392
423
|
|
|
393
424
|
Lookup dialogue groups use the same collection paths as lookup members:
|
|
@@ -110,7 +110,7 @@ wrappers.
|
|
|
110
110
|
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
111
111
|
|
|
112
112
|
```html
|
|
113
|
-
<!-- reviewed-through-cli: 0.
|
|
113
|
+
<!-- reviewed-through-cli: 0.65.0 -->
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
The quoted version above is checked too, so this instruction cannot go stale
|