castle-web-cli 0.4.78 → 0.4.80
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-prompts.d.ts +4 -1
- package/dist/agent-prompts.js +28 -7
- package/dist/agent.d.ts +7 -2
- package/dist/agent.js +655 -51
- package/dist/native/loop.d.ts +2 -0
- package/dist/native/loop.js +698 -0
- package/dist/native/openrouter.d.ts +55 -0
- package/dist/native/openrouter.js +354 -0
- package/dist/native/playtest-browser.d.ts +34 -0
- package/dist/native/playtest-browser.js +354 -0
- package/dist/native/playtest-executor.d.ts +3 -0
- package/dist/native/playtest-executor.js +156 -0
- package/dist/native/playtest.d.ts +131 -0
- package/dist/native/playtest.js +314 -0
- package/dist/native/tools.d.ts +38 -0
- package/dist/native/tools.js +690 -0
- package/dist/native/types.d.ts +40 -0
- package/dist/native/types.js +41 -0
- package/dist/serve.js +12 -0
- package/dist/shell/assets/{index-yGdKhgfZ.js → index-D3unT7do.js} +37 -37
- package/dist/shell/assets/{index-WE24qX3d.css → index-RZrw5gQ2.css} +1 -1
- package/dist/shell/index.html +2 -2
- package/kits/basic-2d/CLAUDE.md +29 -3
- package/kits/basic-2d/behaviors/Layout.jsx +10 -0
- package/kits/basic-2d/behaviors/Sprite.jsx +1 -1
- package/kits/basic-2d/blueprints/cauldron.scene +22 -0
- package/kits/basic-2d/castle.json +5 -7
- package/kits/basic-2d/docs/pxart-format.md +4 -3
- package/kits/basic-2d/drawings/cauldron.pxart +113 -0
- package/kits/basic-2d/editors/BlueprintLibrary.jsx +247 -0
- package/kits/basic-2d/editors/PlayOnly.jsx +1 -0
- package/kits/basic-2d/editors/SceneEditor.jsx +399 -411
- package/kits/basic-2d/editors/SelectionOverlay.jsx +125 -63
- package/kits/basic-2d/editors/SingleEditor.jsx +11 -2
- package/kits/basic-2d/editors/editorHistory.js +8 -2
- package/kits/basic-2d/editors/inspectorSheet.js +5 -19
- package/kits/basic-2d/engine/ScenePlayer.jsx +2 -2
- package/kits/basic-2d/engine/blueprint.js +423 -0
- package/kits/basic-2d/engine/files.js +1 -1
- package/kits/basic-2d/engine/scene.js +29 -29
- package/kits/basic-2d/engine/ui.jsx +160 -21
- package/kits/basic-2d/engine/ui.module.css +155 -13
- package/kits/basic-2d/pnpm-workspace.yaml +3 -0
- package/kits/basic-2d/scenes/main.scene +3 -13
- package/package.json +2 -1
- package/kits/basic-2d/drawings/pig.pxart +0 -26
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
faCircle,
|
|
12
12
|
faClone,
|
|
13
13
|
faCode,
|
|
14
|
+
faCodeBranch,
|
|
14
15
|
faEraser,
|
|
15
16
|
faEyeDropper,
|
|
16
17
|
faFile,
|
|
@@ -115,9 +116,26 @@ export function EditorHeader({ title, subtitle, right, onToggleFiles, filesOpen,
|
|
|
115
116
|
if (!headerHost) return null;
|
|
116
117
|
return createPortal(header, headerHost);
|
|
117
118
|
}
|
|
119
|
+
// Safari only moves keyboard focus into an iframe -- at the browser level,
|
|
120
|
+
// not just document.activeElement -- when the click lands on something
|
|
121
|
+
// Safari itself focuses on click (a text input; NOT a button, div, or
|
|
122
|
+
// canvas). PxArtEditor/SceneEditor each already focus their own canvas on
|
|
123
|
+
// pointer-down for exactly this reason, but that only covers clicks that
|
|
124
|
+
// land on the canvas: clicking a tool button, a palette swatch, the
|
|
125
|
+
// timeline, etc. does nothing, so Safari's frame focus can stay wherever it
|
|
126
|
+
// was and the NEXT keyboard shortcut or Cmd+Z falls through to the browser
|
|
127
|
+
// -- reproducing right after the user just clicked in the editor. Claiming
|
|
128
|
+
// focus here, once, for every pointer-down anywhere in the editor body
|
|
129
|
+
// closes that gap for both editors without duplicating the fix. Real text
|
|
130
|
+
// inputs still win focus normally right after: the browser's own
|
|
131
|
+
// click-focuses-inputs behavior runs as mousedown's default action, which
|
|
132
|
+
// fires AFTER this pointerdown handler.
|
|
133
|
+
function claimEditorFocus(event) {
|
|
134
|
+
event.currentTarget.focus({ preventScroll: true });
|
|
135
|
+
}
|
|
118
136
|
export const EditorBody = React.forwardRef(function EditorBody({ children }, ref) {
|
|
119
137
|
return (
|
|
120
|
-
<div ref={ref} className={styles.editorBody}>
|
|
138
|
+
<div ref={ref} className={styles.editorBody} tabIndex={-1} onPointerDown={claimEditorFocus}>
|
|
121
139
|
{children}
|
|
122
140
|
</div>
|
|
123
141
|
);
|
|
@@ -158,6 +176,8 @@ const icons = {
|
|
|
158
176
|
circle: faCircle,
|
|
159
177
|
clone: faClone,
|
|
160
178
|
code: faCode,
|
|
179
|
+
// FA5 has no faCodeFork (that's the FA6 name); faCodeBranch is the fork glyph.
|
|
180
|
+
'code-fork': faCodeBranch,
|
|
161
181
|
eraser: faEraser,
|
|
162
182
|
eyedropper: faEyeDropper,
|
|
163
183
|
fill: faFillDrip,
|
|
@@ -361,33 +381,125 @@ export function isHexColor(value) {
|
|
|
361
381
|
typeof value === 'string' && /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3}([0-9a-fA-F]{2})?)?$/.test(value)
|
|
362
382
|
);
|
|
363
383
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
384
|
+
// --- Bottom-sheet sizing (mobile only) -------------------------------------
|
|
385
|
+
// The inspector / file sheets are docked panels on desktop; below the CSS
|
|
386
|
+
// breakpoint they become drag-resizable bottom sheets. `useMobileSheet` owns a
|
|
387
|
+
// continuous pixel height so the sheet tracks the finger 1:1 and can rest at
|
|
388
|
+
// any height (the old version only read the drag delta at release and jumped
|
|
389
|
+
// between two fixed snaps, which felt dead mid-drag).
|
|
390
|
+
const SHEET_PEEK_FRACTION = 0.18; // collapsed "peek" height, fraction of viewport
|
|
391
|
+
const SHEET_DEFAULT_FRACTION = 0.62; // expanded default height
|
|
392
|
+
const SHEET_MAX_FRACTION = 0.92; // never quite cover the whole screen
|
|
393
|
+
const SHEET_MIN_PX = 96; // floor so the grab handle always fits
|
|
394
|
+
const SHEET_TAP_PX = 6; // release movement under this reads as a tap, not a drag
|
|
395
|
+
const SHEET_FLICK_VELOCITY = 0.6; // px/ms; a faster release flings to an end
|
|
396
|
+
function sheetViewportHeight() {
|
|
397
|
+
return typeof window !== 'undefined' ? window.innerHeight : 800;
|
|
398
|
+
}
|
|
399
|
+
function sheetSizes() {
|
|
400
|
+
const vh = sheetViewportHeight();
|
|
401
|
+
const peek = Math.max(SHEET_MIN_PX, vh * SHEET_PEEK_FRACTION);
|
|
402
|
+
const max = vh * SHEET_MAX_FRACTION;
|
|
403
|
+
const expanded = Math.min(max, Math.max(peek, vh * SHEET_DEFAULT_FRACTION));
|
|
404
|
+
return { peek, expanded, max };
|
|
405
|
+
}
|
|
406
|
+
function clampSheetHeight(px) {
|
|
407
|
+
const { peek, max } = sheetSizes();
|
|
408
|
+
return Math.min(max, Math.max(peek, px));
|
|
409
|
+
}
|
|
410
|
+
function releasePointer(event, pointerId) {
|
|
411
|
+
try {
|
|
412
|
+
event.currentTarget.releasePointerCapture(pointerId);
|
|
413
|
+
} catch {
|
|
414
|
+
// Capture may already be gone after a cancel / blur.
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Decide where the sheet rests when the finger lifts: a stationary press toggles
|
|
418
|
+
// between the peek and the last expanded height; a fast flick flings to the
|
|
419
|
+
// nearest end; otherwise it stays exactly where it was dragged (free resize).
|
|
420
|
+
function settleSheet(drag, setHeight, expandedRef) {
|
|
421
|
+
const { peek, expanded, max } = sheetSizes();
|
|
422
|
+
if (drag.moved < SHEET_TAP_PX) {
|
|
423
|
+
const wasCollapsed = drag.startHeight <= peek + 2;
|
|
424
|
+
if (!wasCollapsed) expandedRef.current = drag.startHeight;
|
|
425
|
+
setHeight(wasCollapsed ? expandedRef.current || expanded : peek);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (drag.velocity > SHEET_FLICK_VELOCITY) {
|
|
429
|
+
setHeight(peek);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (drag.velocity < -SHEET_FLICK_VELOCITY) {
|
|
433
|
+
expandedRef.current = max;
|
|
434
|
+
setHeight(max);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const settled = setHeight(drag.startHeight - (drag.lastY - drag.startY));
|
|
438
|
+
if (settled > peek + 2) expandedRef.current = settled;
|
|
439
|
+
}
|
|
440
|
+
export function useMobileSheet({ open = true, baseClassName }) {
|
|
441
|
+
const [height, setHeightState] = useState(() => sheetSizes().expanded);
|
|
442
|
+
const [dragging, setDragging] = useState(false);
|
|
443
|
+
const heightRef = useRef(height);
|
|
444
|
+
const expandedRef = useRef(height); // last height above the peek, for tap-restore
|
|
445
|
+
const dragRef = useRef(null);
|
|
446
|
+
const setHeight = (px) => {
|
|
447
|
+
const next = clampSheetHeight(px);
|
|
448
|
+
heightRef.current = next;
|
|
449
|
+
setHeightState(next);
|
|
450
|
+
return next;
|
|
451
|
+
};
|
|
452
|
+
// Restore the last expanded height whenever the sheet (re)opens, and keep the
|
|
453
|
+
// height in range across viewport resize / rotation.
|
|
454
|
+
useEffect(() => {
|
|
455
|
+
if (open) setHeight(expandedRef.current);
|
|
456
|
+
}, [open]);
|
|
457
|
+
useEffect(() => {
|
|
458
|
+
const onResize = () => setHeight(heightRef.current);
|
|
459
|
+
window.addEventListener('resize', onResize);
|
|
460
|
+
return () => window.removeEventListener('resize', onResize);
|
|
461
|
+
}, []);
|
|
368
462
|
function onPointerDown(event) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
463
|
+
dragRef.current = {
|
|
464
|
+
pointerId: event.pointerId,
|
|
465
|
+
startY: event.clientY,
|
|
466
|
+
startHeight: heightRef.current,
|
|
467
|
+
lastY: event.clientY,
|
|
468
|
+
lastT: event.timeStamp,
|
|
469
|
+
velocity: 0,
|
|
470
|
+
moved: 0,
|
|
471
|
+
};
|
|
472
|
+
setDragging(true);
|
|
473
|
+
try {
|
|
474
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
475
|
+
} catch {
|
|
476
|
+
// No active pointer to capture (e.g. synthetic events) -- drag still works.
|
|
477
|
+
}
|
|
373
478
|
}
|
|
374
479
|
function onPointerMove(event) {
|
|
375
|
-
|
|
376
|
-
|
|
480
|
+
const drag = dragRef.current;
|
|
481
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
482
|
+
const dy = event.clientY - drag.startY; // down is positive
|
|
483
|
+
drag.moved = Math.max(drag.moved, Math.abs(dy));
|
|
484
|
+
const dt = event.timeStamp - drag.lastT;
|
|
485
|
+
if (dt > 0) drag.velocity = (event.clientY - drag.lastY) / dt;
|
|
486
|
+
drag.lastY = event.clientY;
|
|
487
|
+
drag.lastT = event.timeStamp;
|
|
488
|
+
setHeight(drag.startHeight - dy); // dragging up grows the sheet
|
|
377
489
|
}
|
|
378
|
-
function endDrag() {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
else if (dy < -30) onTransition('up');
|
|
490
|
+
function endDrag(event) {
|
|
491
|
+
const drag = dragRef.current;
|
|
492
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
493
|
+
dragRef.current = null;
|
|
494
|
+
setDragging(false);
|
|
495
|
+
releasePointer(event, drag.pointerId);
|
|
496
|
+
settleSheet(drag, setHeight, expandedRef);
|
|
386
497
|
}
|
|
387
498
|
return {
|
|
388
499
|
rootProps: {
|
|
389
|
-
className: baseClassName,
|
|
390
|
-
'data-sheet-
|
|
500
|
+
className: cx(baseClassName, dragging && styles.sheetDragging),
|
|
501
|
+
'data-sheet-open': open ? 'true' : 'false',
|
|
502
|
+
style: { '--sheet-h': `${Math.round(height)}px` },
|
|
391
503
|
},
|
|
392
504
|
grabProps: {
|
|
393
505
|
className: cx(styles.sheetGrab, styles.mobileOnly),
|
|
@@ -466,6 +578,33 @@ export function ContextMenu({ x, y, items, onClose }) {
|
|
|
466
578
|
</div>
|
|
467
579
|
);
|
|
468
580
|
}
|
|
581
|
+
// Cursor-independent modal confirm (unlike `ContextMenu`, which anchors to a
|
|
582
|
+
// click point) -- for actions with real consequences, like the blueprint
|
|
583
|
+
// library's cascade delete. Dismisses on Escape or a backdrop click, same as
|
|
584
|
+
// cancelling.
|
|
585
|
+
export function ConfirmDialog({ title, message, confirmLabel = 'Delete', danger = true, onConfirm, onCancel }) {
|
|
586
|
+
useEffect(() => {
|
|
587
|
+
const onKeyDown = (event) => {
|
|
588
|
+
if (event.key === 'Escape') onCancel();
|
|
589
|
+
};
|
|
590
|
+
window.addEventListener('keydown', onKeyDown);
|
|
591
|
+
return () => window.removeEventListener('keydown', onKeyDown);
|
|
592
|
+
}, [onCancel]);
|
|
593
|
+
return (
|
|
594
|
+
<div className={styles.confirmBackdrop} onPointerDown={onCancel}>
|
|
595
|
+
<div className={styles.confirmDialog} role="alertdialog" onPointerDown={(event) => event.stopPropagation()}>
|
|
596
|
+
<div className={styles.confirmTitle}>{title}</div>
|
|
597
|
+
<div className={styles.confirmMessage}>{message}</div>
|
|
598
|
+
<div className={styles.confirmActions}>
|
|
599
|
+
<Button onClick={onCancel}>Cancel</Button>
|
|
600
|
+
<Button variant={danger ? 'danger' : ''} onClick={onConfirm}>
|
|
601
|
+
{confirmLabel}
|
|
602
|
+
</Button>
|
|
603
|
+
</div>
|
|
604
|
+
</div>
|
|
605
|
+
</div>
|
|
606
|
+
);
|
|
607
|
+
}
|
|
469
608
|
export function SelectField({ label, value, onChange, options }) {
|
|
470
609
|
return (
|
|
471
610
|
<FieldRow label={label}>
|
|
@@ -372,8 +372,13 @@
|
|
|
372
372
|
|
|
373
373
|
.bpSlot,
|
|
374
374
|
.bpActionSlot {
|
|
375
|
+
position: relative;
|
|
375
376
|
width: 46px;
|
|
376
377
|
height: 46px;
|
|
378
|
+
/* Never shrink in the flex hotbar -- keep the square 46x46 and let the
|
|
379
|
+
hotbar scroll (overflow-x: auto) when the panel gets narrow, instead of
|
|
380
|
+
squishing slots (the add button collapsed since its icon is only 14px). */
|
|
381
|
+
flex: 0 0 46px;
|
|
377
382
|
display: inline-flex;
|
|
378
383
|
align-items: center;
|
|
379
384
|
justify-content: center;
|
|
@@ -387,11 +392,34 @@
|
|
|
387
392
|
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
|
|
388
393
|
}
|
|
389
394
|
|
|
395
|
+
.bpSlotBadge {
|
|
396
|
+
position: absolute;
|
|
397
|
+
right: -4px;
|
|
398
|
+
bottom: -4px;
|
|
399
|
+
min-width: 15px;
|
|
400
|
+
height: 15px;
|
|
401
|
+
padding: 0 3px;
|
|
402
|
+
border-radius: 999px;
|
|
403
|
+
background: var(--castle-selected);
|
|
404
|
+
color: var(--castle-selected-ink);
|
|
405
|
+
font-size: 10px;
|
|
406
|
+
font-weight: 700;
|
|
407
|
+
line-height: 15px;
|
|
408
|
+
text-align: center;
|
|
409
|
+
}
|
|
410
|
+
|
|
390
411
|
.bpSlot:hover,
|
|
391
412
|
.bpActionSlot:hover {
|
|
392
413
|
background: var(--castle-inspector-input-bg);
|
|
393
414
|
}
|
|
394
415
|
|
|
416
|
+
.bpSlotSelected,
|
|
417
|
+
.bpSlotSelected:hover {
|
|
418
|
+
border-color: var(--castle-selected);
|
|
419
|
+
box-shadow: 0 0 0 1px var(--castle-selected);
|
|
420
|
+
background: var(--castle-inspector-input-bg);
|
|
421
|
+
}
|
|
422
|
+
|
|
395
423
|
.bpSlot:disabled,
|
|
396
424
|
.bpActionSlot:disabled {
|
|
397
425
|
cursor: default;
|
|
@@ -448,6 +476,119 @@
|
|
|
448
476
|
flex: 1 1 auto;
|
|
449
477
|
}
|
|
450
478
|
|
|
479
|
+
.confirmBackdrop {
|
|
480
|
+
position: fixed;
|
|
481
|
+
inset: 0;
|
|
482
|
+
z-index: 2000;
|
|
483
|
+
display: flex;
|
|
484
|
+
align-items: center;
|
|
485
|
+
justify-content: center;
|
|
486
|
+
background: rgba(0, 0, 0, 0.55);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.confirmDialog {
|
|
490
|
+
width: min(340px, calc(100vw - 32px));
|
|
491
|
+
padding: 18px;
|
|
492
|
+
border: 1px solid var(--castle-border);
|
|
493
|
+
border-radius: 10px;
|
|
494
|
+
background: var(--castle-panel-raised);
|
|
495
|
+
color: var(--castle-text);
|
|
496
|
+
box-shadow: 0 20px 48px rgba(0, 0, 0, 0.5);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.confirmTitle {
|
|
500
|
+
font-size: 15px;
|
|
501
|
+
font-weight: 650;
|
|
502
|
+
margin-bottom: 8px;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.confirmMessage {
|
|
506
|
+
font-size: 13px;
|
|
507
|
+
color: var(--castle-text-dim);
|
|
508
|
+
line-height: 1.45;
|
|
509
|
+
margin-bottom: 16px;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.confirmActions {
|
|
513
|
+
display: flex;
|
|
514
|
+
justify-content: flex-end;
|
|
515
|
+
gap: 8px;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.blueprintBadge {
|
|
519
|
+
display: inline-flex;
|
|
520
|
+
align-items: center;
|
|
521
|
+
padding: 2px 7px;
|
|
522
|
+
border-radius: 999px;
|
|
523
|
+
border: 1px solid var(--castle-inspector-border);
|
|
524
|
+
background: var(--castle-inspector-input-bg);
|
|
525
|
+
color: var(--castle-inspector-muted);
|
|
526
|
+
font-size: 11px;
|
|
527
|
+
font-weight: 600;
|
|
528
|
+
letter-spacing: 0.02em;
|
|
529
|
+
text-transform: uppercase;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.editorHeaderTitleRow {
|
|
533
|
+
display: inline-flex;
|
|
534
|
+
align-items: center;
|
|
535
|
+
gap: 8px;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.instanceHint {
|
|
539
|
+
display: flex;
|
|
540
|
+
align-items: center;
|
|
541
|
+
justify-content: space-between;
|
|
542
|
+
gap: 8px;
|
|
543
|
+
padding: 10px 16px;
|
|
544
|
+
border-bottom: 1px solid var(--castle-inspector-divider);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.instanceHintLabel {
|
|
548
|
+
color: var(--castle-inspector-muted);
|
|
549
|
+
font-size: 12px;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.instanceForkButton {
|
|
553
|
+
border: 1px solid var(--castle-inspector-border);
|
|
554
|
+
border-radius: 6px;
|
|
555
|
+
background: var(--castle-inspector-input-bg);
|
|
556
|
+
color: inherit;
|
|
557
|
+
font-size: 11px;
|
|
558
|
+
padding: 5px 9px;
|
|
559
|
+
cursor: pointer;
|
|
560
|
+
white-space: nowrap;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.instanceForkButton:hover {
|
|
564
|
+
background: var(--castle-inspector-border);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/* Editable blueprint title: reads as a heading until hovered/focused, where a
|
|
568
|
+
border + input background reveal that it's editable. */
|
|
569
|
+
.blueprintNameInput {
|
|
570
|
+
flex: 1;
|
|
571
|
+
min-width: 0;
|
|
572
|
+
border: 1px solid transparent;
|
|
573
|
+
border-radius: var(--castle-radius);
|
|
574
|
+
background: transparent;
|
|
575
|
+
color: var(--castle-inspector-text);
|
|
576
|
+
font-size: 14px;
|
|
577
|
+
font-weight: 600;
|
|
578
|
+
padding: 4px 6px;
|
|
579
|
+
margin: -4px -6px;
|
|
580
|
+
outline: 0;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.blueprintNameInput:hover {
|
|
584
|
+
border-color: var(--castle-inspector-border);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.blueprintNameInput:focus {
|
|
588
|
+
border-color: var(--castle-inspector-border);
|
|
589
|
+
background: var(--castle-inspector-input-bg);
|
|
590
|
+
}
|
|
591
|
+
|
|
451
592
|
.stageWrap {
|
|
452
593
|
grid-column: 1;
|
|
453
594
|
grid-row: 2;
|
|
@@ -581,9 +722,11 @@
|
|
|
581
722
|
|
|
582
723
|
.selBox {
|
|
583
724
|
position: absolute;
|
|
725
|
+
box-sizing: border-box;
|
|
584
726
|
border: 2px dashed var(--castle-text);
|
|
585
727
|
border-radius: 3px;
|
|
586
728
|
opacity: 0.85;
|
|
729
|
+
pointer-events: none;
|
|
587
730
|
}
|
|
588
731
|
|
|
589
732
|
/* Connector stem from the box's right-center edge to the rotate handle. */
|
|
@@ -1734,8 +1877,9 @@
|
|
|
1734
1877
|
|
|
1735
1878
|
@media (max-width: 600px) {
|
|
1736
1879
|
:root {
|
|
1880
|
+
/* Fallback for the file-browser sheet and the inspector's initial slide-in;
|
|
1881
|
+
the inspector's live height is driven by the hook's --sheet-h (see below). */
|
|
1737
1882
|
--sheet-high-height: 62vh;
|
|
1738
|
-
--sheet-low-height: 18vh;
|
|
1739
1883
|
}
|
|
1740
1884
|
|
|
1741
1885
|
/* `display: contents` makes wrapper spans transparent in layout so their
|
|
@@ -1883,7 +2027,7 @@
|
|
|
1883
2027
|
overflow-y: auto;
|
|
1884
2028
|
}
|
|
1885
2029
|
|
|
1886
|
-
/* Inspector sheet: hidden off-screen by default; slides up
|
|
2030
|
+
/* Inspector sheet: hidden off-screen by default; slides up when open. */
|
|
1887
2031
|
.inspector {
|
|
1888
2032
|
z-index: 45;
|
|
1889
2033
|
border-left: 0;
|
|
@@ -1891,20 +2035,18 @@
|
|
|
1891
2035
|
padding: 0;
|
|
1892
2036
|
}
|
|
1893
2037
|
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
.inspector[data-sheet-snap='low'] {
|
|
2038
|
+
/* Open inspector sheet: continuous height driven by the hook's live --sheet-h
|
|
2039
|
+
(falls back to the default expanded height). Drag the grab handle to resize
|
|
2040
|
+
freely; the hook picks the settle point on release. */
|
|
2041
|
+
.inspector[data-sheet-open='true'] {
|
|
1900
2042
|
transform: translateY(0);
|
|
1901
|
-
height: var(--sheet-
|
|
2043
|
+
height: var(--sheet-h, var(--sheet-high-height));
|
|
1902
2044
|
}
|
|
1903
2045
|
|
|
1904
|
-
/*
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
2046
|
+
/* While dragging, drop the transition so the sheet tracks the finger 1:1; the
|
|
2047
|
+
settle animation returns the moment this class is removed on release. */
|
|
2048
|
+
.inspector.sheetDragging {
|
|
2049
|
+
transition: none;
|
|
1908
2050
|
}
|
|
1909
2051
|
|
|
1910
2052
|
/* Shared sheet chrome -- grab bar pill + label row. Always visible when sheet is visible. */
|
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"background": "#
|
|
2
|
+
"background": "#1a1932",
|
|
3
3
|
"actors": [
|
|
4
4
|
{
|
|
5
5
|
"id": "pig",
|
|
6
|
+
"blueprint": "blueprints/cauldron.scene",
|
|
6
7
|
"components": {
|
|
7
8
|
"Layout": {
|
|
8
9
|
"x": 210,
|
|
9
|
-
"y": 310
|
|
10
|
-
"width": 80,
|
|
11
|
-
"height": 80,
|
|
12
|
-
"z": 1
|
|
13
|
-
},
|
|
14
|
-
"Sprite": {
|
|
15
|
-
"file": "drawings/pig.pxart"
|
|
16
|
-
},
|
|
17
|
-
"Collider": {
|
|
18
|
-
"kind": "solid",
|
|
19
|
-
"width": 70,
|
|
20
|
-
"height": 70
|
|
10
|
+
"y": 310
|
|
21
11
|
}
|
|
22
12
|
}
|
|
23
13
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "castle-web-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.80",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"castle-web": "./dist/index.js"
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"nanoid": "^5.1.7",
|
|
41
41
|
"open": "^10.0.0",
|
|
42
42
|
"picomatch": "^4.0.4",
|
|
43
|
+
"playwright-core": "^1.61.1",
|
|
43
44
|
"react": "^18.3.1",
|
|
44
45
|
"react-dom": "^18.3.1",
|
|
45
46
|
"vite": "^8.0.3",
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"format": "compact",
|
|
3
|
-
"palette": {
|
|
4
|
-
"6": "#f5555d",
|
|
5
|
-
"q": "#391f21",
|
|
6
|
-
"v": "#f6ca9f"
|
|
7
|
-
},
|
|
8
|
-
"grid": [
|
|
9
|
-
"................",
|
|
10
|
-
"................",
|
|
11
|
-
"........q..q....",
|
|
12
|
-
"....qqqqvqqvq...",
|
|
13
|
-
"...qvvvvvvvvq...",
|
|
14
|
-
"..qvvvvvvvvvvq..",
|
|
15
|
-
".qvvvvvvvqvvqvq.",
|
|
16
|
-
".qvvvvvvvqvvqvq.",
|
|
17
|
-
"q6vvvvvvvvv666q.",
|
|
18
|
-
"q6vvvvvvvv6q6q6q",
|
|
19
|
-
".qvvvvvvvv66666q",
|
|
20
|
-
".qvvvvvvvvv666q.",
|
|
21
|
-
"..qvvvvvvvvvqq..",
|
|
22
|
-
"..q66qqqq6q6q...",
|
|
23
|
-
"..q6q...q6q.....",
|
|
24
|
-
"................"
|
|
25
|
-
]
|
|
26
|
-
}
|