castle-web-cli 0.4.88 → 0.4.90
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/get-deck.js +4 -0
- package/dist/init.d.ts +7 -0
- package/dist/init.js +1 -1
- package/dist/normalize.d.ts +1 -0
- package/dist/normalize.js +66 -0
- package/dist/pull.js +4 -0
- package/kits/basic-2d/editors/BlueprintLibrary.jsx +18 -1
- package/kits/physics-2d/editors/BlueprintLibrary.jsx +18 -1
- package/package.json +1 -1
package/dist/get-deck.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as path from 'path';
|
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
5
|
import { nanoid } from 'nanoid';
|
|
6
6
|
import * as api from './api.js';
|
|
7
|
+
import { normalizeDeckPackageJson } from './normalize.js';
|
|
7
8
|
function readCastleJson(dir) {
|
|
8
9
|
const p = path.join(dir, 'castle.json');
|
|
9
10
|
if (!fs.existsSync(p))
|
|
@@ -60,5 +61,8 @@ export async function getDeck(dir, options = {}) {
|
|
|
60
61
|
}
|
|
61
62
|
catch { /* nothing to clean */ }
|
|
62
63
|
}
|
|
64
|
+
if (normalizeDeckPackageJson(targetDir)) {
|
|
65
|
+
console.log(`Repointed package.json at this machine's sdk/cli`);
|
|
66
|
+
}
|
|
63
67
|
console.log(`Extracted to ${targetDir}`);
|
|
64
68
|
}
|
package/dist/init.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export declare function resolveScaffoldRefs(): {
|
|
2
|
+
workspaceMode: boolean;
|
|
3
|
+
sdkRef: string;
|
|
4
|
+
cliCommand: string;
|
|
5
|
+
cliDistAbs: string | null;
|
|
6
|
+
sdkPathPosix: string | null;
|
|
7
|
+
};
|
|
1
8
|
export declare function init(dir: string, opts?: {
|
|
2
9
|
kit?: string;
|
|
3
10
|
serve?: boolean;
|
package/dist/init.js
CHANGED
|
@@ -88,7 +88,7 @@ function stripCastleJsonIdentity(projectDir) {
|
|
|
88
88
|
// sdk -> file: absolute path to sdk/, scripts -> `node <abs cli dist>/index.js`
|
|
89
89
|
// published mode (globally-installed castle-web, no sibling sdk/):
|
|
90
90
|
// sdk -> registry `^${PUBLISHED_SDK_VERSION}`, scripts -> the `castle-web` binary
|
|
91
|
-
function resolveScaffoldRefs() {
|
|
91
|
+
export function resolveScaffoldRefs() {
|
|
92
92
|
const sdkPath = getSdkPackagePath();
|
|
93
93
|
// "Workspace mode" = the cli is running from a castle-experimental-web
|
|
94
94
|
// checkout (sdk/ exists next to cli/). Otherwise we're a globally-installed
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function normalizeDeckPackageJson(projectDir: string): boolean;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { resolveScaffoldRefs } from './init.js';
|
|
4
|
+
// `<path>/cli/dist/<entry>.js` -- a path into a castle-experimental-web checkout,
|
|
5
|
+
// as `init` writes into a deck scaffolded in workspace mode.
|
|
6
|
+
//
|
|
7
|
+
// The lookbehind stops a match from starting mid-token, which is what makes this
|
|
8
|
+
// idempotent: without it, the published `castle-web-cli/dist/bundle.js` specifier
|
|
9
|
+
// this very function writes contains a literal `cli/dist/bundle.js`, so a second
|
|
10
|
+
// pass would rewrite that tail again into `castle-web-castle-web-cli/dist/...`.
|
|
11
|
+
function cliPathPattern(lead, entry) {
|
|
12
|
+
return new RegExp(`(?<![^\\s'"(])${lead}(?:[^\\s'"]+/)?cli/dist/${entry}\\.js`, 'g');
|
|
13
|
+
}
|
|
14
|
+
function resolvesHere(projectDir, ref) {
|
|
15
|
+
return fs.existsSync(path.resolve(projectDir, ref));
|
|
16
|
+
}
|
|
17
|
+
// Point a deck's `castle-web-sdk` dependency and its script commands at whatever
|
|
18
|
+
// this machine actually has. `save-deck` archives the deck's package.json verbatim,
|
|
19
|
+
// so a deck saved from a castle-experimental-web checkout carries that checkout's
|
|
20
|
+
// absolute paths (`file:/Users/.../sdk`, `node /Users/.../cli/dist/index.js`);
|
|
21
|
+
// fetched anywhere else -- a cloud sandbox, another laptop -- those paths don't
|
|
22
|
+
// exist and the install fails outright.
|
|
23
|
+
//
|
|
24
|
+
// Only refs that DON'T resolve here are rewritten, so fetching your own deck back
|
|
25
|
+
// into the checkout it came from leaves its local wiring alone.
|
|
26
|
+
export function normalizeDeckPackageJson(projectDir) {
|
|
27
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
28
|
+
if (!fs.existsSync(pkgPath))
|
|
29
|
+
return false;
|
|
30
|
+
let pkg;
|
|
31
|
+
try {
|
|
32
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false; // unparseable package.json -- leave it for the user to fix
|
|
36
|
+
}
|
|
37
|
+
const refs = resolveScaffoldRefs();
|
|
38
|
+
let changed = false;
|
|
39
|
+
const sdk = pkg.dependencies?.['castle-web-sdk'];
|
|
40
|
+
if (typeof sdk === 'string' &&
|
|
41
|
+
sdk.startsWith('file:') &&
|
|
42
|
+
!resolvesHere(projectDir, sdk.slice('file:'.length))) {
|
|
43
|
+
pkg.dependencies['castle-web-sdk'] = refs.sdkRef;
|
|
44
|
+
changed = true;
|
|
45
|
+
}
|
|
46
|
+
const bundleRef = refs.workspaceMode
|
|
47
|
+
? `${refs.cliDistAbs}/bundle.js`
|
|
48
|
+
: 'castle-web-cli/dist/bundle.js';
|
|
49
|
+
for (const [name, command] of Object.entries(pkg.scripts ?? {})) {
|
|
50
|
+
if (typeof command !== 'string')
|
|
51
|
+
continue;
|
|
52
|
+
const rewritten = command
|
|
53
|
+
// The whole `node <path>` goes, not just the path: published mode runs the
|
|
54
|
+
// `castle-web` binary directly, with no `node` in front of it.
|
|
55
|
+
.replace(cliPathPattern('node\\s+', 'index'), (match) => resolvesHere(projectDir, match.replace(/^node\s+/, '')) ? match : refs.cliCommand)
|
|
56
|
+
.replace(cliPathPattern('', 'bundle'), (match) => resolvesHere(projectDir, match) ? match : bundleRef);
|
|
57
|
+
if (rewritten !== command) {
|
|
58
|
+
pkg.scripts[name] = rewritten;
|
|
59
|
+
changed = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (changed) {
|
|
63
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
return changed;
|
|
66
|
+
}
|
package/dist/pull.js
CHANGED
|
@@ -5,6 +5,7 @@ import { nanoid } from 'nanoid';
|
|
|
5
5
|
import * as api from './api.js';
|
|
6
6
|
import { extractTarball } from './get-deck.js';
|
|
7
7
|
import { installDeps } from './install.js';
|
|
8
|
+
import { normalizeDeckPackageJson } from './normalize.js';
|
|
8
9
|
import { archiveSource } from './save-deck.js';
|
|
9
10
|
// Everything else in the deck dir is source and gets replaced wholesale, so that a
|
|
10
11
|
// file deleted upstream actually disappears here -- untarring over the old tree
|
|
@@ -109,6 +110,9 @@ export async function pull(dir, options = {}) {
|
|
|
109
110
|
/* nothing to clean */
|
|
110
111
|
}
|
|
111
112
|
}
|
|
113
|
+
if (normalizeDeckPackageJson(projectDir)) {
|
|
114
|
+
console.log(`Repointed package.json at this machine's sdk/cli`);
|
|
115
|
+
}
|
|
112
116
|
console.log(`Updated ${projectDir}`);
|
|
113
117
|
// The pulled tree can want different deps than the one just deleted.
|
|
114
118
|
installDeps(projectDir, fs.existsSync(path.join(projectDir, 'pnpm-lock.yaml')));
|
|
@@ -25,11 +25,28 @@ export function BlueprintLibrary({
|
|
|
25
25
|
}) {
|
|
26
26
|
const blueprints = listBlueprints(files);
|
|
27
27
|
const dragRef = useRef(null);
|
|
28
|
+
const hotbarRef = useRef(null);
|
|
28
29
|
const suppressClickRef = useRef(false);
|
|
29
30
|
const [dragPreview, setDragPreview] = useState(null);
|
|
30
31
|
const [contextMenu, setContextMenu] = useState(null);
|
|
31
32
|
const [confirmDelete, setConfirmDelete] = useState(null);
|
|
32
33
|
|
|
34
|
+
// Map a vertical mouse wheel gesture onto horizontal scroll so plain-mouse
|
|
35
|
+
// users (no trackpad/touch) can pan the hotbar. Bound natively rather than via
|
|
36
|
+
// React's onWheel so the listener is non-passive and can preventDefault.
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const bar = hotbarRef.current;
|
|
39
|
+
if (!bar) return undefined;
|
|
40
|
+
const onWheel = (event) => {
|
|
41
|
+
if (event.deltaY !== 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
|
|
42
|
+
bar.scrollLeft += event.deltaY;
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
bar.addEventListener('wheel', onWheel, { passive: false });
|
|
47
|
+
return () => bar.removeEventListener('wheel', onWheel);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
33
50
|
const onSlotContextMenu = (event, blueprint) => {
|
|
34
51
|
if (isPlaying) return;
|
|
35
52
|
event.preventDefault();
|
|
@@ -99,7 +116,7 @@ export function BlueprintLibrary({
|
|
|
99
116
|
};
|
|
100
117
|
|
|
101
118
|
return (
|
|
102
|
-
<div className={styles.bpHotbar} aria-label="Blueprints">
|
|
119
|
+
<div ref={hotbarRef} className={styles.bpHotbar} aria-label="Blueprints">
|
|
103
120
|
<button
|
|
104
121
|
type="button"
|
|
105
122
|
className={styles.bpActionSlot}
|
|
@@ -25,11 +25,28 @@ export function BlueprintLibrary({
|
|
|
25
25
|
}) {
|
|
26
26
|
const blueprints = listBlueprints(files);
|
|
27
27
|
const dragRef = useRef(null);
|
|
28
|
+
const hotbarRef = useRef(null);
|
|
28
29
|
const suppressClickRef = useRef(false);
|
|
29
30
|
const [dragPreview, setDragPreview] = useState(null);
|
|
30
31
|
const [contextMenu, setContextMenu] = useState(null);
|
|
31
32
|
const [confirmDelete, setConfirmDelete] = useState(null);
|
|
32
33
|
|
|
34
|
+
// Map a vertical mouse wheel gesture onto horizontal scroll so plain-mouse
|
|
35
|
+
// users (no trackpad/touch) can pan the hotbar. Bound natively rather than via
|
|
36
|
+
// React's onWheel so the listener is non-passive and can preventDefault.
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const bar = hotbarRef.current;
|
|
39
|
+
if (!bar) return undefined;
|
|
40
|
+
const onWheel = (event) => {
|
|
41
|
+
if (event.deltaY !== 0 && Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
|
|
42
|
+
bar.scrollLeft += event.deltaY;
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
bar.addEventListener('wheel', onWheel, { passive: false });
|
|
47
|
+
return () => bar.removeEventListener('wheel', onWheel);
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
33
50
|
const onSlotContextMenu = (event, blueprint) => {
|
|
34
51
|
if (isPlaying) return;
|
|
35
52
|
event.preventDefault();
|
|
@@ -99,7 +116,7 @@ export function BlueprintLibrary({
|
|
|
99
116
|
};
|
|
100
117
|
|
|
101
118
|
return (
|
|
102
|
-
<div className={styles.bpHotbar} aria-label="Blueprints">
|
|
119
|
+
<div ref={hotbarRef} className={styles.bpHotbar} aria-label="Blueprints">
|
|
103
120
|
<button
|
|
104
121
|
type="button"
|
|
105
122
|
className={styles.bpActionSlot}
|