castle-web-cli 0.4.101 → 0.4.103
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/dist/agent-failures.d.ts +2 -1
- package/dist/agent-failures.js +9 -0
- package/dist/agent.js +155 -102
- package/dist/anthropic-key-helper.d.ts +1 -0
- package/dist/anthropic-key-helper.js +7 -0
- package/dist/byo-auth.d.ts +23 -0
- package/dist/byo-auth.js +130 -0
- package/dist/ide.js +18 -2
- package/dist/metering.d.ts +18 -0
- package/dist/metering.js +42 -0
- package/dist/native/openrouter.d.ts +3 -0
- package/dist/native/openrouter.js +63 -2
- package/dist/shell/assets/{index-CepwemNv.js → index-BNvlZx4-.js} +38 -38
- package/dist/shell/assets/{index-BfN3y__V.css → index-DIcWN-RS.css} +1 -1
- package/dist/shell/index.html +2 -2
- package/dist/vitePlugins.js +30 -2
- package/kits/basic-2d/castle.json +26 -1
- package/kits/physics-2d/behaviors/Collider.jsx +21 -46
- package/kits/physics-2d/castle.json +27 -2
- package/kits/physics-2d/engine/blueprint.js +16 -1
- package/package.json +1 -1
|
@@ -29,6 +29,9 @@ const linkBtn = {
|
|
|
29
29
|
const deleteBtn = { ...linkBtn, color: '#ff7a7a' };
|
|
30
30
|
const actionRow = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', margin: '4px 0 12px' };
|
|
31
31
|
const noteStyle = { fontSize: 12, color: 'var(--castle-inspector-muted, #888)', margin: '2px 0 10px' };
|
|
32
|
+
const resetRow = { display: 'flex', justifyContent: 'space-between', alignItems: 'center', margin: '2px 0 10px' };
|
|
33
|
+
const overrideNote = { fontSize: 12, color: '#b58dff' };
|
|
34
|
+
const resetLink = { ...linkBtn, color: '#b58dff' };
|
|
32
35
|
|
|
33
36
|
// --- shape helpers ---------------------------------------------------------
|
|
34
37
|
const FULL_BOX = { type: 'box', x: 0.5, y: 0.5, w: 1, h: 1 };
|
|
@@ -177,21 +180,11 @@ export class Collider {
|
|
|
177
180
|
const offXPx = Math.round(((shape.x ?? 0.5) - 0.5) * bw);
|
|
178
181
|
const offYPx = Math.round(((shape.y ?? 0.5) - 0.5) * bh);
|
|
179
182
|
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
const
|
|
185
|
-
const bShape = Array.isArray(bpShapes) ? bpShapes[i] : null;
|
|
186
|
-
const shapeOv = (key, dflt, toPx) => {
|
|
187
|
-
if (!bShape || bShape.type !== shape.type) return {};
|
|
188
|
-
const bv = bShape[key] ?? dflt;
|
|
189
|
-
return {
|
|
190
|
-
overridden: Math.abs((shape[key] ?? dflt) - bv) > 1e-4,
|
|
191
|
-
defaultValue: toPx(bv),
|
|
192
|
-
onReset: () => patchSel({ [key]: bv }),
|
|
193
|
-
};
|
|
194
|
-
};
|
|
183
|
+
// The shapes list is one array prop, so it's overridden as a whole (not per
|
|
184
|
+
// field). The Panel title already tints when the instance diverges (via
|
|
185
|
+
// override.anyOverridden); this drives a single "reset the whole shape set
|
|
186
|
+
// back to the blueprint" affordance -- coarse on purpose, to stay simple.
|
|
187
|
+
const shapesOverridden = Boolean(override?.isOverridden?.('shapes'));
|
|
195
188
|
|
|
196
189
|
const density = component.density > 0 ? component.density : 0.001;
|
|
197
190
|
|
|
@@ -217,6 +210,14 @@ export class Collider {
|
|
|
217
210
|
+
|
|
218
211
|
</button>
|
|
219
212
|
</div>
|
|
213
|
+
{shapesOverridden ? (
|
|
214
|
+
<div style={resetRow}>
|
|
215
|
+
<span style={overrideNote}>Shapes overridden</span>
|
|
216
|
+
<button type="button" style={resetLink} onClick={() => override.reset('shapes')}>
|
|
217
|
+
Reset to blueprint
|
|
218
|
+
</button>
|
|
219
|
+
</div>
|
|
220
|
+
) : null}
|
|
220
221
|
{addOpen ? (
|
|
221
222
|
<div style={addMenu}>
|
|
222
223
|
{/* triangle/polygon are supported by the data model + runtime but not
|
|
@@ -238,50 +239,24 @@ export class Collider {
|
|
|
238
239
|
<>
|
|
239
240
|
<SelectField label="Shape" value={shape.type} onChange={setType} options={['box', 'circle']} />
|
|
240
241
|
{isCircle ? (
|
|
241
|
-
<NumberField
|
|
242
|
-
label="Radius"
|
|
243
|
-
value={radiusPx}
|
|
244
|
-
onChange={(v) => patchSel({ r: v / Math.min(bw, bh) })}
|
|
245
|
-
{...shapeOv('r', 0.5, (f) => Math.round(f * Math.min(bw, bh)))}
|
|
246
|
-
/>
|
|
242
|
+
<NumberField label="Radius" value={radiusPx} onChange={(v) => patchSel({ r: v / Math.min(bw, bh) })} />
|
|
247
243
|
) : (
|
|
248
244
|
<>
|
|
249
|
-
<NumberField
|
|
250
|
-
|
|
251
|
-
value={widthPx}
|
|
252
|
-
onChange={(v) => patchSel({ w: v / bw })}
|
|
253
|
-
{...shapeOv('w', 1, (f) => Math.round(f * bw))}
|
|
254
|
-
/>
|
|
255
|
-
<NumberField
|
|
256
|
-
label="Height"
|
|
257
|
-
value={heightPx}
|
|
258
|
-
onChange={(v) => patchSel({ h: v / bh })}
|
|
259
|
-
{...shapeOv('h', 1, (f) => Math.round(f * bh))}
|
|
260
|
-
/>
|
|
245
|
+
<NumberField label="Width" value={widthPx} onChange={(v) => patchSel({ w: v / bw })} />
|
|
246
|
+
<NumberField label="Height" value={heightPx} onChange={(v) => patchSel({ h: v / bh })} />
|
|
261
247
|
<NumberField
|
|
262
248
|
label="Angle"
|
|
263
249
|
value={Math.round(shape.angle ?? 0)}
|
|
264
250
|
min={-180}
|
|
265
251
|
max={180}
|
|
266
252
|
onChange={(v) => patchSel({ angle: v })}
|
|
267
|
-
{...shapeOv('angle', 0, (f) => Math.round(f))}
|
|
268
253
|
/>
|
|
269
254
|
</>
|
|
270
255
|
)}
|
|
271
256
|
</>
|
|
272
257
|
)}
|
|
273
|
-
<NumberField
|
|
274
|
-
|
|
275
|
-
value={offXPx}
|
|
276
|
-
onChange={(v) => patchSel({ x: 0.5 + v / bw })}
|
|
277
|
-
{...shapeOv('x', 0.5, (f) => Math.round((f - 0.5) * bw))}
|
|
278
|
-
/>
|
|
279
|
-
<NumberField
|
|
280
|
-
label="Offset Y"
|
|
281
|
-
value={offYPx}
|
|
282
|
-
onChange={(v) => patchSel({ y: 0.5 + v / bh })}
|
|
283
|
-
{...shapeOv('y', 0.5, (f) => Math.round((f - 0.5) * bh))}
|
|
284
|
-
/>
|
|
258
|
+
<NumberField label="Offset X" value={offXPx} onChange={(v) => patchSel({ x: 0.5 + v / bw })} />
|
|
259
|
+
<NumberField label="Offset Y" value={offYPx} onChange={(v) => patchSel({ y: 0.5 + v / bh })} />
|
|
285
260
|
<div style={actionRow}>
|
|
286
261
|
{autoFit && !fitted ? (
|
|
287
262
|
<button type="button" onClick={() => writeShapes(shapes.map((s, k) => (k === i ? autoFit : s)))} style={linkBtn}>
|
|
@@ -23,10 +23,35 @@
|
|
|
23
23
|
"scenes/**",
|
|
24
24
|
"blueprints/**",
|
|
25
25
|
"behaviors/**"
|
|
26
|
-
]
|
|
26
|
+
],
|
|
27
|
+
"fileTypes": [
|
|
28
|
+
{
|
|
29
|
+
"ext": ".scene",
|
|
30
|
+
"label": "Scene",
|
|
31
|
+
"new": "New scene",
|
|
32
|
+
"icon": "globe",
|
|
33
|
+
"editor": "kit",
|
|
34
|
+
"data": true
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"ext": ".pxart",
|
|
38
|
+
"label": "Pixel Art",
|
|
39
|
+
"new": "New drawing",
|
|
40
|
+
"icon": "layer-group",
|
|
41
|
+
"editor": "kit",
|
|
42
|
+
"data": true
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"ext": ".jsx",
|
|
46
|
+
"label": "Behavior",
|
|
47
|
+
"new": "New behavior",
|
|
48
|
+
"icon": "code"
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"defaultPlayFile": "scenes/main.scene"
|
|
27
52
|
},
|
|
28
53
|
"deckId": "ckRZGFW4iPrx",
|
|
29
54
|
"cardId": "_oBFbAW6DxsO",
|
|
30
55
|
"title": "physics-2d",
|
|
31
|
-
"publishedVersion": "2026-07-
|
|
56
|
+
"publishedVersion": "2026-07-28T22:34:22.244Z"
|
|
32
57
|
}
|
|
@@ -300,6 +300,18 @@ function findBehavior(behaviors, name) {
|
|
|
300
300
|
return behaviors.find((candidate) => candidate.behaviorName === name);
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
// Structural equality for plain JSON prop values (scalars, arrays, plain
|
|
304
|
+
// objects) -- used to tell whether an instance override has become identical to
|
|
305
|
+
// its blueprint baseline, so it can be dropped back to inheriting.
|
|
306
|
+
function deepEqual(a, b) {
|
|
307
|
+
if (Object.is(a, b)) return true;
|
|
308
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
|
|
309
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
310
|
+
const keys = Object.keys(a);
|
|
311
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
312
|
+
return keys.every((k) => deepEqual(a[k], b[k]));
|
|
313
|
+
}
|
|
314
|
+
|
|
303
315
|
// Apply an inspector-style partial patch (`{ propName: value, ... }`) to one
|
|
304
316
|
// actor's component, IN PLACE. For a blueprint's own template actor (no
|
|
305
317
|
// `blueprint` ref) this is a plain merge -- there's no baseline to diff
|
|
@@ -321,7 +333,10 @@ export function applyComponentPatch(files, behaviors, actor, behaviorName, nextP
|
|
|
321
333
|
const overrides = { ...(actor.components[behaviorName] ?? {}) };
|
|
322
334
|
for (const [prop, value] of Object.entries(nextProps)) {
|
|
323
335
|
const baseline = prop in templateProps ? templateProps[prop] : defaultProps[prop];
|
|
324
|
-
|
|
336
|
+
// Deep-equal, not Object.is: object/array props (e.g. Collider `shapes`,
|
|
337
|
+
// Joints `list`) reset to the blueprint value are a fresh reference with
|
|
338
|
+
// identical content, so a reference check would never drop the override.
|
|
339
|
+
if (isInheritedProp(Behavior, prop) && deepEqual(value, baseline)) delete overrides[prop];
|
|
325
340
|
else overrides[prop] = value;
|
|
326
341
|
}
|
|
327
342
|
if (Object.keys(overrides).length === 0) delete actor.components[behaviorName];
|