castle-web-cli 0.4.113 → 0.4.115
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/castleJson.d.ts +1 -0
- package/dist/editorConfig.d.ts +34 -0
- package/dist/editorConfig.js +101 -0
- package/dist/ide.js +245 -120
- package/dist/imports.d.ts +6 -0
- package/dist/imports.js +90 -7
- package/dist/init.js +15 -2
- package/dist/serve.js +16 -0
- package/dist/shell/assets/index-CUamb8rK.js +144 -0
- package/dist/shell/assets/index-Y6cJRRCX.css +1 -0
- package/dist/shell/index.html +2 -2
- package/dist/unsupportedMedia.d.ts +1 -0
- package/dist/unsupportedMedia.js +54 -0
- package/dist/vitePlugins.js +12 -19
- package/kits/basic-2d/castle.json +1 -1
- package/kits/basic-2d/editors/behaviorRegistry.js +5 -7
- package/kits/physics-2d/CLAUDE.md +42 -23
- package/kits/physics-2d/{physics/behaviors → behaviors}/AnalogStick.jsx +3 -3
- package/kits/physics-2d/behaviors/Collider.jsx +1 -1
- package/kits/physics-2d/{physics/behaviors → behaviors}/Draggable.jsx +3 -3
- package/kits/physics-2d/{physics/behaviors → behaviors}/Joints.jsx +4 -4
- package/kits/physics-2d/{physics/behaviors → behaviors}/RigidBody.jsx +2 -2
- package/kits/physics-2d/{physics/behaviors → behaviors}/Slingshot.jsx +3 -3
- package/kits/physics-2d/behaviors/Sound.jsx +152 -0
- package/kits/physics-2d/behaviors/Sprite.jsx +98 -33
- package/kits/physics-2d/behaviors/Tone.jsx +83 -0
- package/kits/physics-2d/behaviors/Video.jsx +111 -0
- package/kits/physics-2d/behaviors/tint.js +24 -9
- package/kits/physics-2d/castle.json +64 -2
- package/kits/physics-2d/editors/BlueprintLibrary.jsx +7 -3
- package/kits/physics-2d/editors/ImageViewer.jsx +206 -0
- package/kits/physics-2d/editors/MediaPlayer.jsx +57 -0
- package/kits/physics-2d/editors/SceneEditor.jsx +7 -3
- package/kits/physics-2d/editors/SingleEditor.jsx +8 -0
- package/kits/physics-2d/editors/behaviorRegistry.js +5 -7
- package/kits/physics-2d/editors/mediaFile.js +20 -0
- package/kits/physics-2d/editors/mediaViewer.module.css +128 -0
- package/kits/physics-2d/engine/ScenePlayer.jsx +1 -0
- package/kits/physics-2d/engine/art.js +105 -0
- package/kits/physics-2d/engine/assets.js +11 -3
- package/kits/physics-2d/engine/audioContext.js +34 -0
- package/kits/physics-2d/engine/collider.js +28 -17
- package/kits/physics-2d/engine/files.js +74 -0
- package/kits/physics-2d/engine/media.js +212 -0
- package/kits/physics-2d/{physics → engine/physics}/PhysicsSystem.js +1 -1
- package/kits/physics-2d/{physics → engine/physics}/jointArt.js +1 -2
- package/kits/physics-2d/{physics → engine/physics}/matterBridge.js +2 -9
- package/kits/physics-2d/engine/scene.js +12 -0
- package/kits/physics-2d/engine/tone.js +112 -0
- package/kits/physics-2d/systems/media.js +34 -0
- package/kits/physics-2d/systems/physics.js +12 -3
- package/package.json +1 -1
- package/dist/shell/assets/index-BjOuaDJM.js +0 -144
- package/dist/shell/assets/index-CdXEpv_P.css +0 -1
- package/kits/physics-2d/physics/index.js +0 -26
- /package/kits/physics-2d/{physics → engine/physics}/controls.js +0 -0
- /package/kits/physics-2d/{physics → engine/physics}/joints.js +0 -0
package/dist/imports.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as api from './api.js';
|
|
|
5
5
|
import { runTar } from './save-deck.js';
|
|
6
6
|
import { normalizeDeckPackageJson } from './normalize.js';
|
|
7
7
|
import { getKitsDir } from './localPaths.js';
|
|
8
|
-
import { readCastleJsonOrThrow as readCastleJson, } from './castleJson.js';
|
|
8
|
+
import { readCastleJson as tryReadCastleJson, readCastleJsonOrThrow as readCastleJson, } from './castleJson.js';
|
|
9
9
|
// Adding another deck as a dependency (`castle-web add-import`; removing one is a
|
|
10
10
|
// later command). Deliberately NOT `get-deck`: that one
|
|
11
11
|
// replaces THIS deck's own source from the server (and carries guards for the
|
|
@@ -503,6 +503,94 @@ export async function addImport(dir, options = {}) {
|
|
|
503
503
|
if (result.dependencies.length > 0)
|
|
504
504
|
console.log('Run `castle-web install` to install them.');
|
|
505
505
|
}
|
|
506
|
+
// Take one import's update: replace its files with the version the server has
|
|
507
|
+
// now, then follow that version's own pins for anything newly required. Both
|
|
508
|
+
// `update-import` and the serve-start auto update come through here, so there
|
|
509
|
+
// is one path that replaces an import's files rather than two that have to be
|
|
510
|
+
// kept in step. `log` is how the caller labels the lines it produces.
|
|
511
|
+
async function applyImportUpdate(deckDir, alias, update, source, log) {
|
|
512
|
+
await placeImport(deckDir, alias, update.deckId, source, update.via);
|
|
513
|
+
log(`Updated ${alias} (${update.from} -> ${source.updatedAt})`);
|
|
514
|
+
// Its own dependencies may have moved too.
|
|
515
|
+
const transitive = await addTransitiveImports(deckDir, alias, importedDeckIds(deckDir));
|
|
516
|
+
for (const t of transitive)
|
|
517
|
+
log(`Also imported ${IMPORTS_DIR}/${t} (needed by ${alias})`);
|
|
518
|
+
}
|
|
519
|
+
// Whether the deck behind an import asked to be tracked rather than pinned --
|
|
520
|
+
// `autoUpdateWhenImported` in ITS castle.json, which is a statement its author
|
|
521
|
+
// makes about their own deck (see castleJson.ts).
|
|
522
|
+
//
|
|
523
|
+
// Read from the copy already on disk, NOT from the version about to be fetched.
|
|
524
|
+
// The flag the importer has is the one that was there when they took this
|
|
525
|
+
// version; letting the incoming version answer would let any deck start pushing
|
|
526
|
+
// to everyone who already imported it, after the fact. The cost is a one-update
|
|
527
|
+
// lag both ways -- turning the flag on reaches an existing importer only once
|
|
528
|
+
// they take an update by hand, and turning it off gets delivered by one last
|
|
529
|
+
// auto update -- which is the safe direction for both.
|
|
530
|
+
function importOptsIntoAutoUpdate(deckDir, alias) {
|
|
531
|
+
const config = tryReadCastleJson(path.join(deckDir, IMPORTS_DIR, alias));
|
|
532
|
+
return config?.autoUpdateWhenImported === true;
|
|
533
|
+
}
|
|
534
|
+
// Auto-update is honored only for imports of decks on the castle account. The
|
|
535
|
+
// flag is the mechanism and this is a leash on it: a deck that pushes new code
|
|
536
|
+
// to everyone who imported it is asking for a lot of trust, and until decks
|
|
537
|
+
// carry some other signal of it, the only ones that get it are the ones we
|
|
538
|
+
// publish (the framework kits -- the same account `init` qualifies their aliases
|
|
539
|
+
// with).
|
|
540
|
+
//
|
|
541
|
+
// Deliberately not a setting: nothing a deck or a person can flip, since that
|
|
542
|
+
// would be the leash undoing itself. The env var is a test seam -- the automated
|
|
543
|
+
// check can't publish a deck to the castle account (nothing should), so it
|
|
544
|
+
// substitutes which account counts and exercises both branches for real.
|
|
545
|
+
const AUTO_UPDATE_ACCOUNT = process.env.CASTLE_AUTO_UPDATE_TRUSTED_ACCOUNT ?? 'castle';
|
|
546
|
+
// Who published a deck. Readable without auth for the same decks webDeckSource
|
|
547
|
+
// is, so this answers in a sandbox with no login. A lookup that fails answers
|
|
548
|
+
// "not trusted": the failure direction has to be the one that leaves an import
|
|
549
|
+
// where its deck's author pinned it.
|
|
550
|
+
async function importIsOnTrustedAccount(deckId) {
|
|
551
|
+
const meta = await api.deckMeta(deckId).catch(() => null);
|
|
552
|
+
return meta?.creator?.username === AUTO_UPDATE_ACCOUNT;
|
|
553
|
+
}
|
|
554
|
+
// Move every opted-in import that the account gate allows to the version the
|
|
555
|
+
// server has now.
|
|
556
|
+
//
|
|
557
|
+
// Called once from serve start (see serve.ts) and nowhere else: that is the one
|
|
558
|
+
// moment where replacing a dependency's files can't pull the rug out from under
|
|
559
|
+
// anything -- no editor is holding unsaved state and no play session is running
|
|
560
|
+
// yet. A page reload happens mid-session, so it deliberately does not do this.
|
|
561
|
+
//
|
|
562
|
+
// Silent when nothing moves. Nothing here is fatal: an import that can't be
|
|
563
|
+
// checked (offline, no source on the server, a bad archive) stays exactly as it
|
|
564
|
+
// is pinned, and the serve starts anyway.
|
|
565
|
+
export async function autoUpdateImports(deckDir) {
|
|
566
|
+
const pins = tryReadCastleJson(deckDir)?.imports ?? {};
|
|
567
|
+
const updated = [];
|
|
568
|
+
for (const [alias, pin] of Object.entries(pins)) {
|
|
569
|
+
// A kit copied from the CLI has no deck to ask; it moves when the CLI does.
|
|
570
|
+
if (!pin?.deckId)
|
|
571
|
+
continue;
|
|
572
|
+
if (!importOptsIntoAutoUpdate(deckDir, alias))
|
|
573
|
+
continue;
|
|
574
|
+
try {
|
|
575
|
+
if (!(await importIsOnTrustedAccount(pin.deckId)))
|
|
576
|
+
continue;
|
|
577
|
+
const source = await api.webDeckSource(pin.deckId);
|
|
578
|
+
if (!source || source.updatedAt === pin.version)
|
|
579
|
+
continue;
|
|
580
|
+
await applyImportUpdate(deckDir, alias, { deckId: pin.deckId, from: pin.version, via: pin.via }, source, (message) => console.log(`auto-update: ${message}`));
|
|
581
|
+
updated.push({ alias, from: pin.version, to: source.updatedAt });
|
|
582
|
+
}
|
|
583
|
+
catch (e) {
|
|
584
|
+
console.warn(`auto-update: left "${alias}" as it is -- ${e instanceof Error ? e.message : String(e)}`);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (updated.length > 0) {
|
|
588
|
+
for (const dep of syncImportDependencies(deckDir)) {
|
|
589
|
+
console.log(`auto-update: added dependency ${dep}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return updated;
|
|
593
|
+
}
|
|
506
594
|
// `castle-web update-import [alias] [dir]`: re-fetch an import (or all of them)
|
|
507
595
|
// at the version the server has now. Updates happen while editing, never on
|
|
508
596
|
// someone else's behalf at play time -- the deck that gets published is one that
|
|
@@ -552,12 +640,7 @@ export async function updateImport(dir, options = {}) {
|
|
|
552
640
|
changed = true;
|
|
553
641
|
continue;
|
|
554
642
|
}
|
|
555
|
-
await
|
|
556
|
-
console.log(`Updated ${alias} (${pin.version} -> ${source.updatedAt})`);
|
|
557
|
-
// Its own dependencies may have moved too.
|
|
558
|
-
const transitive = await addTransitiveImports(deckDir, alias, importedDeckIds(deckDir));
|
|
559
|
-
for (const t of transitive)
|
|
560
|
-
console.log(`Also imported ${IMPORTS_DIR}/${t} (needed by ${alias})`);
|
|
643
|
+
await applyImportUpdate(deckDir, alias, { deckId: pin.deckId, from: pin.version, via: pin.via }, source, (message) => console.log(message));
|
|
561
644
|
changed = true;
|
|
562
645
|
}
|
|
563
646
|
if (!changed)
|
package/dist/init.js
CHANGED
|
@@ -36,7 +36,7 @@ const DEFAULT_KIT = "physics-2d";
|
|
|
36
36
|
// Registry version of castle-web-sdk to inject when scaffolding from a
|
|
37
37
|
// globally-installed castle-web (not from inside the workspace). Bumped
|
|
38
38
|
// alongside cli/sdk version bumps.
|
|
39
|
-
const PUBLISHED_SDK_VERSION = "0.4.
|
|
39
|
+
const PUBLISHED_SDK_VERSION = "0.4.12";
|
|
40
40
|
// The account the first-party kits are (to be) published under, so a kit
|
|
41
41
|
// imported from the CLI's copy is named the same as one fetched from the server.
|
|
42
42
|
const KIT_AUTHOR = "castle";
|
|
@@ -296,6 +296,13 @@ function makeKitPin(kitConfig, kit) {
|
|
|
296
296
|
}
|
|
297
297
|
return { source: "builtin", kit, version: getCliVersion() };
|
|
298
298
|
}
|
|
299
|
+
// The kit's editor block minus `fileTypes` -- the layout and file filters a new
|
|
300
|
+
// deck starts from, without the vocabulary it resolves from the kit itself.
|
|
301
|
+
function withoutFileTypes(editor) {
|
|
302
|
+
const rest = { ...(editor ?? {}) };
|
|
303
|
+
delete rest.fileTypes;
|
|
304
|
+
return rest;
|
|
305
|
+
}
|
|
299
306
|
// Scaffold a deck that IMPORTS its kit instead of copying it. The kit's files
|
|
300
307
|
// land in `imports/<kit>/` (read-only, like any dependency) and the deck itself
|
|
301
308
|
// holds only what is genuinely its own: an entry point, its config, and its
|
|
@@ -331,8 +338,14 @@ function scaffoldFromKitImport(kit, projectDir) {
|
|
|
331
338
|
// The deck's own castle.json: the kit's editor config (panel layout, file
|
|
332
339
|
// filters) as a starting point -- it is the deck's to edit from here -- plus
|
|
333
340
|
// the import pin.
|
|
341
|
+
//
|
|
342
|
+
// `fileTypes` is deliberately NOT copied. It is the kit's own vocabulary, and
|
|
343
|
+
// the deck resolves it live from every import's castle.json (see
|
|
344
|
+
// editorConfig.resolveFileTypes) -- so the kit keeps owning it, a kit fix
|
|
345
|
+
// reaches the deck like any other, and a kit imported LATER contributes its
|
|
346
|
+
// types too, which a copy taken at scaffold time never could.
|
|
334
347
|
writeJsonFile(path.join(projectDir, "castle.json"), {
|
|
335
|
-
...(kitConfig.editor ? { editor: kitConfig.editor } : {}),
|
|
348
|
+
...(kitConfig.editor ? { editor: withoutFileTypes(kitConfig.editor) } : {}),
|
|
336
349
|
imports: { [alias]: makeKitPin(kitConfig, kit) },
|
|
337
350
|
});
|
|
338
351
|
// The kit's code runs from THIS deck's node_modules, so its dependencies are
|
package/dist/serve.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createIdeServer, FAVICON_LINK_TAGS } from './ide.js';
|
|
|
8
8
|
import { createAgentServer } from './agent.js';
|
|
9
9
|
import { sceneFilesPlugin, importsAliasPlugin } from './vitePlugins.js';
|
|
10
10
|
import { installFilesChangedWatcher } from './filesChanged.js';
|
|
11
|
+
import { autoUpdateImports } from './imports.js';
|
|
11
12
|
import * as config from './config.js';
|
|
12
13
|
import { graphql as castleGraphql } from './api.js';
|
|
13
14
|
import { executeCommand } from './castle-host/host.js';
|
|
@@ -214,6 +215,21 @@ export async function serve(dir, options = {}) {
|
|
|
214
215
|
await serveDetached(projectDir, options);
|
|
215
216
|
return;
|
|
216
217
|
}
|
|
218
|
+
// Imports whose decks opted into auto-update move to their latest version
|
|
219
|
+
// now, before anything is served. Serve start is the only safe moment for it:
|
|
220
|
+
// the process is booting, so no editor holds unsaved state and no play
|
|
221
|
+
// session is running against the copy being replaced.
|
|
222
|
+
//
|
|
223
|
+
// Skipped when this deck already has a live serve. That deck IS open -- the
|
|
224
|
+
// dedup above normally returns before here, but an explicit --port/--host
|
|
225
|
+
// asks for a second serve, and a second serve is not a reason to replace
|
|
226
|
+
// files under the first one's session.
|
|
227
|
+
if (!existingServe(projectDir)) {
|
|
228
|
+
// A failure to check must never keep the deck from opening.
|
|
229
|
+
await autoUpdateImports(projectDir).catch((e) => {
|
|
230
|
+
console.warn(`auto-update: skipped -- ${e instanceof Error ? e.message : String(e)}`);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
217
233
|
// Close stdin when not attached to a TTY so the process can be cleanly
|
|
218
234
|
// backgrounded with `&` (otherwise stdin reads can block or hold the parent).
|
|
219
235
|
if (!process.stdin.isTTY) {
|