lecodes-cli 0.19.2 → 0.20.1
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/README.md +1 -0
- package/dist/index.js +1324 -658
- package/package.json +9 -9
- package/runtime/materials/decal-relief.mat +178 -0
- package/runtime/materials/decal.mat +172 -0
- package/runtime/materials/lightmap-baked-lite.mat +175 -0
- package/runtime/materials/lightmap-baked.mat +176 -0
- package/runtime/materials/lightmap.mat +181 -0
- package/runtime/materials/lit.mat +43 -0
- package/runtime/materials/particles-quad.mat +171 -0
- package/runtime/materials/particles.mat +121 -0
- package/runtime/materials/shadow.mat +21 -0
- package/runtime/materials/terrain.mat +134 -0
- package/runtime/materials/unlit-transparent.mat +42 -0
- package/runtime/materials/unlit.mat +44 -0
- package/runtime/materials/video.mat +31 -0
- package/runtime/sdk-types.json +1 -1
- package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
- package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
- package/runtime/web/assets/basis-C64VHDVD.js +1 -0
- package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
- package/runtime/web/assets/createViewerLite-C_bkKXLg.js +852 -0
- package/runtime/web/assets/draco-BiISTFcR.js +118 -0
- package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
- package/runtime/web/assets/index-BTB5KV9x.js +2 -0
- package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
- package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
- package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
- package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
- package/runtime/web/embed.html +32 -0
- package/runtime/web/embed.js +171 -0
- package/src/cmgenTool.ts +17 -9
- package/src/commands/app.ts +6 -1
- package/src/commands/appDesktop.ts +27 -4
- package/src/commands/appShared.ts +145 -3
- package/src/commands/appTemplates.ts +28 -4
- package/src/commands/assets.ts +3 -7
- package/src/commands/design.ts +9 -26
- package/src/commands/desktop.ts +9 -1
- package/src/commands/dev.ts +64 -0
- package/src/commands/projectTemplates.ts +17 -4
- package/src/commands/render.ts +9 -11
- package/src/commands/scene.ts +2 -6
- package/src/commands/shaders.ts +17 -1
- package/src/commands/shadersNew.ts +133 -0
- package/src/commands/test.ts +2 -1
- package/src/commands/thumbs.ts +3 -6
- package/src/commands/update.ts +42 -21
- package/src/desktopRenderer.ts +32 -2
- package/src/desktopScript.ts +1 -1
- package/src/dev/androidDev.ts +154 -0
- package/src/dev/devServer.ts +53 -6
- package/src/dev/webRunner.ts +22 -0
- package/src/index.ts +24 -4
- package/src/matcTool.ts +31 -19
- package/src/peerInstall.ts +160 -0
- package/src/peers.ts +48 -14
- package/src/projectEnv.ts +4 -0
- package/src/releases.ts +16 -0
package/dist/index.js
CHANGED
|
@@ -47,6 +47,78 @@ var __export = (target, all) => {
|
|
|
47
47
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
48
48
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
49
49
|
|
|
50
|
+
// package.json
|
|
51
|
+
var package_default;
|
|
52
|
+
var init_package = __esm(() => {
|
|
53
|
+
package_default = {
|
|
54
|
+
name: "lecodes-cli",
|
|
55
|
+
version: "0.20.1",
|
|
56
|
+
dependencies: {
|
|
57
|
+
"@letary/chisel": "^0.10.0",
|
|
58
|
+
jimp: "^1.6.1",
|
|
59
|
+
"lecodes-sdk": "workspace:*"
|
|
60
|
+
},
|
|
61
|
+
devDependencies: {
|
|
62
|
+
"lecodes-design": "^0.9.0",
|
|
63
|
+
"lecodes-renderer": "workspace:*",
|
|
64
|
+
"qrcode-terminal": "^0.12.0",
|
|
65
|
+
"lecodes-3d-editor": "^0.7.0",
|
|
66
|
+
"lecodes-assets": "workspace:*",
|
|
67
|
+
"lecodes-creator-nav": "workspace:*",
|
|
68
|
+
"lecodes-viewer-lite": "workspace:*"
|
|
69
|
+
},
|
|
70
|
+
peerDependencies: {
|
|
71
|
+
"lecodes-design": "^0.9.0",
|
|
72
|
+
"lecodes-renderer": "^0.20.0",
|
|
73
|
+
"lecodes-3d-editor": "^0.7.0",
|
|
74
|
+
"lecodes-assets": "^0.2.1"
|
|
75
|
+
},
|
|
76
|
+
bin: {
|
|
77
|
+
lecodes: "dist/index.js"
|
|
78
|
+
},
|
|
79
|
+
description: "Clone, edit and push LeCodes projects from your machine.",
|
|
80
|
+
engines: {
|
|
81
|
+
node: ">=18"
|
|
82
|
+
},
|
|
83
|
+
files: [
|
|
84
|
+
"dist",
|
|
85
|
+
"src",
|
|
86
|
+
"runtime",
|
|
87
|
+
"README.md"
|
|
88
|
+
],
|
|
89
|
+
keywords: [
|
|
90
|
+
"lecodes",
|
|
91
|
+
"cli"
|
|
92
|
+
],
|
|
93
|
+
license: "MIT",
|
|
94
|
+
peerDependenciesMeta: {
|
|
95
|
+
"lecodes-design": {
|
|
96
|
+
optional: true
|
|
97
|
+
},
|
|
98
|
+
"lecodes-renderer": {
|
|
99
|
+
optional: true
|
|
100
|
+
},
|
|
101
|
+
"lecodes-3d-editor": {
|
|
102
|
+
optional: true
|
|
103
|
+
},
|
|
104
|
+
"lecodes-assets": {
|
|
105
|
+
optional: true
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
scripts: {
|
|
109
|
+
icon: "bun scripts/make-icon.ts",
|
|
110
|
+
"vendor-runtime": "bun scripts/vendor-runtime.ts",
|
|
111
|
+
"sync-peers": "bun scripts/sync-peer-versions.ts",
|
|
112
|
+
build: 'bun scripts/vendor-runtime.ts && bun build src/index.ts --target=node --outfile dist/index.js --external @letary/chisel --external lecodes-renderer --external lecodes-design --external lecodes-3d-editor --external lecodes-assets --banner="#!/usr/bin/env node"',
|
|
113
|
+
"build:bin": "bun scripts/build-bin.ts",
|
|
114
|
+
typecheck: "tsc --noEmit",
|
|
115
|
+
dev: "bun run src/index.ts",
|
|
116
|
+
prepublishOnly: "bun run sync-peers && bun run build"
|
|
117
|
+
},
|
|
118
|
+
type: "module"
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
50
122
|
// ../sdk/src/compile/assetName.ts
|
|
51
123
|
var inRanges = (cp, ranges) => ranges.some(([from, to]) => cp >= from && cp <= to), INVISIBLE, ODD_SPACE, ILLEGAL = '/\\:*?"<>|', sanitizeAssetName = (name) => {
|
|
52
124
|
let out = "";
|
|
@@ -172165,17 +172237,12 @@ var buildHeader = (name, code, opts = {}) => {
|
|
|
172165
172237
|
};
|
|
172166
172238
|
|
|
172167
172239
|
// ../sdk/src/compile/compileProject.ts
|
|
172168
|
-
var
|
|
172169
|
-
const entries = stubServerEntries(opts.entries, opts.serverUrl ?? "");
|
|
172170
|
-
const entry = opts.entryOverride ?? detectEntry(entries, scanModulesChisel).entry;
|
|
172171
|
-
if (!entry) {
|
|
172172
|
-
throw new Error("Couldn't find an entrypoint (no .ts/.js project file — name yours main.ts). Pass entryOverride.");
|
|
172173
|
-
}
|
|
172240
|
+
var entriesToFiles = (entries, opts) => {
|
|
172174
172241
|
const files = {};
|
|
172175
172242
|
for (const e of entries) {
|
|
172176
172243
|
if (e.type === "resource") {
|
|
172177
172244
|
if (opts.localAssets && !e.remote) {
|
|
172178
|
-
const name = e.path.split(/[
|
|
172245
|
+
const name = e.path.split(/[\/]/).pop() ?? e.path;
|
|
172179
172246
|
files[e.path] = `export default ("id:" + _creatorUtils.fetchLocal(${JSON.stringify(name)}))`;
|
|
172180
172247
|
} else {
|
|
172181
172248
|
files[e.path] = `export default "${opts.publicUrl}${e.fileSrc ?? ""}"`;
|
|
@@ -172183,7 +172250,7 @@ var compileProject = async (opts) => {
|
|
|
172183
172250
|
} else if (e.type === "shader") {
|
|
172184
172251
|
const shaders = e.shaders ?? [];
|
|
172185
172252
|
if (e.shadersLocal && shaders.length > 0) {
|
|
172186
|
-
const name = shaders[0].src.split(/[
|
|
172253
|
+
const name = shaders[0].src.split(/[\/]/).pop() ?? "";
|
|
172187
172254
|
const [prefix, suffix = ".filamat"] = name.split("_opengl");
|
|
172188
172255
|
files[e.path] = `export default ("id:" + _creatorUtils.fetchLocal(${JSON.stringify(prefix + "_")} + _creator.backend + ${JSON.stringify(suffix)}))`;
|
|
172189
172256
|
} else if (shaders.length > 0) {
|
|
@@ -172193,21 +172260,30 @@ var compileProject = async (opts) => {
|
|
|
172193
172260
|
}
|
|
172194
172261
|
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
172195
172262
|
files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
|
|
172263
|
+
} else if (e.path.toLowerCase().endsWith(".json")) {
|
|
172264
|
+
files[e.path] = `export default ${e.text?.trim() || "null"}`;
|
|
172196
172265
|
} else {
|
|
172197
172266
|
files[e.path] = e.text ?? "";
|
|
172198
172267
|
}
|
|
172199
172268
|
}
|
|
172200
|
-
|
|
172201
|
-
|
|
172202
|
-
|
|
172203
|
-
|
|
172269
|
+
return files;
|
|
172270
|
+
}, compileProjectWithMap = async (opts) => {
|
|
172271
|
+
const entries = stubServerEntries(opts.entries, opts.serverUrl ?? "");
|
|
172272
|
+
const entry = opts.entryOverride ?? detectEntry(entries, scanModulesChisel).entry;
|
|
172273
|
+
if (!entry) {
|
|
172274
|
+
throw new Error("Couldn't find an entrypoint (no .ts/.js project file — name yours main.ts). Pass entryOverride.");
|
|
172204
172275
|
}
|
|
172276
|
+
const files = entriesToFiles(entries, opts);
|
|
172277
|
+
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm", opts.minify, opts.editor, opts.libraries, opts.sourcemap ?? true);
|
|
172278
|
+
if (opts.header === false)
|
|
172279
|
+
return { code, map, entry };
|
|
172205
172280
|
const header = buildHeader(opts.name, code, { icon: opts.icon, fonts: opts.fontsHeader, icons: opts.iconsHeader });
|
|
172206
|
-
if (!map)
|
|
172207
|
-
return header + code;
|
|
172208
172281
|
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
172209
|
-
return
|
|
172210
|
-
|
|
172282
|
+
return { code: header + code, map: map ? offsetSourceMap(map, headerLines) : null, entry };
|
|
172283
|
+
}, compileProject = async (opts) => {
|
|
172284
|
+
const { code, map } = await compileProjectWithMap(opts);
|
|
172285
|
+
return map ? `${code}
|
|
172286
|
+
${inlineSourceMapComment(map)}` : code;
|
|
172211
172287
|
};
|
|
172212
172288
|
var init_compileProject = __esm(() => {
|
|
172213
172289
|
init_bundler();
|
|
@@ -173142,10 +173218,12 @@ __export(exports_compile, {
|
|
|
173142
173218
|
faceFileName: () => faceFileName,
|
|
173143
173219
|
extractMatBlocks: () => extractMatBlocks,
|
|
173144
173220
|
externalSamplerReg: () => externalSamplerReg,
|
|
173221
|
+
entriesToFiles: () => entriesToFiles,
|
|
173145
173222
|
detectProjectKind: () => detectProjectKind,
|
|
173146
173223
|
detectEntry: () => detectEntry,
|
|
173147
173224
|
deriveEndpointParams: () => deriveEndpointParams,
|
|
173148
173225
|
compileServerBundle: () => compileServerBundle,
|
|
173226
|
+
compileProjectWithMap: () => compileProjectWithMap,
|
|
173149
173227
|
compileProject: () => compileProject,
|
|
173150
173228
|
collectVendoredIcons: () => collectVendoredIcons,
|
|
173151
173229
|
bundleProjectWithMap: () => bundleProjectWithMap,
|
|
@@ -214125,26 +214203,26 @@ var init_esm30 = __esm(() => {
|
|
|
214125
214203
|
|
|
214126
214204
|
// src/types.ts
|
|
214127
214205
|
import { dirname as dirname13, join as join22, relative as relative3, sep } from "node:path";
|
|
214128
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as
|
|
214206
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync15, readdirSync as readdirSync11, readFileSync as readFileSync18, rmSync as rmSync8, statSync as statSync9, writeFileSync as writeFileSync17 } from "node:fs";
|
|
214129
214207
|
import { createRequire as createRequire4 } from "node:module";
|
|
214130
214208
|
var TSCONFIG, listBundleFiles = (dir, base = dir) => {
|
|
214131
214209
|
if (!existsSync21(dir))
|
|
214132
214210
|
return [];
|
|
214133
214211
|
const out = [];
|
|
214134
|
-
for (const name of
|
|
214212
|
+
for (const name of readdirSync11(dir)) {
|
|
214135
214213
|
const path2 = join22(dir, name);
|
|
214136
|
-
if (
|
|
214214
|
+
if (statSync9(path2).isDirectory())
|
|
214137
214215
|
out.push(...listBundleFiles(path2, base));
|
|
214138
214216
|
else
|
|
214139
214217
|
out.push(relative3(base, path2).split(sep).join("/"));
|
|
214140
214218
|
}
|
|
214141
214219
|
return out;
|
|
214142
214220
|
}, pruneEmptyDirs = (dir, root) => {
|
|
214143
|
-
if (!existsSync21(dir) || !
|
|
214221
|
+
if (!existsSync21(dir) || !statSync9(dir).isDirectory())
|
|
214144
214222
|
return;
|
|
214145
|
-
for (const name of
|
|
214223
|
+
for (const name of readdirSync11(dir))
|
|
214146
214224
|
pruneEmptyDirs(join22(dir, name), root);
|
|
214147
|
-
if (dir !== root &&
|
|
214225
|
+
if (dir !== root && readdirSync11(dir).length === 0)
|
|
214148
214226
|
rmSync8(dir, { recursive: true });
|
|
214149
214227
|
}, writeTypeBundle = (root, bundle) => {
|
|
214150
214228
|
const typesDir = join22(root, LECODES_DIR, "types");
|
|
@@ -214230,13 +214308,13 @@ var init_types4 = __esm(() => {
|
|
|
214230
214308
|
|
|
214231
214309
|
// src/compile/designCompile.ts
|
|
214232
214310
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
214233
|
-
import { basename as
|
|
214311
|
+
import { basename as basename10 } from "node:path";
|
|
214234
214312
|
var LOCAL_ASSET_SCHEME = "lecodesfile://", compileDesignScreen = async (opts) => {
|
|
214235
214313
|
const { root, manifest, designDir, designKey, screenId, state } = opts;
|
|
214236
214314
|
const entries = [];
|
|
214237
214315
|
const scan2 = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest?.files ?? {})) });
|
|
214238
214316
|
for (const file of scan2.files) {
|
|
214239
|
-
const type = manifest?.files[file.path]?.type ?? classifyKind(
|
|
214317
|
+
const type = manifest?.files[file.path]?.type ?? classifyKind(basename10(file.path));
|
|
214240
214318
|
if (type === "resource") {
|
|
214241
214319
|
entries.push({
|
|
214242
214320
|
path: file.path,
|
|
@@ -214368,7 +214446,7 @@ var init_browserAuth = __esm(() => {
|
|
|
214368
214446
|
init_util();
|
|
214369
214447
|
});
|
|
214370
214448
|
|
|
214371
|
-
//
|
|
214449
|
+
// ../../node_modules/.bun/lecodes-design@0.9.0+edf5a69441922373/node_modules/lecodes-design/src/core/concept.ts
|
|
214372
214450
|
var SPEC_TEMPLATE = `# Concept
|
|
214373
214451
|
|
|
214374
214452
|
The narrative half of this design. \`meta.json\` + the canvas hold the *visual/flow* half (screens,
|
|
@@ -214404,7 +214482,7 @@ Behaviour that isn't visible in a single screen — validation, permissions, not
|
|
|
214404
214482
|
cases — and the decisions you've made and why, so they aren't re-litigated later.
|
|
214405
214483
|
`;
|
|
214406
214484
|
|
|
214407
|
-
//
|
|
214485
|
+
// ../../node_modules/.bun/lecodes-design@0.9.0+edf5a69441922373/node_modules/lecodes-design/src/templates.ts
|
|
214408
214486
|
var SPEC_MD, META_JSON = `{
|
|
214409
214487
|
"screens": {
|
|
214410
214488
|
"home": { "pos": [0, 0], "desc": "Example screen — replace me", "role": "list" }
|
|
@@ -214843,12 +214921,179 @@ var init_designTemplates = __esm(() => {
|
|
|
214843
214921
|
init_templates();
|
|
214844
214922
|
});
|
|
214845
214923
|
|
|
214924
|
+
// src/peers.ts
|
|
214925
|
+
import { existsSync as existsSync24, readFileSync as readFileSync20 } from "node:fs";
|
|
214926
|
+
import { createRequire as createRequire5 } from "node:module";
|
|
214927
|
+
import { homedir as homedir8 } from "node:os";
|
|
214928
|
+
import { dirname as dirname14, join as join24, resolve as resolve11 } from "node:path";
|
|
214929
|
+
var OPTIONAL_PEERS, peersDir = () => process.env.LECODES_PEERS_DIR ? resolve11(process.env.LECODES_PEERS_DIR) : join24(homedir8(), ".lecodes", "peers"), peerDirInPrefix = (name) => join24(peersDir(), "node_modules", ...name.split("/")), readVersion = (dir) => {
|
|
214930
|
+
try {
|
|
214931
|
+
return JSON.parse(readFileSync20(join24(dir, "package.json"), "utf8")).version ?? "unknown";
|
|
214932
|
+
} catch {
|
|
214933
|
+
return null;
|
|
214934
|
+
}
|
|
214935
|
+
}, peerVersionGlobal = (name, probe) => {
|
|
214936
|
+
try {
|
|
214937
|
+
const require2 = createRequire5(import.meta.url);
|
|
214938
|
+
let dir = dirname14(require2.resolve(probe));
|
|
214939
|
+
for (let depth = 0;depth < 16; depth++) {
|
|
214940
|
+
const manifest = join24(dir, "package.json");
|
|
214941
|
+
if (existsSync24(manifest)) {
|
|
214942
|
+
const parsed = JSON.parse(readFileSync20(manifest, "utf8"));
|
|
214943
|
+
if (parsed.name === name)
|
|
214944
|
+
return parsed.version ?? "unknown";
|
|
214945
|
+
}
|
|
214946
|
+
const parent = dirname14(dir);
|
|
214947
|
+
if (parent === dir)
|
|
214948
|
+
break;
|
|
214949
|
+
dir = parent;
|
|
214950
|
+
}
|
|
214951
|
+
} catch {}
|
|
214952
|
+
return null;
|
|
214953
|
+
}, peerVersionPrefix = (name) => readVersion(peerDirInPrefix(name)), peerVersion = (name, probe) => peerVersionGlobal(name, probe) ?? peerVersionPrefix(name), peerSource = (name, probe) => peerVersionGlobal(name, probe) !== null ? "global" : peerVersionPrefix(name) !== null ? "prefix" : null;
|
|
214954
|
+
var init_peers = __esm(() => {
|
|
214955
|
+
OPTIONAL_PEERS = [
|
|
214956
|
+
["lecodes-renderer", "lecodes-renderer/headless"],
|
|
214957
|
+
["lecodes-design", "lecodes-design/server"],
|
|
214958
|
+
["lecodes-3d-editor", "lecodes-3d-editor/server"],
|
|
214959
|
+
["lecodes-assets", "lecodes-assets/cli"],
|
|
214960
|
+
["@napi-rs/canvas", "@napi-rs/canvas"]
|
|
214961
|
+
];
|
|
214962
|
+
});
|
|
214963
|
+
|
|
214964
|
+
// src/peerInstall.ts
|
|
214965
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
214966
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync18, readFileSync as readFileSync21, writeFileSync as writeFileSync19 } from "node:fs";
|
|
214967
|
+
import { join as join25 } from "node:path";
|
|
214968
|
+
import { pathToFileURL } from "node:url";
|
|
214969
|
+
async function loadPeer(name, subpath, opts) {
|
|
214970
|
+
const spec = subpath ? `${name}/${subpath}` : name;
|
|
214971
|
+
try {
|
|
214972
|
+
return await import(spec);
|
|
214973
|
+
} catch (e2) {
|
|
214974
|
+
if (!isNotFound(e2, spec))
|
|
214975
|
+
throw e2;
|
|
214976
|
+
}
|
|
214977
|
+
const dir = peerDirInPrefix(name);
|
|
214978
|
+
if (!existsSync25(join25(dir, "package.json"))) {
|
|
214979
|
+
if (opts.install === false)
|
|
214980
|
+
return null;
|
|
214981
|
+
if (peerVersionGlobal(name, spec) !== null)
|
|
214982
|
+
throw new CliError(`${name} is installed but "${spec}" can't be loaded from it — reinstall it: npm install -g ${peerSpec(name)}`);
|
|
214983
|
+
await installPeers(companions(name), opts.for);
|
|
214984
|
+
}
|
|
214985
|
+
return await import(pathToFileURL(resolveEntry(dir, subpath)).href);
|
|
214986
|
+
}
|
|
214987
|
+
var CANVAS_RANGE_FALLBACK = "^0.1.65", readJson2 = (path2) => {
|
|
214988
|
+
try {
|
|
214989
|
+
return JSON.parse(readFileSync21(path2, "utf8"));
|
|
214990
|
+
} catch {
|
|
214991
|
+
return null;
|
|
214992
|
+
}
|
|
214993
|
+
}, peerSpec = (name) => {
|
|
214994
|
+
if (name === "lecodes-renderer")
|
|
214995
|
+
return `lecodes-renderer@${package_default.version}`;
|
|
214996
|
+
if (name === "@napi-rs/canvas") {
|
|
214997
|
+
const renderer = readJson2(join25(peerDirInPrefix("lecodes-renderer"), "package.json"));
|
|
214998
|
+
const range2 = renderer?.peerDependencies?.["@napi-rs/canvas"] ?? renderer?.optionalDependencies?.["@napi-rs/canvas"] ?? CANVAS_RANGE_FALLBACK;
|
|
214999
|
+
return `@napi-rs/canvas@${range2}`;
|
|
215000
|
+
}
|
|
215001
|
+
const range = package_default.peerDependencies?.[name];
|
|
215002
|
+
return range ? `${name}@${range}` : `${name}@latest`;
|
|
215003
|
+
}, companions = (name) => name === "lecodes-renderer" ? ["lecodes-renderer", "@napi-rs/canvas"] : [name], onPath = (cmd) => {
|
|
215004
|
+
try {
|
|
215005
|
+
return spawnSync6(cmd, ["--version"], { stdio: "ignore", shell: process.platform === "win32" }).status === 0;
|
|
215006
|
+
} catch {
|
|
215007
|
+
return false;
|
|
215008
|
+
}
|
|
215009
|
+
}, pickInstaller = () => onPath("npm") ? "npm" : onPath("bun") ? "bun" : null, manualCommand = (dir, specs) => `npm install --prefix "${dir}" ${specs.join(" ")}`, installPeers = async (names3, why, opts = {}) => {
|
|
215010
|
+
const dir = peersDir();
|
|
215011
|
+
mkdirSync18(dir, { recursive: true });
|
|
215012
|
+
const manifest = join25(dir, "package.json");
|
|
215013
|
+
if (!existsSync25(manifest)) {
|
|
215014
|
+
writeFileSync19(manifest, JSON.stringify({
|
|
215015
|
+
name: "lecodes-peers",
|
|
215016
|
+
private: true,
|
|
215017
|
+
description: "Optional lecodes-cli peers, installed on first use. Safe to delete — the CLI re-installs what a command needs."
|
|
215018
|
+
}, null, 2) + `
|
|
215019
|
+
`);
|
|
215020
|
+
}
|
|
215021
|
+
const ordered = [...names3].sort((a2, b) => a2 === "lecodes-renderer" ? -1 : b === "lecodes-renderer" ? 1 : 0);
|
|
215022
|
+
const specsNow = ordered.map(peerSpec);
|
|
215023
|
+
const yes = opts.yes === true || !!process.env.LECODES_YES;
|
|
215024
|
+
if (!yes) {
|
|
215025
|
+
const interactive = process.stdin.isTTY && process.stdout.isTTY;
|
|
215026
|
+
if (!interactive) {
|
|
215027
|
+
throw new CliError(`${why} needs ${names3.join(" + ")}, which isn't installed. Re-run with --yes to install it into ${dir}, or install by hand: ${manualCommand(dir, specsNow)}`);
|
|
215028
|
+
}
|
|
215029
|
+
log(`${why} needs ${c.bold(names3.join(" + "))}.`);
|
|
215030
|
+
log(c.dim(`It installs once into ${dir} (no admin rights); a newer CLI re-installs its own version.`));
|
|
215031
|
+
if (!await confirm("Install now?", true)) {
|
|
215032
|
+
throw new CliError(`Not installed. When you want it: ${manualCommand(dir, specsNow)}`);
|
|
215033
|
+
}
|
|
215034
|
+
}
|
|
215035
|
+
const tool = pickInstaller();
|
|
215036
|
+
if (!tool)
|
|
215037
|
+
throw new CliError(`Neither npm nor bun is on PATH, so ${names3.join(" + ")} can't be installed automatically. Install one, or run on a machine with Node: ${manualCommand(dir, specsNow)}`);
|
|
215038
|
+
for (const name of ordered) {
|
|
215039
|
+
const spec = peerSpec(name);
|
|
215040
|
+
const argv = tool === "npm" ? ["install", "--prefix", dir, "--no-audit", "--no-fund", "--loglevel=error", spec] : ["add", "--cwd", dir, spec];
|
|
215041
|
+
note(`$ ${tool} ${argv.join(" ")}`);
|
|
215042
|
+
const r2 = spawnSync6(tool, argv, { stdio: "inherit", shell: process.platform === "win32", timeout: 10 * 60000 });
|
|
215043
|
+
if (r2.error)
|
|
215044
|
+
throw new CliError(`${tool} could not run: ${r2.error.message}`);
|
|
215045
|
+
if (r2.status !== 0)
|
|
215046
|
+
throw new CliError(`${tool} exited with ${r2.status ?? "a signal"} while installing ${spec}. By hand: ${manualCommand(dir, [spec])}`);
|
|
215047
|
+
if (!existsSync25(join25(peerDirInPrefix(name), "package.json")))
|
|
215048
|
+
throw new CliError(`${name} did not land in ${dir} — the installer finished but the package is missing`);
|
|
215049
|
+
}
|
|
215050
|
+
}, resolveEntry = (pkgDir, subpath) => {
|
|
215051
|
+
const manifest = readJson2(join25(pkgDir, "package.json"));
|
|
215052
|
+
if (!manifest)
|
|
215053
|
+
throw new CliError(`${pkgDir} holds no package.json`);
|
|
215054
|
+
const key = subpath ? `./${subpath}` : ".";
|
|
215055
|
+
const pick = (target) => {
|
|
215056
|
+
if (typeof target === "string")
|
|
215057
|
+
return target;
|
|
215058
|
+
if (target && typeof target === "object") {
|
|
215059
|
+
const t2 = target;
|
|
215060
|
+
for (const cond of ["import", "node", "default", "require"])
|
|
215061
|
+
if (cond in t2) {
|
|
215062
|
+
const p2 = pick(t2[cond]);
|
|
215063
|
+
if (p2)
|
|
215064
|
+
return p2;
|
|
215065
|
+
}
|
|
215066
|
+
}
|
|
215067
|
+
return null;
|
|
215068
|
+
};
|
|
215069
|
+
let rel = null;
|
|
215070
|
+
const exp = manifest.exports;
|
|
215071
|
+
if (typeof exp === "string")
|
|
215072
|
+
rel = key === "." ? exp : null;
|
|
215073
|
+
else if (exp && typeof exp === "object")
|
|
215074
|
+
rel = pick(exp[key]);
|
|
215075
|
+
if (!rel && key === ".")
|
|
215076
|
+
rel = typeof manifest.main === "string" ? manifest.main : "index.js";
|
|
215077
|
+
if (!rel)
|
|
215078
|
+
throw new CliError(`${manifest.name} has no export "${key}"`);
|
|
215079
|
+
return join25(pkgDir, rel);
|
|
215080
|
+
}, isNotFound = (e2, spec) => {
|
|
215081
|
+
const code = e2?.code;
|
|
215082
|
+
const msg = e2 instanceof Error ? e2.message : String(e2);
|
|
215083
|
+
return (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /Cannot find (module|package)/i.test(msg)) && msg.includes(spec.split("/")[0]);
|
|
215084
|
+
};
|
|
215085
|
+
var init_peerInstall = __esm(() => {
|
|
215086
|
+
init_package();
|
|
215087
|
+
init_peers();
|
|
215088
|
+
init_util();
|
|
215089
|
+
});
|
|
215090
|
+
|
|
214846
215091
|
// src/designMeta.ts
|
|
214847
|
-
import { readFileSync as
|
|
214848
|
-
import { join as
|
|
215092
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
215093
|
+
import { join as join26 } from "node:path";
|
|
214849
215094
|
var PHONE_SIZE, readMetaFrames = (designDir) => {
|
|
214850
215095
|
try {
|
|
214851
|
-
const meta = JSON.parse(
|
|
215096
|
+
const meta = JSON.parse(readFileSync22(join26(designDir, "meta.json"), "utf8"));
|
|
214852
215097
|
const d = meta?.defaultSize;
|
|
214853
215098
|
const valid = Array.isArray(d) && d.length === 2 && d.every((n2) => typeof n2 === "number" && Number.isFinite(n2) && n2 >= 64 && n2 <= 8192);
|
|
214854
215099
|
return {
|
|
@@ -214872,8 +215117,8 @@ __export(exports_design, {
|
|
|
214872
215117
|
flushDesignComments: () => flushDesignComments,
|
|
214873
215118
|
design: () => design
|
|
214874
215119
|
});
|
|
214875
|
-
import { existsSync as
|
|
214876
|
-
import { basename as
|
|
215120
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync19, readFileSync as readFileSync23, readdirSync as readdirSync14, writeFileSync as writeFileSync20, appendFileSync } from "node:fs";
|
|
215121
|
+
import { basename as basename11, join as join27 } from "node:path";
|
|
214877
215122
|
var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
214878
215123
|
let root;
|
|
214879
215124
|
let manifest;
|
|
@@ -214888,21 +215133,16 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214888
215133
|
return {
|
|
214889
215134
|
root,
|
|
214890
215135
|
manifest,
|
|
214891
|
-
name: manifest?.name ??
|
|
215136
|
+
name: manifest?.name ?? basename11(root),
|
|
214892
215137
|
dirName,
|
|
214893
|
-
designDir:
|
|
215138
|
+
designDir: join27(root, dirName),
|
|
214894
215139
|
designKey: "/" + dirName.replace(/\\/g, "/")
|
|
214895
215140
|
};
|
|
214896
215141
|
}, loadDesignServer = async (ctx, needs = []) => {
|
|
214897
|
-
if (!
|
|
215142
|
+
if (!existsSync26(ctx.designDir)) {
|
|
214898
215143
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
214899
215144
|
}
|
|
214900
|
-
|
|
214901
|
-
try {
|
|
214902
|
-
mod = await import("lecodes-design/server");
|
|
214903
|
-
} catch {
|
|
214904
|
-
throw new CliError("The design canvas isn't installed. Run: npm install -g lecodes-design");
|
|
214905
|
-
}
|
|
215145
|
+
const mod = await loadPeer("lecodes-design", "server", { for: "lecodes design" });
|
|
214906
215146
|
const missing = needs.filter((name) => typeof mod[name] !== "function");
|
|
214907
215147
|
if (missing.length) {
|
|
214908
215148
|
throw new CliError(`This needs a newer lecodes-design — the installed one has no \`${missing[0]}\`. Run: npm install -g lecodes-design@latest`);
|
|
@@ -214910,7 +215150,7 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214910
215150
|
return mod;
|
|
214911
215151
|
}, requirePlatform = (ctx, verb) => {
|
|
214912
215152
|
if (!ctx.manifest) {
|
|
214913
|
-
throw new CliError(`\`lecodes design comments ${verb}\` needs a cloned project. This folder is standalone, so
|
|
215153
|
+
throw new CliError(`\`lecodes design comments ${verb}\` needs a cloned project. This folder is standalone, so ` + `${ctx.dirName}/comments/ is already the only store — there is nothing to sync with.`);
|
|
214914
215154
|
}
|
|
214915
215155
|
const config = loadConfig();
|
|
214916
215156
|
return { apiUrl: requireApiUrl(config), token: requireToken(config), uuid: ctx.manifest.uuid };
|
|
@@ -214926,17 +215166,17 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214926
215166
|
`).find((l2) => l2.trim())?.trim() ?? "";
|
|
214927
215167
|
return line.length > max ? line.slice(0, max - 1) + "…" : line;
|
|
214928
215168
|
}, listScreens = (designDir) => {
|
|
214929
|
-
const dir =
|
|
214930
|
-
if (!
|
|
215169
|
+
const dir = join27(designDir, "screens");
|
|
215170
|
+
if (!existsSync26(dir))
|
|
214931
215171
|
return [];
|
|
214932
|
-
return
|
|
215172
|
+
return readdirSync14(dir).filter((f3) => f3.endsWith(".ts") && !f3.startsWith(".")).map((f3) => f3.slice(0, -3)).sort();
|
|
214933
215173
|
}, writeMcpConfig = (ctx, port) => {
|
|
214934
|
-
const path2 =
|
|
215174
|
+
const path2 = join27(ctx.root, ".mcp.json");
|
|
214935
215175
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
214936
215176
|
let config = {};
|
|
214937
|
-
if (
|
|
215177
|
+
if (existsSync26(path2)) {
|
|
214938
215178
|
try {
|
|
214939
|
-
config = JSON.parse(
|
|
215179
|
+
config = JSON.parse(readFileSync23(path2, "utf8"));
|
|
214940
215180
|
} catch {
|
|
214941
215181
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-design server by hand.");
|
|
214942
215182
|
return;
|
|
@@ -214949,7 +215189,7 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214949
215189
|
return;
|
|
214950
215190
|
}
|
|
214951
215191
|
config.mcpServers["lecodes-design"] = entry;
|
|
214952
|
-
|
|
215192
|
+
writeFileSync20(path2, JSON.stringify(config, null, 2) + `
|
|
214953
215193
|
`);
|
|
214954
215194
|
log(`${c.green(existing ? "updated" : "created")} .mcp.json (lecodes-design MCP → ${entry.url})`);
|
|
214955
215195
|
}, init = async (ctx, args) => {
|
|
@@ -214958,26 +215198,26 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214958
215198
|
["spec.md", SPEC_MD],
|
|
214959
215199
|
["README.md", README_HINT],
|
|
214960
215200
|
["CLAUDE.md", CLAUDE_MD],
|
|
214961
|
-
[
|
|
214962
|
-
[
|
|
214963
|
-
[
|
|
215201
|
+
[join27("shared", "tokens.ts"), TOKENS_TS],
|
|
215202
|
+
[join27("shared", "tabs.ts"), TABS_TS],
|
|
215203
|
+
[join27("screens", "home.ts"), HOME_SCREEN_TS]
|
|
214964
215204
|
];
|
|
214965
|
-
|
|
214966
|
-
|
|
215205
|
+
mkdirSync19(join27(ctx.designDir, "screens"), { recursive: true });
|
|
215206
|
+
mkdirSync19(join27(ctx.designDir, "shared"), { recursive: true });
|
|
214967
215207
|
let created = 0;
|
|
214968
215208
|
for (const [rel, content] of files) {
|
|
214969
|
-
const abs =
|
|
214970
|
-
if (
|
|
215209
|
+
const abs = join27(ctx.designDir, rel);
|
|
215210
|
+
if (existsSync26(abs)) {
|
|
214971
215211
|
note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
214972
215212
|
continue;
|
|
214973
215213
|
}
|
|
214974
|
-
|
|
215214
|
+
writeFileSync20(abs, content);
|
|
214975
215215
|
log(`${c.green("created")} ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
214976
215216
|
created++;
|
|
214977
215217
|
}
|
|
214978
|
-
const ignorePath =
|
|
215218
|
+
const ignorePath = join27(ctx.root, ".lecodesignore");
|
|
214979
215219
|
const ignoreLines = [`${ctx.dirName}/.snapshots/`, `${ctx.dirName}/comments/`];
|
|
214980
|
-
const current =
|
|
215220
|
+
const current = existsSync26(ignorePath) ? readFileSync23(ignorePath, "utf8") : "";
|
|
214981
215221
|
const present = new Set(current.split(/\r?\n/));
|
|
214982
215222
|
const missing = ignoreLines.filter((line) => !present.has(line));
|
|
214983
215223
|
if (missing.length) {
|
|
@@ -214992,8 +215232,8 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
214992
215232
|
writeMcpConfig(ctx, Number(flagStr(args, "port")) || DEFAULT_PORT);
|
|
214993
215233
|
const wroteTypes = materializeTypesLocal(ctx.designDir);
|
|
214994
215234
|
writeTsconfig(ctx.designDir);
|
|
214995
|
-
|
|
214996
|
-
|
|
215235
|
+
mkdirSync19(join27(ctx.designDir, ".lecodes", "types"), { recursive: true });
|
|
215236
|
+
writeFileSync20(join27(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
|
|
214997
215237
|
log(`${c.green("created")} ${ctx.dirName}/tsconfig.json${wroteTypes ? ` + ${ctx.dirName}/.lecodes/types/` : ""} (IDE types)`);
|
|
214998
215238
|
if (!wroteTypes)
|
|
214999
215239
|
warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
|
|
@@ -215016,23 +215256,14 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215016
215256
|
const allStates = flagBool(args, "all-states");
|
|
215017
215257
|
if (stateFlag !== undefined && allStates)
|
|
215018
215258
|
throw new CliError("Use either --state <name> or --all-states, not both.");
|
|
215019
|
-
|
|
215020
|
-
try {
|
|
215021
|
-
renderer = await import("lecodes-renderer/headless");
|
|
215022
|
-
} catch {
|
|
215023
|
-
throw new CliError("The renderer isn't installed. Run: npm install -g lecodes-renderer");
|
|
215024
|
-
}
|
|
215259
|
+
const renderer = await loadPeer("lecodes-renderer", "headless", { for: "lecodes design render" });
|
|
215025
215260
|
let discover = null;
|
|
215026
215261
|
if (stateFlag !== undefined || allStates) {
|
|
215027
|
-
|
|
215028
|
-
|
|
215029
|
-
|
|
215030
|
-
|
|
215031
|
-
|
|
215032
|
-
};
|
|
215033
|
-
} catch {
|
|
215034
|
-
throw new CliError("Rendering specific states needs the design canvas. Run: npm install -g lecodes-design");
|
|
215035
|
-
}
|
|
215262
|
+
const ds = await loadPeer("lecodes-design", "server", { for: "lecodes design render --state" });
|
|
215263
|
+
discover = (src) => {
|
|
215264
|
+
const d = ds.discoverScreenStates(src);
|
|
215265
|
+
return { states: d.states, canonical: d.canonical };
|
|
215266
|
+
};
|
|
215036
215267
|
}
|
|
215037
215268
|
const killer = setTimeout(() => {
|
|
215038
215269
|
warnErr("Snapshot timed out after 120s.");
|
|
@@ -215041,16 +215272,16 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215041
215272
|
killer.unref?.();
|
|
215042
215273
|
const png2 = flagBool(args, "png") || flagStr(args, "png") !== undefined;
|
|
215043
215274
|
const frames = readMetaFrames(ctx.designDir);
|
|
215044
|
-
const outDir = flagStr(args, "out-dir") ??
|
|
215275
|
+
const outDir = flagStr(args, "out-dir") ?? join27(ctx.designDir, ".snapshots");
|
|
215045
215276
|
const single = targets.length === 1 && requested.length === 1 && !png2 && !allStates && !flagStr(args, "out-dir");
|
|
215046
215277
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
215047
215278
|
if (!single)
|
|
215048
|
-
|
|
215279
|
+
mkdirSync19(outDir, { recursive: true });
|
|
215049
215280
|
for (const id of targets) {
|
|
215050
215281
|
const [width, height] = frames.screens[id]?.size ?? frames.defaultSize;
|
|
215051
215282
|
let plan = [{ state: undefined, bases: [id] }];
|
|
215052
215283
|
if (discover) {
|
|
215053
|
-
const { states, canonical } = discover(
|
|
215284
|
+
const { states, canonical } = discover(readFileSync23(join27(ctx.designDir, "screens", `${id}.ts`), "utf8"));
|
|
215054
215285
|
if (stateFlag !== undefined) {
|
|
215055
215286
|
if (!states.includes(stateFlag)) {
|
|
215056
215287
|
if (requested.length === 1)
|
|
@@ -215091,11 +215322,11 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215091
215322
|
for (const w of pngResult.warnings)
|
|
215092
215323
|
warnErr(`${tag}: ${w}`);
|
|
215093
215324
|
for (const base of bases) {
|
|
215094
|
-
|
|
215095
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
215325
|
+
writeFileSync20(join27(outDir, `${base}.json`), result.text);
|
|
215326
|
+
log(`${c.green("✓")} ${tag} → ${join27(outDir, `${base}.json`)}`);
|
|
215096
215327
|
if (pngResult) {
|
|
215097
|
-
|
|
215098
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
215328
|
+
writeFileSync20(join27(outDir, `${base}.png`), pngResult.png);
|
|
215329
|
+
log(`${c.green("✓")} ${tag} → ${join27(outDir, `${base}.png`)}`);
|
|
215099
215330
|
}
|
|
215100
215331
|
}
|
|
215101
215332
|
}
|
|
@@ -215103,12 +215334,7 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215103
215334
|
}, serve = async (ctx, args) => {
|
|
215104
215335
|
const designServer = await loadDesignServer(ctx);
|
|
215105
215336
|
const port = Number(flagStr(args, "port")) || DEFAULT_PORT;
|
|
215106
|
-
|
|
215107
|
-
try {
|
|
215108
|
-
renderer = await import("lecodes-renderer/headless");
|
|
215109
|
-
} catch {
|
|
215110
|
-
renderer = null;
|
|
215111
|
-
}
|
|
215337
|
+
const renderer = await loadPeer("lecodes-renderer", "headless", { for: "lecodes design (render_screen)", install: false });
|
|
215112
215338
|
const renderScreen = renderer ? async (id, format, state) => {
|
|
215113
215339
|
const r2 = renderer;
|
|
215114
215340
|
const js = await compileDesignScreen({
|
|
@@ -215397,17 +215623,14 @@ var DEFAULT_DIR = "design", DEFAULT_PORT = 4477, getContext = (args) => {
|
|
|
215397
215623
|
note("Read them with: lecodes design comments");
|
|
215398
215624
|
}, flushDesignComments = async (args) => {
|
|
215399
215625
|
const ctx = getContext(args);
|
|
215400
|
-
if (!
|
|
215626
|
+
if (!existsSync26(ctx.designDir))
|
|
215401
215627
|
return null;
|
|
215402
215628
|
const platform = optionalPlatform(ctx);
|
|
215403
215629
|
if (!platform)
|
|
215404
215630
|
return null;
|
|
215405
|
-
|
|
215406
|
-
|
|
215407
|
-
designServer = await import("lecodes-design/server");
|
|
215408
|
-
} catch {
|
|
215631
|
+
const designServer = await loadPeer("lecodes-design", "server", { for: "lecodes design", install: false });
|
|
215632
|
+
if (!designServer)
|
|
215409
215633
|
return null;
|
|
215410
|
-
}
|
|
215411
215634
|
if (typeof designServer.queuedThreads !== "function")
|
|
215412
215635
|
return null;
|
|
215413
215636
|
if (typeof designServer.settleQueued !== "function" || designServer.settleQueued.length < 4)
|
|
@@ -215514,6 +215737,7 @@ var init_design = __esm(() => {
|
|
|
215514
215737
|
init_browserAuth();
|
|
215515
215738
|
init_designTemplates();
|
|
215516
215739
|
init_util();
|
|
215740
|
+
init_peerInstall();
|
|
215517
215741
|
init_config();
|
|
215518
215742
|
init_api();
|
|
215519
215743
|
init_types4();
|
|
@@ -216536,80 +216760,15 @@ var require_main = __commonJS((exports, module) => {
|
|
|
216536
216760
|
}
|
|
216537
216761
|
};
|
|
216538
216762
|
});
|
|
216539
|
-
|
|
216540
|
-
|
|
216541
|
-
|
|
216542
|
-
version: "0.19.2",
|
|
216543
|
-
dependencies: {
|
|
216544
|
-
"@letary/chisel": "^0.10.0",
|
|
216545
|
-
jimp: "^1.6.1",
|
|
216546
|
-
"lecodes-sdk": "workspace:*"
|
|
216547
|
-
},
|
|
216548
|
-
devDependencies: {
|
|
216549
|
-
"lecodes-design": "link:lecodes-design",
|
|
216550
|
-
"lecodes-renderer": "workspace:*",
|
|
216551
|
-
"qrcode-terminal": "^0.12.0",
|
|
216552
|
-
"lecodes-3d-editor": "link:lecodes-3d-editor",
|
|
216553
|
-
"lecodes-assets": "workspace:*",
|
|
216554
|
-
"lecodes-creator-nav": "workspace:*",
|
|
216555
|
-
"lecodes-viewer-lite": "workspace:*"
|
|
216556
|
-
},
|
|
216557
|
-
peerDependencies: {
|
|
216558
|
-
"lecodes-design": "^0.9.0",
|
|
216559
|
-
"lecodes-renderer": "^0.19.2",
|
|
216560
|
-
"lecodes-3d-editor": "^0.7.0",
|
|
216561
|
-
"lecodes-assets": "^0.2.1"
|
|
216562
|
-
},
|
|
216563
|
-
bin: {
|
|
216564
|
-
lecodes: "dist/index.js"
|
|
216565
|
-
},
|
|
216566
|
-
description: "Clone, edit and push LeCodes projects from your machine.",
|
|
216567
|
-
engines: {
|
|
216568
|
-
node: ">=18"
|
|
216569
|
-
},
|
|
216570
|
-
files: [
|
|
216571
|
-
"dist",
|
|
216572
|
-
"src",
|
|
216573
|
-
"runtime",
|
|
216574
|
-
"README.md"
|
|
216575
|
-
],
|
|
216576
|
-
keywords: [
|
|
216577
|
-
"lecodes",
|
|
216578
|
-
"cli"
|
|
216579
|
-
],
|
|
216580
|
-
license: "MIT",
|
|
216581
|
-
peerDependenciesMeta: {
|
|
216582
|
-
"lecodes-design": {
|
|
216583
|
-
optional: true
|
|
216584
|
-
},
|
|
216585
|
-
"lecodes-renderer": {
|
|
216586
|
-
optional: true
|
|
216587
|
-
},
|
|
216588
|
-
"lecodes-3d-editor": {
|
|
216589
|
-
optional: true
|
|
216590
|
-
},
|
|
216591
|
-
"lecodes-assets": {
|
|
216592
|
-
optional: true
|
|
216593
|
-
}
|
|
216594
|
-
},
|
|
216595
|
-
scripts: {
|
|
216596
|
-
icon: "bun scripts/make-icon.ts",
|
|
216597
|
-
"vendor-runtime": "bun scripts/vendor-runtime.ts",
|
|
216598
|
-
"sync-peers": "bun scripts/sync-peer-versions.ts",
|
|
216599
|
-
build: 'bun scripts/vendor-runtime.ts && bun build src/index.ts --target=node --outfile dist/index.js --external @letary/chisel --external lecodes-renderer --external lecodes-design --external lecodes-3d-editor --external lecodes-assets --banner="#!/usr/bin/env node"',
|
|
216600
|
-
"build:bin": "bun scripts/build-bin.ts",
|
|
216601
|
-
typecheck: "tsc --noEmit",
|
|
216602
|
-
dev: "bun run src/index.ts",
|
|
216603
|
-
prepublishOnly: "bun run sync-peers && bun run build"
|
|
216604
|
-
},
|
|
216605
|
-
type: "module"
|
|
216606
|
-
};
|
|
216763
|
+
|
|
216764
|
+
// src/index.ts
|
|
216765
|
+
init_package();
|
|
216607
216766
|
|
|
216608
216767
|
// src/commands/app.ts
|
|
216609
216768
|
init_manifest();
|
|
216610
216769
|
init_config();
|
|
216611
216770
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
216612
|
-
import { cpSync as cpSync4, existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync17, readdirSync as
|
|
216771
|
+
import { cpSync as cpSync4, existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync17, readdirSync as readdirSync10, rmSync as rmSync7, statSync as statSync8, writeFileSync as writeFileSync16 } from "node:fs";
|
|
216613
216772
|
import { homedir as homedir7 } from "node:os";
|
|
216614
216773
|
import { basename as basename8, dirname as dirname12, join as join21, resolve as resolve8 } from "node:path";
|
|
216615
216774
|
|
|
@@ -216767,6 +216926,11 @@ var semverNewer = (a, b) => {
|
|
|
216767
216926
|
return (pb[i] ?? 0) - (pa[i] ?? 0);
|
|
216768
216927
|
return 0;
|
|
216769
216928
|
};
|
|
216929
|
+
var newestForPlatform = (versions, key) => {
|
|
216930
|
+
const carrying = versions.filter((v) => v.files[key]).sort((a, b) => semverNewer(a.version, b.version));
|
|
216931
|
+
const newest = carrying[0];
|
|
216932
|
+
return newest ? { version: newest.version, file: newest.files[key] } : null;
|
|
216933
|
+
};
|
|
216770
216934
|
var fetchReleaseProduct = async (product, label) => {
|
|
216771
216935
|
let manifest;
|
|
216772
216936
|
try {
|
|
@@ -216871,12 +217035,15 @@ var installedMatcVersions = () => {
|
|
|
216871
217035
|
}
|
|
216872
217036
|
};
|
|
216873
217037
|
var fetchMatcManifest = () => fetchReleaseProduct("matc", "matc (shader compiler)");
|
|
216874
|
-
var
|
|
217038
|
+
var pickMatc = (versions) => {
|
|
216875
217039
|
const key = platformKey();
|
|
216876
|
-
const
|
|
216877
|
-
if (!
|
|
216878
|
-
throw new CliError(`matc
|
|
217040
|
+
const hit = newestForPlatform(versions, key);
|
|
217041
|
+
if (!hit) {
|
|
217042
|
+
throw new CliError(`matc isn't published for ${key} — push the project once so shaders compile on the ` + `server, or set LECODES_MATC to a locally built matc.`);
|
|
216879
217043
|
}
|
|
217044
|
+
return hit;
|
|
217045
|
+
};
|
|
217046
|
+
var downloadMatc = async (version, file, dest) => {
|
|
216880
217047
|
note(`Downloading the shader compiler (matc ${version}, ${(file.size / 1024 / 1024).toFixed(1)} MB)…`);
|
|
216881
217048
|
await downloadReleaseArchive(file, {
|
|
216882
217049
|
cacheRoot: cacheRoot(),
|
|
@@ -216898,24 +217065,26 @@ var resolveMatcExe = async () => {
|
|
|
216898
217065
|
const cached = resolveMatcExeCached();
|
|
216899
217066
|
if (cached)
|
|
216900
217067
|
return cached;
|
|
216901
|
-
const {
|
|
216902
|
-
const
|
|
216903
|
-
|
|
217068
|
+
const { versions } = await fetchMatcManifest();
|
|
217069
|
+
const { version, file } = pickMatc(versions);
|
|
217070
|
+
const dir = join12(cacheRoot(), `matc-${version}`);
|
|
217071
|
+
await downloadMatc(version, file, dir);
|
|
216904
217072
|
const exe = findMatc(dir);
|
|
216905
217073
|
if (!exe)
|
|
216906
217074
|
throw new CliError(`matc cache at ${dir} is broken — delete the folder and retry.`);
|
|
216907
217075
|
return exe;
|
|
216908
217076
|
};
|
|
216909
217077
|
var updateMatc = async () => {
|
|
216910
|
-
const {
|
|
217078
|
+
const { versions } = await fetchMatcManifest();
|
|
217079
|
+
const { version, file } = pickMatc(versions);
|
|
216911
217080
|
const before = installedMatcVersions()[0];
|
|
216912
|
-
const dir = join12(cacheRoot(), `matc-${
|
|
217081
|
+
const dir = join12(cacheRoot(), `matc-${version}`);
|
|
216913
217082
|
if (existsSync10(dir) && findMatc(dir) !== null) {
|
|
216914
|
-
note(`Already up to date: matc ${
|
|
217083
|
+
note(`Already up to date: matc ${version}.`);
|
|
216915
217084
|
return;
|
|
216916
217085
|
}
|
|
216917
|
-
await downloadMatc(
|
|
216918
|
-
note(before ? `matc updated to ${
|
|
217086
|
+
await downloadMatc(version, file, dir);
|
|
217087
|
+
note(before ? `matc updated to ${version} (was ${before}).` : `matc ${version} installed.`);
|
|
216919
217088
|
};
|
|
216920
217089
|
|
|
216921
217090
|
// src/compile/shaders.ts
|
|
@@ -217036,12 +217205,13 @@ init_manifest();
|
|
|
217036
217205
|
init_ignore();
|
|
217037
217206
|
init_util();
|
|
217038
217207
|
import { execFileSync } from "node:child_process";
|
|
217039
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "node:fs";
|
|
217208
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync8, readdirSync as readdirSync5, readFileSync as readFileSync13, statSync as statSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
217040
217209
|
import { homedir as homedir4 } from "node:os";
|
|
217041
217210
|
import { dirname as dirname8, join as join15, resolve as resolve2 } from "node:path";
|
|
217042
217211
|
var FIRST_PARTY_PLUGINS = {
|
|
217043
217212
|
camera: "letary/lecodes-plugins",
|
|
217044
217213
|
"qr-scanner": "letary/lecodes-plugins",
|
|
217214
|
+
map: "letary/lecodes-plugins",
|
|
217045
217215
|
geolocation: "letary/lecodes-plugins",
|
|
217046
217216
|
push: "letary/lecodes-plugins"
|
|
217047
217217
|
};
|
|
@@ -217072,6 +217242,7 @@ var desktopDefaultViewport = (root) => {
|
|
|
217072
217242
|
height: cfg.desktop.window?.height ?? DESKTOP_DEFAULT_WINDOW.height
|
|
217073
217243
|
};
|
|
217074
217244
|
};
|
|
217245
|
+
var swiftPackageName = (url) => (url.replace(/\/+$/, "").split("/").pop() ?? url).replace(/\.git$/, "");
|
|
217075
217246
|
var readJson = (path) => {
|
|
217076
217247
|
if (!existsSync13(path))
|
|
217077
217248
|
return null;
|
|
@@ -217225,7 +217396,8 @@ var ensureRepo = (repo, wantTag, lock) => {
|
|
|
217225
217396
|
git(cache, "checkout", "--quiet", "--detach", commit);
|
|
217226
217397
|
return { dir: cache, tag: tag ?? "unknown", commit };
|
|
217227
217398
|
};
|
|
217228
|
-
var
|
|
217399
|
+
var PLUGIN_PLATFORM_LABEL = { ios: "iOS", android: "Android", desktop: "desktop" };
|
|
217400
|
+
function resolvePlugin(id, spec, lock, platform, opts) {
|
|
217229
217401
|
let repo;
|
|
217230
217402
|
let subdir;
|
|
217231
217403
|
let wantTag = null;
|
|
@@ -217249,7 +217421,9 @@ var resolvePlugin = (id, spec, lock, platform) => {
|
|
|
217249
217421
|
if (manifest.id !== id)
|
|
217250
217422
|
throw new CliError(`Plugin "${id}": manifest declares id "${manifest.id}"`);
|
|
217251
217423
|
if (!manifest[platform]) {
|
|
217252
|
-
|
|
217424
|
+
if (opts?.optional)
|
|
217425
|
+
return null;
|
|
217426
|
+
throw new CliError(`Plugin "${id}" does not support ${PLUGIN_PLATFORM_LABEL[platform]} (manifest "${platform}" is null)`);
|
|
217253
217427
|
}
|
|
217254
217428
|
if (spec.startsWith("github:")) {
|
|
217255
217429
|
const names = [...manifest.provides?.views ?? [], ...manifest.provides?.services ?? []];
|
|
@@ -217264,7 +217438,7 @@ var resolvePlugin = (id, spec, lock, platform) => {
|
|
|
217264
217438
|
manifest,
|
|
217265
217439
|
lock: { repo, tag: checkout.tag, commit: checkout.commit, version: manifest.version ?? "unknown" }
|
|
217266
217440
|
};
|
|
217267
|
-
}
|
|
217441
|
+
}
|
|
217268
217442
|
var latestPluginTag = (repo) => {
|
|
217269
217443
|
try {
|
|
217270
217444
|
return ensureRepo(repo, null, undefined).tag;
|
|
@@ -217272,6 +217446,55 @@ var latestPluginTag = (repo) => {
|
|
|
217272
217446
|
return null;
|
|
217273
217447
|
}
|
|
217274
217448
|
};
|
|
217449
|
+
var desktopPluginTarget = () => `${process.platform}-${process.arch}`;
|
|
217450
|
+
var isDesktopPluginLibrary = (name) => {
|
|
217451
|
+
const n = name.toLowerCase();
|
|
217452
|
+
return process.platform === "win32" ? n.endsWith(".dll") : n.endsWith(".so");
|
|
217453
|
+
};
|
|
217454
|
+
var desktopPluginLibraries = (root, opts = {}) => {
|
|
217455
|
+
const dirs = [];
|
|
217456
|
+
const files = [];
|
|
217457
|
+
const local = join15(root, "plugins");
|
|
217458
|
+
if (existsSync13(local) && statSync3(local).isDirectory()) {
|
|
217459
|
+
dirs.push(local);
|
|
217460
|
+
for (const f2 of readdirSync5(local))
|
|
217461
|
+
if (isDesktopPluginLibrary(f2))
|
|
217462
|
+
files.push(join15(local, f2));
|
|
217463
|
+
}
|
|
217464
|
+
const config = readJson(join15(root, "app.json"));
|
|
217465
|
+
const specs = Object.entries(config?.plugins ?? {});
|
|
217466
|
+
if (specs.length === 0)
|
|
217467
|
+
return { dirs, files };
|
|
217468
|
+
const lock = readJson(join15(root, "plugins.lock.json"))?.plugins ?? {};
|
|
217469
|
+
const target = desktopPluginTarget();
|
|
217470
|
+
for (const [id, spec] of specs) {
|
|
217471
|
+
let plugin;
|
|
217472
|
+
try {
|
|
217473
|
+
plugin = resolvePlugin(id, spec, lock[id], "desktop", { optional: true });
|
|
217474
|
+
} catch (e) {
|
|
217475
|
+
if (opts.strict)
|
|
217476
|
+
throw e;
|
|
217477
|
+
warn(`Plugin "${id}": ${e.message} — skipped for this desktop run`);
|
|
217478
|
+
continue;
|
|
217479
|
+
}
|
|
217480
|
+
const rel = plugin?.manifest.desktop?.library?.[target];
|
|
217481
|
+
if (!plugin || !rel)
|
|
217482
|
+
continue;
|
|
217483
|
+
const abs = resolve2(plugin.dir, rel);
|
|
217484
|
+
if (!existsSync13(abs)) {
|
|
217485
|
+
const msg = `Plugin "${id}": desktop library ${abs} is missing (manifest desktop.library["${target}"])`;
|
|
217486
|
+
if (opts.strict)
|
|
217487
|
+
throw new CliError(msg);
|
|
217488
|
+
warn(`${msg} — skipped for this desktop run`);
|
|
217489
|
+
continue;
|
|
217490
|
+
}
|
|
217491
|
+
files.push(abs);
|
|
217492
|
+
const dir = dirname8(abs);
|
|
217493
|
+
if (!dirs.includes(dir))
|
|
217494
|
+
dirs.push(dir);
|
|
217495
|
+
}
|
|
217496
|
+
return { dirs, files };
|
|
217497
|
+
};
|
|
217275
217498
|
|
|
217276
217499
|
// src/commands/compile.ts
|
|
217277
217500
|
init_util();
|
|
@@ -218076,13 +218299,31 @@ var APP_ICON_JSON = `{
|
|
|
218076
218299
|
`;
|
|
218077
218300
|
var runtimePackageTemplate = (sdkVersion, variant, plugins) => {
|
|
218078
218301
|
const product = `LeCodesSDK-${variant}`;
|
|
218079
|
-
const
|
|
218302
|
+
const packages = new Map;
|
|
218303
|
+
for (const p of plugins) {
|
|
218304
|
+
for (const pkg of p.packages ?? []) {
|
|
218305
|
+
const prior = packages.get(pkg.url);
|
|
218306
|
+
if (prior && (prior.exact !== pkg.exact || prior.from !== pkg.from)) {
|
|
218307
|
+
throw new Error(`Plugin "${p.id}" pins ${pkg.url} at ${pkg.exact ?? `from ${pkg.from}`} but another plugin pins ${prior.exact ?? `from ${prior.from}`} — the shell can link one version.`);
|
|
218308
|
+
}
|
|
218309
|
+
packages.set(pkg.url, pkg);
|
|
218310
|
+
}
|
|
218311
|
+
}
|
|
218312
|
+
const packageDecls = [...packages.values()].map((pkg) => `
|
|
218313
|
+
.package(url: "${pkg.url}", ${pkg.exact ? `exact: "${pkg.exact}"` : `from: "${pkg.from}"`}),`).join("");
|
|
218314
|
+
const pluginTargets = plugins.map((p) => {
|
|
218315
|
+
const deps = [
|
|
218316
|
+
`.product(name: "${product}", package: "lecodes-ios-sdk")`,
|
|
218317
|
+
...(p.packages ?? []).flatMap((pkg) => pkg.products.map((name) => `.product(name: "${name}", package: "${swiftPackageName(pkg.url)}")`))
|
|
218318
|
+
];
|
|
218319
|
+
return `
|
|
218080
218320
|
// plugin: ${p.id} (vendored — see plugins.lock.json)
|
|
218081
218321
|
.target(
|
|
218082
218322
|
name: "${p.module}",
|
|
218083
|
-
dependencies: [.
|
|
218323
|
+
dependencies: [${deps.join(", ")}],
|
|
218084
218324
|
path: "Sources/${p.module}"
|
|
218085
|
-
)
|
|
218325
|
+
),`;
|
|
218326
|
+
}).join("");
|
|
218086
218327
|
const runtimeDeps = [`.product(name: "${product}", package: "lecodes-ios-sdk")`, ...plugins.map((p) => `"${p.module}"`)];
|
|
218087
218328
|
return `// swift-tools-version:5.9
|
|
218088
218329
|
//
|
|
@@ -218101,7 +218342,7 @@ let package = Package(
|
|
|
218101
218342
|
.library(name: "LeCodesRuntime", targets: ["LeCodesRuntime"]),
|
|
218102
218343
|
],
|
|
218103
218344
|
dependencies: [
|
|
218104
|
-
.package(url: "https://github.com/letary/lecodes-ios-sdk.git", exact: "${sdkVersion}")
|
|
218345
|
+
.package(url: "https://github.com/letary/lecodes-ios-sdk.git", exact: "${sdkVersion}"),${packageDecls}
|
|
218105
218346
|
],
|
|
218106
218347
|
targets: [
|
|
218107
218348
|
.target(
|
|
@@ -218247,7 +218488,7 @@ buildNumber) — edit it there, then re-run \`lecodes app sync\`. Plugins are ma
|
|
|
218247
218488
|
init_manifest();
|
|
218248
218489
|
import { execFileSync as execFileSync2, spawn, spawnSync as spawnSync3 } from "node:child_process";
|
|
218249
218490
|
import { createHash as createHash6 } from "node:crypto";
|
|
218250
|
-
import { chmodSync, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync15, readdirSync as
|
|
218491
|
+
import { chmodSync, cpSync, existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync15, readdirSync as readdirSync6, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync4, writeFileSync as writeFileSync12 } from "node:fs";
|
|
218251
218492
|
import { homedir as homedir5 } from "node:os";
|
|
218252
218493
|
import { basename as basename5, dirname as dirname10, join as join17 } from "node:path";
|
|
218253
218494
|
init_util();
|
|
@@ -219494,7 +219735,7 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
219494
219735
|
}
|
|
219495
219736
|
const namespace = register.split(".").slice(0, -1).join(".");
|
|
219496
219737
|
const libsDir = join17(dest, "libs");
|
|
219497
|
-
const aarNames = existsSync16(libsDir) ?
|
|
219738
|
+
const aarNames = existsSync16(libsDir) ? readdirSync6(libsDir).filter((f3) => f3.endsWith(".aar")) : [];
|
|
219498
219739
|
for (const aar of aarNames) {
|
|
219499
219740
|
const base = aar.replace(/\.aar$/, "");
|
|
219500
219741
|
const wrapDir = join17(libsDir, base);
|
|
@@ -219510,8 +219751,8 @@ var syncAndroid = async (root, config, prior) => {
|
|
|
219510
219751
|
log(`${c.green("plugin")} ${id}@${plugin.lock.version} (${plugin.lock.tag} → plugins/${module})`);
|
|
219511
219752
|
}
|
|
219512
219753
|
const keep = new Set(runtimePlugins.map((p2) => p2.module));
|
|
219513
|
-
for (const entry of existsSync16(pluginsDir) ?
|
|
219514
|
-
if (!keep.has(entry) &&
|
|
219754
|
+
for (const entry of existsSync16(pluginsDir) ? readdirSync6(pluginsDir) : []) {
|
|
219755
|
+
if (!keep.has(entry) && statSync4(join17(pluginsDir, entry)).isDirectory()) {
|
|
219515
219756
|
rmrf(join17(pluginsDir, entry));
|
|
219516
219757
|
log(`${c.yellow("removed")} android/plugins/${entry} (plugin uninstalled)`);
|
|
219517
219758
|
}
|
|
@@ -219676,7 +219917,7 @@ var buildAndroid = async (args) => {
|
|
|
219676
219917
|
for (const dir of [apkDir, aabDir]) {
|
|
219677
219918
|
if (!existsSync16(dir))
|
|
219678
219919
|
continue;
|
|
219679
|
-
for (const f3 of
|
|
219920
|
+
for (const f3 of readdirSync6(dir)) {
|
|
219680
219921
|
if (f3.endsWith(".apk") || f3.endsWith(".aab"))
|
|
219681
219922
|
outputs.push(join17(dir, f3));
|
|
219682
219923
|
}
|
|
@@ -219733,7 +219974,7 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
219733
219974
|
const validVariant = requestedVariant === "auto" || SDK_VARIANTS.includes(requestedVariant);
|
|
219734
219975
|
check(validVariant, `android.variant "${requestedVariant}"`, `use "auto" or one of ${SDK_VARIANTS.join(", ")}`);
|
|
219735
219976
|
const wrapperJar = join17(androidDir, "gradle", "wrapper", "gradle-wrapper.jar");
|
|
219736
|
-
check(existsSync16(wrapperJar) &&
|
|
219977
|
+
check(existsSync16(wrapperJar) && statSync4(wrapperJar).size > 0, "Gradle wrapper jar", `run \`gradle wrapper --gradle-version ${GRADLE_VERSION}\` in android/ (or let Android Studio repair it)`);
|
|
219737
219978
|
check(existsSync16(join17(androidDir, "app", "src", "main", "assets", "app.js")), "Bundled app.js", "run `lecodes app sync`");
|
|
219738
219979
|
const linked = shellSdkVersionAndroid(androidDir);
|
|
219739
219980
|
const pin = config.android?.sdk ?? "latest";
|
|
@@ -219756,14 +219997,14 @@ var doctorAndroidChecks = async (root, config, check) => {
|
|
|
219756
219997
|
|
|
219757
219998
|
// src/commands/appDesktop.ts
|
|
219758
219999
|
import { execFileSync as execFileSync5, spawn as spawn4 } from "node:child_process";
|
|
219759
|
-
import { cpSync as cpSync3, existsSync as existsSync19, mkdirSync as mkdirSync13, readdirSync as
|
|
220000
|
+
import { cpSync as cpSync3, existsSync as existsSync19, mkdirSync as mkdirSync13, readdirSync as readdirSync9, rmSync as rmSync6, statSync as statSync7, writeFileSync as writeFileSync15 } from "node:fs";
|
|
219760
220001
|
import { basename as basename7, dirname as dirname11, join as join20, resolve as resolve7 } from "node:path";
|
|
219761
220002
|
|
|
219762
220003
|
// src/desktopRenderer.ts
|
|
219763
220004
|
import { execFileSync as execFileSync3, spawn as spawn2, spawnSync as spawnSync4 } from "node:child_process";
|
|
219764
|
-
import { copyFileSync, existsSync as existsSync17, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync2, readdirSync as
|
|
220005
|
+
import { copyFileSync, existsSync as existsSync17, mkdirSync as mkdirSync11, mkdtempSync as mkdtempSync2, readdirSync as readdirSync7, readFileSync as readFileSync16, rmSync as rmSync4, statSync as statSync5, writeFileSync as writeFileSync13 } from "node:fs";
|
|
219765
220006
|
import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
|
|
219766
|
-
import { join as join18, resolve as resolve5 } from "node:path";
|
|
220007
|
+
import { delimiter, join as join18, resolve as resolve5 } from "node:path";
|
|
219767
220008
|
init_util();
|
|
219768
220009
|
|
|
219769
220010
|
// ../viewer-lite/src/sourcemapError.ts
|
|
@@ -219883,7 +220124,7 @@ var isMac = () => process.platform === "darwin";
|
|
|
219883
220124
|
var exeName = () => process.platform === "win32" ? "lecodes-desktop.exe" : isMac() ? "lecodes-app" : "lecodes-desktop";
|
|
219884
220125
|
var findExe = (dir) => {
|
|
219885
220126
|
const name = exeName();
|
|
219886
|
-
for (const entry of
|
|
220127
|
+
for (const entry of readdirSync7(dir, { withFileTypes: true })) {
|
|
219887
220128
|
const p2 = join18(dir, entry.name);
|
|
219888
220129
|
if (entry.isFile() && entry.name === name)
|
|
219889
220130
|
return p2;
|
|
@@ -219899,7 +220140,7 @@ var cacheRoot2 = () => join18(homedir6(), ".lecodes", "renderer");
|
|
|
219899
220140
|
var currentFile = () => join18(cacheRoot2(), "current.json");
|
|
219900
220141
|
var installedDesktopVersions = () => {
|
|
219901
220142
|
try {
|
|
219902
|
-
return
|
|
220143
|
+
return readdirSync7(cacheRoot2(), { withFileTypes: true }).filter((e2) => e2.isDirectory() && e2.name.startsWith("desktop-")).map((e2) => e2.name.slice("desktop-".length)).filter((v) => findExe(join18(cacheRoot2(), `desktop-${v}`)) !== null).sort(semverNewer);
|
|
219903
220144
|
} catch {
|
|
219904
220145
|
return [];
|
|
219905
220146
|
}
|
|
@@ -219924,7 +220165,7 @@ var extractDmg = (archive, stage) => {
|
|
|
219924
220165
|
mkdirSync11(mount, { recursive: true });
|
|
219925
220166
|
execFileSync3("hdiutil", ["attach", "-nobrowse", "-readonly", "-quiet", "-mountpoint", mount, archive]);
|
|
219926
220167
|
try {
|
|
219927
|
-
const app =
|
|
220168
|
+
const app = readdirSync7(mount).find((n2) => n2.endsWith(".app"));
|
|
219928
220169
|
if (!app)
|
|
219929
220170
|
throw new CliError("The downloaded DMG holds no .app bundle.");
|
|
219930
220171
|
execFileSync3("cp", ["-R", join18(mount, app), join18(stage, app)]);
|
|
@@ -220045,8 +220286,12 @@ var updateDesktop = async () => {
|
|
|
220045
220286
|
else
|
|
220046
220287
|
note(`Desktop renderer set to ${latest} (was ${before}).`);
|
|
220047
220288
|
};
|
|
220289
|
+
var pluginEnv = (dirs) => {
|
|
220290
|
+
const all = [...dirs ?? [], ...process.env.CREATOR_PLUGIN_DIRS ? [process.env.CREATOR_PLUGIN_DIRS] : []];
|
|
220291
|
+
return all.length > 0 ? { CREATOR_PLUGIN_DIRS: all.join(delimiter) } : {};
|
|
220292
|
+
};
|
|
220048
220293
|
var windowEnv = (opts) => {
|
|
220049
|
-
const env = {};
|
|
220294
|
+
const env = { ...pluginEnv(opts.pluginDirs) };
|
|
220050
220295
|
const width = opts.width ?? (opts.height !== undefined ? 1280 : undefined);
|
|
220051
220296
|
const height = opts.height ?? (opts.width !== undefined ? 720 : undefined);
|
|
220052
220297
|
if (width && height)
|
|
@@ -220055,6 +220300,8 @@ var windowEnv = (opts) => {
|
|
|
220055
220300
|
env.CREATOR_MIN_WINDOW_SIZE = `${opts.minWidth}x${opts.minHeight}`;
|
|
220056
220301
|
if (opts.title)
|
|
220057
220302
|
env.CREATOR_WINDOW_TITLE = opts.title;
|
|
220303
|
+
if (opts.storageScope)
|
|
220304
|
+
env.CREATOR_STORAGE_SCOPE = opts.storageScope;
|
|
220058
220305
|
if (opts.resizable === false)
|
|
220059
220306
|
env.CREATOR_WINDOW_FIXED = "1";
|
|
220060
220307
|
if (opts.fullscreen !== undefined)
|
|
@@ -220070,6 +220317,12 @@ var windowEnv = (opts) => {
|
|
|
220070
220317
|
}
|
|
220071
220318
|
if (opts.fps)
|
|
220072
220319
|
env.CREATOR_FPS = "1";
|
|
220320
|
+
if (opts.mouseEmulateTouch !== undefined && process.env.CREATOR_MOUSE_TOUCH === undefined) {
|
|
220321
|
+
env.CREATOR_MOUSE_TOUCH = opts.mouseEmulateTouch ? "1" : "0";
|
|
220322
|
+
}
|
|
220323
|
+
if (opts.virtualKeyboard !== undefined && process.env.CREATOR_VIRTUAL_KEYBOARD === undefined) {
|
|
220324
|
+
env.CREATOR_VIRTUAL_KEYBOARD = typeof opts.virtualKeyboard === "boolean" ? opts.virtualKeyboard ? "1" : "0" : JSON.stringify(opts.virtualKeyboard);
|
|
220325
|
+
}
|
|
220073
220326
|
return env;
|
|
220074
220327
|
};
|
|
220075
220328
|
var netArgs = (net) => {
|
|
@@ -220090,7 +220343,7 @@ var stageBundle = (js, resources) => {
|
|
|
220090
220343
|
const runDir = mkdtempSync2(join18(tmpdir2(), "lecodes-render-"));
|
|
220091
220344
|
writeFileSync13(join18(runDir, "app.js"), js);
|
|
220092
220345
|
for (const r2 of resources) {
|
|
220093
|
-
if (r2.absPath && existsSync17(r2.absPath) &&
|
|
220346
|
+
if (r2.absPath && existsSync17(r2.absPath) && statSync5(r2.absPath).isFile()) {
|
|
220094
220347
|
copyFileSync(r2.absPath, join18(runDir, r2.name));
|
|
220095
220348
|
}
|
|
220096
220349
|
}
|
|
@@ -220182,6 +220435,7 @@ var runDesktopRender = async (js, resources, opts) => {
|
|
|
220182
220435
|
env.CREATOR_CAMERA = opts.camera;
|
|
220183
220436
|
if (opts.view)
|
|
220184
220437
|
env.CREATOR_VIEW = opts.view;
|
|
220438
|
+
Object.assign(env, pluginEnv(opts.pluginDirs));
|
|
220185
220439
|
const demangle = makeStackDemangler(js) ?? ((text) => text);
|
|
220186
220440
|
return await new Promise((done, fail) => {
|
|
220187
220441
|
const child = spawn2(exe, [appJs], { cwd: runDir, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -220240,13 +220494,13 @@ init_util();
|
|
|
220240
220494
|
// src/commands/appDesktopMac.ts
|
|
220241
220495
|
init_util();
|
|
220242
220496
|
import { execFileSync as execFileSync4, spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
220243
|
-
import { cpSync as cpSync2, existsSync as existsSync18, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readdirSync as
|
|
220497
|
+
import { cpSync as cpSync2, existsSync as existsSync18, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync3, readdirSync as readdirSync8, rmSync as rmSync5, statSync as statSync6, symlinkSync, writeFileSync as writeFileSync14 } from "node:fs";
|
|
220244
220498
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
220245
220499
|
import { basename as basename6, join as join19, resolve as resolve6 } from "node:path";
|
|
220246
220500
|
var mb = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
220247
|
-
var dirSize = (dir) =>
|
|
220501
|
+
var dirSize = (dir) => readdirSync8(dir, { withFileTypes: true }).reduce((sum, e2) => {
|
|
220248
220502
|
const p2 = join19(dir, e2.name);
|
|
220249
|
-
return sum + (e2.isDirectory() ? dirSize(p2) : e2.isSymbolicLink() ? 0 :
|
|
220503
|
+
return sum + (e2.isDirectory() ? dirSize(p2) : e2.isSymbolicLink() ? 0 : statSync6(p2).size);
|
|
220250
220504
|
}, 0);
|
|
220251
220505
|
var cleanBundleName = (name) => name.replace(/[/:]/g, "").trim() || "App";
|
|
220252
220506
|
var macHostAppOf = (exe) => {
|
|
@@ -220259,7 +220513,7 @@ var macHostAppOf = (exe) => {
|
|
|
220259
220513
|
var macHostProvidedNames = (exe) => {
|
|
220260
220514
|
const names3 = new Set;
|
|
220261
220515
|
const walk = (dir) => {
|
|
220262
|
-
for (const e2 of
|
|
220516
|
+
for (const e2 of readdirSync8(dir, { withFileTypes: true })) {
|
|
220263
220517
|
if (e2.isDirectory())
|
|
220264
220518
|
walk(join19(dir, e2.name));
|
|
220265
220519
|
else
|
|
@@ -220413,7 +220667,7 @@ ${(r2.stdout + r2.stderr).trim()}`);
|
|
|
220413
220667
|
if (signing.notaryProfile)
|
|
220414
220668
|
notarize(dmg, signing.notaryProfile, tmp, "disk image");
|
|
220415
220669
|
}
|
|
220416
|
-
success(`DMG: ${dmg} (${mb(
|
|
220670
|
+
success(`DMG: ${dmg} (${mb(statSync6(dmg).size)})`);
|
|
220417
220671
|
};
|
|
220418
220672
|
var gatekeeperVerdict = (app) => {
|
|
220419
220673
|
const r2 = spawnSync5("spctl", ["-a", "-t", "exec", "-vv", app], { encoding: "utf8" });
|
|
@@ -220514,12 +220768,12 @@ var buildMacApp = async (input) => {
|
|
|
220514
220768
|
writeFileSync14(join19(proj, "app.js"), js);
|
|
220515
220769
|
let assetBytes = 0, assetCount = 0;
|
|
220516
220770
|
for (const r2 of shipped) {
|
|
220517
|
-
if (!r2.absPath || !existsSync18(r2.absPath) || !
|
|
220771
|
+
if (!r2.absPath || !existsSync18(r2.absPath) || !statSync6(r2.absPath).isFile()) {
|
|
220518
220772
|
warn(`asset "${r2.name}" (${r2.path}) has no local file — skipped`);
|
|
220519
220773
|
continue;
|
|
220520
220774
|
}
|
|
220521
220775
|
cpSync2(r2.absPath, join19(proj, "assets", r2.name));
|
|
220522
|
-
assetBytes +=
|
|
220776
|
+
assetBytes += statSync6(r2.absPath).size;
|
|
220523
220777
|
assetCount++;
|
|
220524
220778
|
}
|
|
220525
220779
|
note(` assets: ${assetCount} files, ${mb(assetBytes)} → Contents/Resources/project/assets/`);
|
|
@@ -220567,7 +220821,7 @@ var buildMacApp = async (input) => {
|
|
|
220567
220821
|
const zip = join19(out, `${appName}.zip`);
|
|
220568
220822
|
rmSync5(zip, { force: true });
|
|
220569
220823
|
execFileSync4("ditto", ["-c", "-k", "--keepParent", app, zip]);
|
|
220570
|
-
success(`Zip: ${zip} (${mb(
|
|
220824
|
+
success(`Zip: ${zip} (${mb(statSync6(zip).size)})`);
|
|
220571
220825
|
} else if (!flagBool(args, "dmg")) {
|
|
220572
220826
|
note("Tip: --zip packs the .app for sending; --dmg wraps it in a drag-to-Applications image.");
|
|
220573
220827
|
}
|
|
@@ -220591,9 +220845,9 @@ or remove the quarantine flag in Terminal: xattr -dr com.apple.quarantine "` +
|
|
|
220591
220845
|
|
|
220592
220846
|
// src/commands/appDesktop.ts
|
|
220593
220847
|
var mb2 = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
220594
|
-
var dirSize2 = (dir) =>
|
|
220848
|
+
var dirSize2 = (dir) => readdirSync9(dir, { withFileTypes: true }).reduce((sum, e2) => {
|
|
220595
220849
|
const p2 = join20(dir, e2.name);
|
|
220596
|
-
return sum + (e2.isDirectory() ? dirSize2(p2) :
|
|
220850
|
+
return sum + (e2.isDirectory() ? dirSize2(p2) : statSync7(p2).size);
|
|
220597
220851
|
}, 0);
|
|
220598
220852
|
var initDesktop = async (args) => {
|
|
220599
220853
|
const root = projectRoot();
|
|
@@ -220622,7 +220876,7 @@ var buildDesktop = async (args) => {
|
|
|
220622
220876
|
const slug = flagStr(args, "slug") ?? cleanFileName(name);
|
|
220623
220877
|
const out = resolve7(root, flagStr(args, "o", "out") ?? join20(root, "dist", slug));
|
|
220624
220878
|
if (existsSync19(out)) {
|
|
220625
|
-
const entries =
|
|
220879
|
+
const entries = readdirSync9(out);
|
|
220626
220880
|
const looksLikeBuild = entries.length === 0 || entries.includes("app.js") || entries.some((e2) => e2.endsWith(".app"));
|
|
220627
220881
|
if (!looksLikeBuild) {
|
|
220628
220882
|
throw new CliError(`${out} exists and doesn't look like a previous desktop build — pick another -o or clear it yourself.`);
|
|
@@ -220659,7 +220913,7 @@ var buildDesktop = async (args) => {
|
|
|
220659
220913
|
const hostProvided = mac ? macHostProvidedNames(exe) : new Set;
|
|
220660
220914
|
if (!mac) {
|
|
220661
220915
|
try {
|
|
220662
|
-
for (const f3 of
|
|
220916
|
+
for (const f3 of readdirSync9(join20(hostDir, "assets")))
|
|
220663
220917
|
hostProvided.add(f3);
|
|
220664
220918
|
} catch {}
|
|
220665
220919
|
}
|
|
@@ -220675,7 +220929,7 @@ var buildDesktop = async (args) => {
|
|
|
220675
220929
|
const version = process.env.LECODES_DESKTOP_EXE ? "local build" : selectedDesktopVersion();
|
|
220676
220930
|
rmSync6(out, { recursive: true, force: true });
|
|
220677
220931
|
mkdirSync13(out, { recursive: true });
|
|
220678
|
-
for (const entry of
|
|
220932
|
+
for (const entry of readdirSync9(hostDir, { withFileTypes: true })) {
|
|
220679
220933
|
if (entry.isFile() && entry.name.toLowerCase().endsWith(".dll")) {
|
|
220680
220934
|
cpSync3(join20(hostDir, entry.name), join20(out, entry.name));
|
|
220681
220935
|
} else if (entry.isDirectory() && entry.name === "assets") {
|
|
@@ -220695,20 +220949,37 @@ var buildDesktop = async (args) => {
|
|
|
220695
220949
|
if (shipped.length > 0)
|
|
220696
220950
|
mkdirSync13(join20(out, ASSETS_DIR), { recursive: true });
|
|
220697
220951
|
for (const r2 of shipped) {
|
|
220698
|
-
if (!r2.absPath || !existsSync19(r2.absPath) || !
|
|
220952
|
+
if (!r2.absPath || !existsSync19(r2.absPath) || !statSync7(r2.absPath).isFile()) {
|
|
220699
220953
|
warn(`asset "${r2.name}" (${r2.path}) has no local file — skipped`);
|
|
220700
220954
|
continue;
|
|
220701
220955
|
}
|
|
220702
220956
|
cpSync3(r2.absPath, join20(out, ASSETS_DIR, r2.name));
|
|
220703
|
-
assetBytes +=
|
|
220957
|
+
assetBytes += statSync7(r2.absPath).size;
|
|
220704
220958
|
assetCount++;
|
|
220705
220959
|
}
|
|
220706
220960
|
note(` assets: ${assetCount} files, ${mb2(assetBytes)} → ${ASSETS_DIR}/`);
|
|
220707
|
-
|
|
220961
|
+
const desktop = config.desktop;
|
|
220962
|
+
writeJson(join20(out, "app.json"), {
|
|
220963
|
+
name,
|
|
220964
|
+
...desktop ? { desktop: {
|
|
220965
|
+
window: desktop.window,
|
|
220966
|
+
...desktop.mouseEmulateTouch !== undefined ? { mouseEmulateTouch: desktop.mouseEmulateTouch } : {},
|
|
220967
|
+
...desktop.virtualKeyboard !== undefined ? { virtualKeyboard: desktop.virtualKeyboard } : {}
|
|
220968
|
+
} } : {}
|
|
220969
|
+
});
|
|
220708
220970
|
if (config.icon) {
|
|
220709
220971
|
await writeIco(root, config, join20(out, "app.ico"));
|
|
220710
220972
|
note(" app.ico rendered from " + config.icon);
|
|
220711
220973
|
}
|
|
220974
|
+
{
|
|
220975
|
+
const { files } = desktopPluginLibraries(root, { strict: true });
|
|
220976
|
+
if (files.length > 0) {
|
|
220977
|
+
mkdirSync13(join20(out, "plugins"), { recursive: true });
|
|
220978
|
+
for (const f3 of files)
|
|
220979
|
+
cpSync3(f3, join20(out, "plugins", basename7(f3)));
|
|
220980
|
+
note(` plugins: ${files.map((f3) => basename7(f3)).join(", ")} → plugins/`);
|
|
220981
|
+
}
|
|
220982
|
+
}
|
|
220712
220983
|
writeFileSync15(join20(out, "README.txt"), "\uFEFF" + readmeText2(name, exeName2, version));
|
|
220713
220984
|
success(`Portable build: ${out} (${mb2(dirSize2(out))})`);
|
|
220714
220985
|
if (flagBool(args, "probe"))
|
|
@@ -220808,7 +221079,7 @@ var zipBuild = (out) => {
|
|
|
220808
221079
|
} else {
|
|
220809
221080
|
execFileSync5("zip", ["-rq", zip, basename7(out)], { cwd: dirname11(out) });
|
|
220810
221081
|
}
|
|
220811
|
-
success(`Zip: ${zip} (${mb2(
|
|
221082
|
+
success(`Zip: ${zip} (${mb2(statSync7(zip).size)})`);
|
|
220812
221083
|
};
|
|
220813
221084
|
var readmeText2 = (name, exeName2, version) => `${name}
|
|
220814
221085
|
${"=".repeat(Math.max(name.length, 4))}
|
|
@@ -221015,13 +221286,18 @@ var syncIos = async (root, config) => {
|
|
|
221015
221286
|
for (const [key, value] of Object.entries(ios.entitlements ?? {})) {
|
|
221016
221287
|
(entitlementValues[key] ??= []).push(value);
|
|
221017
221288
|
}
|
|
221018
|
-
|
|
221289
|
+
for (const pkg of ios.packages ?? []) {
|
|
221290
|
+
if (!pkg.url || !Array.isArray(pkg.products) || pkg.products.length === 0 || !pkg.exact && !pkg.from || pkg.exact && pkg.from) {
|
|
221291
|
+
throw new CliError(`Plugin "${id}": manifest ios.packages entries need { url, exact | from, products: [...] } — got ${JSON.stringify(pkg)}`);
|
|
221292
|
+
}
|
|
221293
|
+
}
|
|
221294
|
+
runtimePlugins.push({ id, module, register, packages: ios.packages });
|
|
221019
221295
|
newLock[id] = plugin.lock;
|
|
221020
221296
|
log(`${c.green("plugin")} ${id}@${plugin.lock.version} (${plugin.lock.tag} → Sources/${module})`);
|
|
221021
221297
|
}
|
|
221022
221298
|
const keep = new Set(["LeCodesRuntime", ...runtimePlugins.map((p2) => p2.module)]);
|
|
221023
|
-
for (const entry of existsSync20(sourcesDir) ?
|
|
221024
|
-
if (!keep.has(entry) &&
|
|
221299
|
+
for (const entry of existsSync20(sourcesDir) ? readdirSync10(sourcesDir) : []) {
|
|
221300
|
+
if (!keep.has(entry) && statSync8(join21(sourcesDir, entry)).isDirectory()) {
|
|
221025
221301
|
rmSync7(join21(sourcesDir, entry), { recursive: true });
|
|
221026
221302
|
log(`${c.yellow("removed")} ios/LeCodesRuntime/Sources/${entry} (plugin uninstalled)`);
|
|
221027
221303
|
}
|
|
@@ -221049,7 +221325,7 @@ var syncIos = async (root, config) => {
|
|
|
221049
221325
|
if (variant === "3d" || variant === "full") {
|
|
221050
221326
|
const sdkResources = await ensureSdkResourcesBundle(sdkVersion);
|
|
221051
221327
|
cpSync4(sdkResources, join21(resourcesDir, "LeCodesSDKResources.bundle"), { recursive: true });
|
|
221052
|
-
for (const f3 of
|
|
221328
|
+
for (const f3 of readdirSync10(sdkResources))
|
|
221053
221329
|
if (f3 !== "Info.plist")
|
|
221054
221330
|
sdkProvided.add(f3);
|
|
221055
221331
|
log(`${c.green("resources")} LeCodesSDKResources.bundle (filament) → ios/App/Resources/`);
|
|
@@ -221387,7 +221663,7 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
221387
221663
|
check(xcodeVersion !== "", `Xcode CLI (${xcodeVersion || "not found"})`, "install Xcode + command line tools");
|
|
221388
221664
|
check(!!config.ios.teamId, "Development team set", "lecodes app init wrote app.json — fill ios.teamId", false);
|
|
221389
221665
|
const iconDir = join21(root, "ios", "App", "Assets.xcassets", "AppIcon.appiconset");
|
|
221390
|
-
const hasIcon = existsSync20(iconDir) &&
|
|
221666
|
+
const hasIcon = existsSync20(iconDir) && readdirSync10(iconDir).some((f3) => f3.endsWith(".png"));
|
|
221391
221667
|
check(hasIcon, "App icon present", "drop a 1024×1024 PNG into ios/App/Assets.xcassets/AppIcon.appiconset/ (App Store requires it)", false);
|
|
221392
221668
|
const bundle = join21(root, "ios", "App", "Resources", "app.js");
|
|
221393
221669
|
check(existsSync20(bundle), "Bundled app.js", "run `lecodes app sync`");
|
|
@@ -221430,7 +221706,7 @@ var doctorIosChecks = async (root, config, check) => {
|
|
|
221430
221706
|
const plist = readFileSync17(join21(root, "ios", "App", "Info.plist"), "utf8");
|
|
221431
221707
|
check(plist.includes("<key>LeCodesUpdateURL</key>"), "OTA update URL in Info.plist", "run `lecodes app sync`", false);
|
|
221432
221708
|
const appDir = join21(root, "ios", "App");
|
|
221433
|
-
const callsUpdater =
|
|
221709
|
+
const callsUpdater = readdirSync10(appDir).some((f3) => f3.endsWith(".swift") && readFileSync17(join21(appDir, f3), "utf8").includes("LeCodesUpdater.activeBundle"));
|
|
221434
221710
|
check(callsUpdater, "App code launches via LeCodesUpdater", `no App/*.swift calls LeCodesUpdater.activeBundle() — downloaded updates would never run; see "Over-the-air updates" in ios/README.md for the ViewController snippet`, false);
|
|
221435
221711
|
}
|
|
221436
221712
|
const asc = readJson(join21(homedir7(), ".lecodes", "asc.json"));
|
|
@@ -221583,7 +221859,7 @@ var app = async (args) => {
|
|
|
221583
221859
|
init_api();
|
|
221584
221860
|
init_config();
|
|
221585
221861
|
init_manifest();
|
|
221586
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync16, readdirSync as
|
|
221862
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync16, readdirSync as readdirSync12 } from "node:fs";
|
|
221587
221863
|
import { resolve as resolve9 } from "node:path";
|
|
221588
221864
|
init_types4();
|
|
221589
221865
|
init_ignore();
|
|
@@ -221606,7 +221882,7 @@ var clone3 = async (args) => {
|
|
|
221606
221882
|
const project = await getProject(apiUrl, token, uuid);
|
|
221607
221883
|
const dir = args._[1] ?? slug(project.name);
|
|
221608
221884
|
const root = resolve9(dir);
|
|
221609
|
-
if (existsSync22(root) &&
|
|
221885
|
+
if (existsSync22(root) && readdirSync12(root).length > 0) {
|
|
221610
221886
|
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
221611
221887
|
}
|
|
221612
221888
|
mkdirSync16(root, { recursive: true });
|
|
@@ -221632,7 +221908,7 @@ init_config();
|
|
|
221632
221908
|
init_manifest();
|
|
221633
221909
|
init_types4();
|
|
221634
221910
|
init_ignore();
|
|
221635
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync17, readdirSync as
|
|
221911
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync17, readdirSync as readdirSync13, writeFileSync as writeFileSync18 } from "node:fs";
|
|
221636
221912
|
import { join as join23, resolve as resolve10 } from "node:path";
|
|
221637
221913
|
|
|
221638
221914
|
// src/commands/projectTemplates.ts
|
|
@@ -221648,14 +221924,14 @@ from \`main.ts\`.
|
|
|
221648
221924
|
|
|
221649
221925
|
\`\`\`
|
|
221650
221926
|
lecodes render # semantic JSON of the launched UI (structure, text, names, values)
|
|
221651
|
-
lecodes render --png shot.png # pixels (
|
|
221927
|
+
lecodes render --png shot.png # pixels (the renderer + @napi-rs/canvas install themselves on first use; --yes skips the prompt)
|
|
221652
221928
|
lecodes render --png card.png --clip card # ONE component/region: a node name or x,y,w,h
|
|
221653
221929
|
lecodes render src/screens/cart.ts # ONE screen module, without booting the app: any file that
|
|
221654
221930
|
# \`export default\`s a UIScreen (or a (state) => UIScreen —
|
|
221655
221931
|
# --state <name> picks the branch). Same flags as above.
|
|
221656
221932
|
lecodes render --desktop --png shot.png # NATIVE render: real Filament 3D + UI + 2D composited —
|
|
221657
221933
|
# use this to SEE 3D scenes / procedural geometry (headless
|
|
221658
|
-
# renders record 3D as JSON
|
|
221934
|
+
# renders record 3D as JSON — simulated, not drawn). Windows/Linux; downloads
|
|
221659
221935
|
# the renderer on first use. --frames <n> for slow loads.
|
|
221660
221936
|
lecodes render --script <file> # drive the app through a scenario (format below)
|
|
221661
221937
|
lecodes render --desktop --view iso --png iso.png # 3D: inspection camera fitted to the WHOLE
|
|
@@ -221668,6 +221944,11 @@ lecodes render --desktop --script <file> # the same scenario on the NATIVE host
|
|
|
221668
221944
|
lecodes test # all tests/*.flow.json + design-derived flows; run before "done"
|
|
221669
221945
|
lecodes test --desktop # the file flows on the NATIVE host (3D/Jolt/real GPU; coordinate
|
|
221670
221946
|
# flows only — design flows stay headless)
|
|
221947
|
+
lecodes dev --android --time 12 --png phone.png # the REAL phone over adb (USB): launches the app on
|
|
221948
|
+
# the dev url via adb reverse, streams device logs as [device:log],
|
|
221949
|
+
# screenshots after 12 s, force-stops, exit 1 on a crash. --serial
|
|
221950
|
+
# <id> with several devices, --apk <file> to install a build first.
|
|
221951
|
+
# Without --time it runs until Ctrl+C (which stops the app).
|
|
221671
221952
|
\`\`\`
|
|
221672
221953
|
|
|
221673
221954
|
Iterate with \`--script\` scenarios and keep the useful ones as \`tests/<name>.flow.json\` — they
|
|
@@ -221704,8 +221985,9 @@ advance in fixed 1/60 steps driven by \`wait\`/\`frames\`/\`drag\`/\`key\` — n
|
|
|
221704
221985
|
so a flow that passes once passes every time. \`Input.key(code)\` reads the keys a flow holds and
|
|
221705
221986
|
\`Input.on('keydown' | 'keyup', e => e.code)\` fires for each \`key\`/\`keyDown\`/\`keyUp\` step
|
|
221706
221987
|
(events land before the next loop tick); \`stick\` values are what \`Input.gamepad(0).axis()\` reads.
|
|
221707
|
-
Headless draws UI + the 2D scene (sprites, tilemaps, Box2D); 3D is captured as JSON
|
|
221708
|
-
|
|
221988
|
+
Headless draws UI + the 2D scene (sprites, tilemaps, Box2D); 3D is captured as JSON with REAL Jolt
|
|
221989
|
+
physics (bodies fall, characters collide, triggers fire, ragdolls drop — a character with no ground
|
|
221990
|
+
under it falls, like on device) — use \`--desktop\` for 3D pixels. \`console.log\` + \`expect.log\` is the assertion
|
|
221709
221991
|
channel for game state that has no UI.
|
|
221710
221992
|
|
|
221711
221993
|
## Make the app testable
|
|
@@ -221721,6 +222003,13 @@ channel for game state that has no UI.
|
|
|
221721
222003
|
with \`"fixtures": "fixtures.json"\` in its envelope.
|
|
221722
222004
|
- \`fill\` fills every empty input — use it before tapping a disabled-until-filled submit.
|
|
221723
222005
|
|
|
222006
|
+
## 3D scenes
|
|
222007
|
+
|
|
222008
|
+
- A HUD over a \`Scene\` is a widget ATTACHED to it: \`const hud = UIWidget(...).style({ left: 16,
|
|
222009
|
+
bottom: 16, pointerEvents: "none" }); hud.attachTo(scene); hud.show()\`. Never a \`UIScreen\`:
|
|
222010
|
+
a screen is a destination, so \`UIScreen(...).open()\` after \`scene.open()\` CLOSES the scene
|
|
222011
|
+
(the host logs "3D scene closed" and you see only the screen) — no background makes it transparent.
|
|
222012
|
+
|
|
221724
222013
|
## SDK documentation
|
|
221725
222014
|
|
|
221726
222015
|
\`.lecodes/types/\` is the exact API surface (signatures only; generated and gitignored — run
|
|
@@ -221774,7 +222063,7 @@ var create = async (args) => {
|
|
|
221774
222063
|
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
221775
222064
|
const dir = args._[1] ?? slug2(name);
|
|
221776
222065
|
const root = resolve10(dir);
|
|
221777
|
-
if (existsSync23(root) &&
|
|
222066
|
+
if (existsSync23(root) && readdirSync13(root).length > 0) {
|
|
221778
222067
|
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
221779
222068
|
}
|
|
221780
222069
|
mkdirSync17(root, { recursive: true });
|
|
@@ -221797,6 +222086,7 @@ var create = async (args) => {
|
|
|
221797
222086
|
// src/commands/desktop.ts
|
|
221798
222087
|
init_util();
|
|
221799
222088
|
init_manifest();
|
|
222089
|
+
import { basename as basename9 } from "node:path";
|
|
221800
222090
|
var resolveScale = (flag, cfg) => {
|
|
221801
222091
|
const raw = flag ?? cfg;
|
|
221802
222092
|
if (raw === undefined || raw === "system")
|
|
@@ -221830,19 +222120,26 @@ var run = async (args) => {
|
|
|
221830
222120
|
const renderer = resolveDesktopRenderer(root, args);
|
|
221831
222121
|
const { js, resources } = await buildBundle(args, undefined, { localAssets: true, desktopRenderer: renderer });
|
|
221832
222122
|
assertRendererSupportsBundle(renderer, js);
|
|
222123
|
+
const { dirs: pluginDirs, files: pluginFiles } = desktopPluginLibraries(root);
|
|
222124
|
+
if (pluginFiles.length > 0)
|
|
222125
|
+
note(`Native plugins: ${pluginFiles.map((f3) => basename9(f3)).join(", ")}`);
|
|
221833
222126
|
note(`Launching LeCodes Desktop${renderer === "vulkan" ? " (Vulkan)" : ""}… (close the window to return)`);
|
|
221834
222127
|
await runDesktopApp(js, resources, {
|
|
221835
222128
|
renderer,
|
|
222129
|
+
pluginDirs,
|
|
221836
222130
|
width: Number(flagStr(args, "width")) || win?.width,
|
|
221837
222131
|
height: Number(flagStr(args, "height")) || win?.height,
|
|
221838
222132
|
minWidth: win?.minWidth,
|
|
221839
222133
|
minHeight: win?.minHeight,
|
|
221840
222134
|
resizable: win?.resizable,
|
|
221841
222135
|
title: flagStr(args, "title") ?? win?.title ?? appCfg?.name,
|
|
222136
|
+
storageScope: appCfg?.name,
|
|
221842
222137
|
fullscreen: flagBool(args, "fullscreen") ? true : flagBool(args, "windowed") ? false : win?.fullscreen,
|
|
221843
222138
|
scale: resolveScale(flagStr(args, "scale"), win?.scale),
|
|
221844
222139
|
renderScale: resolveRenderScale(flagStr(args, "render-scale"), appCfg?.desktop?.renderScale),
|
|
221845
222140
|
maxRenderSize: appCfg?.desktop?.maxRenderSize,
|
|
222141
|
+
mouseEmulateTouch: appCfg?.desktop?.mouseEmulateTouch,
|
|
222142
|
+
virtualKeyboard: appCfg?.desktop?.virtualKeyboard,
|
|
221846
222143
|
fps: flagBool(args, "fps"),
|
|
221847
222144
|
net: flagBool(args, "server") ? { role: "server", port: Number(flagStr(args, "port")) || undefined, maxClients: Number(flagStr(args, "max-clients")) || undefined } : flagStr(args, "connect") ? { role: "client", address: flagStr(args, "connect") } : undefined
|
|
221848
222145
|
});
|
|
@@ -221886,16 +222183,16 @@ init_design();
|
|
|
221886
222183
|
// src/commands/dev.ts
|
|
221887
222184
|
init_manifest();
|
|
221888
222185
|
init_ignore();
|
|
221889
|
-
import { existsSync as
|
|
222186
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync20, readFileSync as readFileSync24, watch, writeFileSync as writeFileSync22 } from "node:fs";
|
|
221890
222187
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
221891
222188
|
import { networkInterfaces } from "node:os";
|
|
221892
|
-
import { basename as
|
|
222189
|
+
import { basename as basename12, join as join31 } from "node:path";
|
|
221893
222190
|
|
|
221894
222191
|
// src/dev/devServer.ts
|
|
221895
222192
|
init_localFiles();
|
|
221896
222193
|
import { createServer as createServer2 } from "node:http";
|
|
221897
|
-
import { createReadStream, statSync as
|
|
221898
|
-
import { join as
|
|
222194
|
+
import { createReadStream, statSync as statSync10 } from "node:fs";
|
|
222195
|
+
import { join as join28 } from "node:path";
|
|
221899
222196
|
init_projectCompile();
|
|
221900
222197
|
init_fonts();
|
|
221901
222198
|
init_assetIcons();
|
|
@@ -222158,7 +222455,12 @@ var MIME = {
|
|
|
222158
222455
|
woff2: "font/woff2",
|
|
222159
222456
|
json: "application/json",
|
|
222160
222457
|
txt: "text/plain",
|
|
222161
|
-
js: "text/javascript"
|
|
222458
|
+
js: "text/javascript",
|
|
222459
|
+
mjs: "text/javascript",
|
|
222460
|
+
css: "text/css",
|
|
222461
|
+
html: "text/html",
|
|
222462
|
+
wasm: "application/wasm",
|
|
222463
|
+
map: "application/json"
|
|
222162
222464
|
};
|
|
222163
222465
|
var contentType = (path2) => {
|
|
222164
222466
|
const i2 = path2.lastIndexOf(".");
|
|
@@ -222169,14 +222471,14 @@ var sendText = (res, status2, type, body) => {
|
|
|
222169
222471
|
res.end(body);
|
|
222170
222472
|
};
|
|
222171
222473
|
var sendJson = (res, status2, body) => sendText(res, status2, "application/json", JSON.stringify(body));
|
|
222172
|
-
var readBody = (req) => new Promise((
|
|
222474
|
+
var readBody = (req) => new Promise((resolve12) => {
|
|
222173
222475
|
let body = "";
|
|
222174
222476
|
req.on("data", (c4) => {
|
|
222175
222477
|
if (body.length < 256 * 1024)
|
|
222176
222478
|
body += c4.toString("utf8");
|
|
222177
222479
|
});
|
|
222178
|
-
req.on("end", () =>
|
|
222179
|
-
req.on("error", () =>
|
|
222480
|
+
req.on("end", () => resolve12(body));
|
|
222481
|
+
req.on("error", () => resolve12(body));
|
|
222180
222482
|
});
|
|
222181
222483
|
var startDevServer = async (options) => {
|
|
222182
222484
|
const { root, manifest, name, origin, token, entryOverride } = options;
|
|
@@ -222253,22 +222555,61 @@ var startDevServer = async (options) => {
|
|
|
222253
222555
|
else if (m.type === "error")
|
|
222254
222556
|
log2(`[device:error] ${demangle(m.text ?? "")}`);
|
|
222255
222557
|
};
|
|
222256
|
-
const transportOf = (url) =>
|
|
222558
|
+
const transportOf = (url) => {
|
|
222559
|
+
const t2 = url.searchParams.get("t");
|
|
222560
|
+
return t2 === "poll" ? "poll" : t2 === "web" ? "web" : "ws";
|
|
222561
|
+
};
|
|
222562
|
+
const withPrelude = (js, url, v2, forced) => {
|
|
222563
|
+
const transport = forced ?? transportOf(url);
|
|
222564
|
+
return transport === "web" ? js : js + `
|
|
222565
|
+
` + renderPrelude({ origin, base, transport, v: v2 });
|
|
222566
|
+
};
|
|
222567
|
+
const webDir = options.webDir ?? null;
|
|
222257
222568
|
const handle = async (req, res) => {
|
|
222258
222569
|
const url = new URL(req.url ?? "/", origin);
|
|
222259
222570
|
const path2 = url.pathname;
|
|
222571
|
+
if (path2 === "/favicon.ico") {
|
|
222572
|
+
res.writeHead(204);
|
|
222573
|
+
res.end();
|
|
222574
|
+
return;
|
|
222575
|
+
}
|
|
222260
222576
|
if (!path2.startsWith(base + "/"))
|
|
222261
222577
|
return sendText(res, 404, "text/plain", "Not found");
|
|
222262
222578
|
const route = path2.slice(base.length);
|
|
222263
222579
|
if (route === "/dev.js") {
|
|
222264
|
-
return sendText(res, 200, "text/javascript", devJs
|
|
222265
|
-
|
|
222580
|
+
return sendText(res, 200, "text/javascript", withPrelude(devJs, url, -1));
|
|
222581
|
+
}
|
|
222582
|
+
if (route === "/dev-poll.js") {
|
|
222583
|
+
return sendText(res, 200, "text/javascript", withPrelude(devJs, url, -1, "poll"));
|
|
222266
222584
|
}
|
|
222267
222585
|
if (route === "/bundle.js") {
|
|
222268
222586
|
if (bundleJs === null)
|
|
222269
222587
|
return sendText(res, 503, "text/plain", "No successful compile yet — check the terminal.");
|
|
222270
|
-
|
|
222271
|
-
|
|
222588
|
+
res.setHeader("x-lecodes-v", String(v));
|
|
222589
|
+
return sendText(res, 200, "text/javascript", withPrelude(bundleJs, url, v));
|
|
222590
|
+
}
|
|
222591
|
+
if (route === "/web") {
|
|
222592
|
+
res.writeHead(302, { location: `${base}/web/${url.search}` });
|
|
222593
|
+
res.end();
|
|
222594
|
+
return;
|
|
222595
|
+
}
|
|
222596
|
+
if (route.startsWith("/web/")) {
|
|
222597
|
+
if (!webDir)
|
|
222598
|
+
return sendText(res, 404, "text/plain", "No Web Lite runner vendored with this CLI (lecodes-viewer-lite dist-embed).");
|
|
222599
|
+
const rel = route === "/web/" ? "embed.html" : route.slice("/web/".length);
|
|
222600
|
+
if (rel.split("/").some((seg) => seg === ".." || seg === "" || seg === "."))
|
|
222601
|
+
return sendText(res, 400, "text/plain", "Bad path");
|
|
222602
|
+
const abs = join28(webDir, ...rel.split("/"));
|
|
222603
|
+
try {
|
|
222604
|
+
if (!statSync10(abs).isFile())
|
|
222605
|
+
throw new Error("not a file");
|
|
222606
|
+
} catch {
|
|
222607
|
+
return sendText(res, 404, "text/plain", "Not found");
|
|
222608
|
+
}
|
|
222609
|
+
const hashed = rel.startsWith("assets/");
|
|
222610
|
+
res.writeHead(200, { "content-type": contentType(rel), "cache-control": hashed ? "max-age=31536000, immutable" : "no-store" });
|
|
222611
|
+
createReadStream(abs).pipe(res);
|
|
222612
|
+
return;
|
|
222272
222613
|
}
|
|
222273
222614
|
if (route === "/version") {
|
|
222274
222615
|
return sendJson(res, 200, { v });
|
|
@@ -222281,9 +222622,9 @@ var startDevServer = async (options) => {
|
|
|
222281
222622
|
const name2 = route.slice("/shaders/".length);
|
|
222282
222623
|
if (!isShaderArtifactName(name2))
|
|
222283
222624
|
return sendText(res, 400, "text/plain", "Bad path");
|
|
222284
|
-
const abs =
|
|
222625
|
+
const abs = join28(shadersCacheDir(root), name2);
|
|
222285
222626
|
try {
|
|
222286
|
-
if (!
|
|
222627
|
+
if (!statSync10(abs).isFile())
|
|
222287
222628
|
throw new Error("not a file");
|
|
222288
222629
|
} catch {
|
|
222289
222630
|
return sendText(res, 404, "text/plain", "Not found");
|
|
@@ -222300,7 +222641,7 @@ var startDevServer = async (options) => {
|
|
|
222300
222641
|
return sendText(res, 400, "text/plain", "Bad path");
|
|
222301
222642
|
}
|
|
222302
222643
|
const abs = resolveLocalPath(root, key);
|
|
222303
|
-
if (!abs || !
|
|
222644
|
+
if (!abs || !statSync10(abs).isFile())
|
|
222304
222645
|
return sendText(res, 404, "text/plain", "Not found");
|
|
222305
222646
|
res.writeHead(200, { "content-type": contentType(key), "cache-control": "no-store" });
|
|
222306
222647
|
createReadStream(abs).pipe(res);
|
|
@@ -222343,17 +222684,18 @@ var startDevServer = async (options) => {
|
|
|
222343
222684
|
if (conn)
|
|
222344
222685
|
clients.add(conn);
|
|
222345
222686
|
});
|
|
222346
|
-
await new Promise((
|
|
222687
|
+
await new Promise((resolve12, reject) => {
|
|
222347
222688
|
server2.once("error", reject);
|
|
222348
222689
|
server2.listen(options.port, options.bind ?? "0.0.0.0", () => {
|
|
222349
222690
|
server2.removeListener("error", reject);
|
|
222350
|
-
|
|
222691
|
+
resolve12();
|
|
222351
222692
|
});
|
|
222352
222693
|
});
|
|
222353
222694
|
const address = server2.address();
|
|
222354
222695
|
const boundPort = typeof address === "object" && address !== null ? address.port : options.port;
|
|
222355
222696
|
return {
|
|
222356
222697
|
url: `${origin}${base}/dev.js`,
|
|
222698
|
+
webUrl: webDir ? `${origin}${base}/web/` : null,
|
|
222357
222699
|
base,
|
|
222358
222700
|
port: boundPort,
|
|
222359
222701
|
v: () => v,
|
|
@@ -222367,6 +222709,147 @@ var startDevServer = async (options) => {
|
|
|
222367
222709
|
};
|
|
222368
222710
|
};
|
|
222369
222711
|
|
|
222712
|
+
// src/dev/webRunner.ts
|
|
222713
|
+
init_distRoot();
|
|
222714
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
222715
|
+
import { createRequire as createRequire6 } from "node:module";
|
|
222716
|
+
import { dirname as dirname15, join as join29 } from "node:path";
|
|
222717
|
+
var resolveWebRunnerDir = () => {
|
|
222718
|
+
const candidates = [join29(distRoot, "runtime", "web")];
|
|
222719
|
+
try {
|
|
222720
|
+
const pkg = createRequire6(import.meta.url).resolve("lecodes-viewer-lite/package.json");
|
|
222721
|
+
candidates.push(join29(dirname15(pkg), "dist-embed"));
|
|
222722
|
+
} catch {}
|
|
222723
|
+
for (const dir of candidates)
|
|
222724
|
+
if (existsSync27(join29(dir, "embed.html")) && existsSync27(join29(dir, "embed.js")))
|
|
222725
|
+
return dir;
|
|
222726
|
+
return null;
|
|
222727
|
+
};
|
|
222728
|
+
|
|
222729
|
+
// src/commands/dev.ts
|
|
222730
|
+
init_browserAuth();
|
|
222731
|
+
|
|
222732
|
+
// src/dev/androidDev.ts
|
|
222733
|
+
init_util();
|
|
222734
|
+
import { existsSync as existsSync28, writeFileSync as writeFileSync21 } from "node:fs";
|
|
222735
|
+
import { join as join30 } from "node:path";
|
|
222736
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
222737
|
+
var DEFAULT_ANDROID_PACKAGE = "com.letary.lecodes";
|
|
222738
|
+
var adbCandidates = () => {
|
|
222739
|
+
const out = [];
|
|
222740
|
+
if (process.env.LECODES_ADB)
|
|
222741
|
+
out.push(process.env.LECODES_ADB);
|
|
222742
|
+
out.push("adb");
|
|
222743
|
+
const exe = process.platform === "win32" ? "adb.exe" : "adb";
|
|
222744
|
+
for (const home of [process.env.ANDROID_HOME, process.env.ANDROID_SDK_ROOT]) {
|
|
222745
|
+
if (home)
|
|
222746
|
+
out.push(join30(home, "platform-tools", exe));
|
|
222747
|
+
}
|
|
222748
|
+
if (process.platform === "win32" && process.env.LOCALAPPDATA)
|
|
222749
|
+
out.push(join30(process.env.LOCALAPPDATA, "Android", "Sdk", "platform-tools", exe));
|
|
222750
|
+
if (process.env.HOME)
|
|
222751
|
+
out.push(join30(process.env.HOME, "Android", "Sdk", "platform-tools", exe), join30(process.env.HOME, "Library", "Android", "sdk", "platform-tools", exe));
|
|
222752
|
+
return out;
|
|
222753
|
+
};
|
|
222754
|
+
var run2 = (adb, args, input) => {
|
|
222755
|
+
const r2 = spawnSync7(adb, args, { encoding: "utf8", input, windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
|
|
222756
|
+
const out = `${r2.stdout ?? ""}${r2.stderr ?? ""}`.trim();
|
|
222757
|
+
return { ok: r2.status === 0, out };
|
|
222758
|
+
};
|
|
222759
|
+
var resolveAdb = () => {
|
|
222760
|
+
for (const cand of adbCandidates()) {
|
|
222761
|
+
if (cand !== "adb" && !existsSync28(cand))
|
|
222762
|
+
continue;
|
|
222763
|
+
const r2 = spawnSync7(cand, ["version"], { encoding: "utf8", windowsHide: true });
|
|
222764
|
+
if (r2.status === 0)
|
|
222765
|
+
return cand;
|
|
222766
|
+
}
|
|
222767
|
+
throw new CliError("adb not found — install Android platform-tools (or point LECODES_ADB at adb).");
|
|
222768
|
+
};
|
|
222769
|
+
var listDevices = (adb) => {
|
|
222770
|
+
const { out } = run2(adb, ["devices", "-l"]);
|
|
222771
|
+
const devices = [];
|
|
222772
|
+
for (const line of out.split(/\r?\n/).slice(1)) {
|
|
222773
|
+
const m = /^(\S+)\s+(\S+)(.*)$/.exec(line.trim());
|
|
222774
|
+
if (!m)
|
|
222775
|
+
continue;
|
|
222776
|
+
const model = /model:(\S+)/.exec(m[3] ?? "")?.[1];
|
|
222777
|
+
devices.push({ serial: m[1], state: m[2], model });
|
|
222778
|
+
}
|
|
222779
|
+
return devices;
|
|
222780
|
+
};
|
|
222781
|
+
var pickDevice = (adb, serial) => {
|
|
222782
|
+
const all = listDevices(adb);
|
|
222783
|
+
if (serial) {
|
|
222784
|
+
const d = all.find((x2) => x2.serial === serial);
|
|
222785
|
+
if (!d)
|
|
222786
|
+
throw new CliError(`No device "${serial}" — adb sees: ${all.map((x2) => `${x2.serial} (${x2.state})`).join(", ") || "nothing"}.`);
|
|
222787
|
+
if (d.state !== "device")
|
|
222788
|
+
throw new CliError(`Device ${serial} is "${d.state}" — accept the USB-debugging prompt on the phone.`);
|
|
222789
|
+
return serial;
|
|
222790
|
+
}
|
|
222791
|
+
const ready = all.filter((d) => d.state === "device");
|
|
222792
|
+
if (ready.length === 1)
|
|
222793
|
+
return ready[0].serial;
|
|
222794
|
+
if (ready.length === 0) {
|
|
222795
|
+
const other = all.map((d) => `${d.serial} (${d.state})`).join(", ");
|
|
222796
|
+
throw new CliError(other ? `No usable device: adb sees ${other}. "unauthorized" = accept the USB-debugging prompt on the phone; "offline" = replug.` : "No Android device: enable USB debugging (Developer options), plug the phone in, accept the prompt — or `adb connect <ip>:5555` for Wi-Fi debugging.");
|
|
222797
|
+
}
|
|
222798
|
+
throw new CliError(`Several devices: ${ready.map((d) => `${d.serial}${d.model ? ` (${d.model})` : ""}`).join(", ")} — pick one with --serial <id>.`);
|
|
222799
|
+
};
|
|
222800
|
+
var launchOnAndroid = (opts) => {
|
|
222801
|
+
const adb = resolveAdb();
|
|
222802
|
+
const serial = pickDevice(adb, opts.serial);
|
|
222803
|
+
const packageName = opts.packageName ?? process.env.LECODES_ANDROID_PACKAGE ?? DEFAULT_ANDROID_PACKAGE;
|
|
222804
|
+
const dev = (args) => run2(adb, ["-s", serial, ...args]);
|
|
222805
|
+
if (opts.apk) {
|
|
222806
|
+
if (!existsSync28(opts.apk))
|
|
222807
|
+
throw new CliError(`--apk: ${opts.apk} does not exist.`);
|
|
222808
|
+
note(`Installing ${opts.apk}…`);
|
|
222809
|
+
const r2 = dev(["install", "-r", opts.apk]);
|
|
222810
|
+
if (!r2.ok || !/Success/.test(r2.out)) {
|
|
222811
|
+
throw new CliError(`adb install failed: ${r2.out}${/USER_RESTRICTED/.test(r2.out) ? `
|
|
222812
|
+
(MIUI: enable "Install via USB" in Developer options.)` : ""}`);
|
|
222813
|
+
}
|
|
222814
|
+
}
|
|
222815
|
+
const installed = dev(["shell", "pm", "path", packageName]);
|
|
222816
|
+
if (!installed.ok || !installed.out.includes("package:")) {
|
|
222817
|
+
throw new CliError(`${packageName} is not installed on ${serial} — pass --apk <file> (lecodes-android: .\\gradlew :app:assembleDebug → app/build/outputs/apk/debug/app-debug.apk).`);
|
|
222818
|
+
}
|
|
222819
|
+
const rev = dev(["reverse", `tcp:${opts.port}`, `tcp:${opts.port}`]);
|
|
222820
|
+
if (!rev.ok)
|
|
222821
|
+
warnErr(`adb reverse failed (${rev.out}) — the phone must reach the LAN url instead.`);
|
|
222822
|
+
dev(["shell", "am", "force-stop", packageName]);
|
|
222823
|
+
dev(["logcat", "-c"]);
|
|
222824
|
+
const start = dev(["shell", "am", "start", "-n", `${packageName}/.MainActivity`, "-a", "android.intent.action.VIEW", "-d", opts.url]);
|
|
222825
|
+
if (!start.ok || /Error/.test(start.out))
|
|
222826
|
+
throw new CliError(`Could not launch ${packageName}: ${start.out}`);
|
|
222827
|
+
let stopped = false;
|
|
222828
|
+
return {
|
|
222829
|
+
serial,
|
|
222830
|
+
packageName,
|
|
222831
|
+
adb,
|
|
222832
|
+
screenshot: (file) => {
|
|
222833
|
+
const r2 = spawnSync7(adb, ["-s", serial, "exec-out", "screencap", "-p"], { windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
|
|
222834
|
+
if (r2.status !== 0 || !r2.stdout || r2.stdout.length === 0)
|
|
222835
|
+
return false;
|
|
222836
|
+
writeFileSync21(file, r2.stdout);
|
|
222837
|
+
return true;
|
|
222838
|
+
},
|
|
222839
|
+
stop: () => {
|
|
222840
|
+
if (stopped)
|
|
222841
|
+
return;
|
|
222842
|
+
stopped = true;
|
|
222843
|
+
dev(["shell", "am", "force-stop", packageName]);
|
|
222844
|
+
dev(["reverse", "--remove", `tcp:${opts.port}`]);
|
|
222845
|
+
}
|
|
222846
|
+
};
|
|
222847
|
+
};
|
|
222848
|
+
var androidCrashLines = (session) => {
|
|
222849
|
+
const { out } = run2(session.adb, ["-s", session.serial, "logcat", "-d", "-v", "brief"]);
|
|
222850
|
+
return out.split(/\r?\n/).filter((l2) => /FATAL EXCEPTION|Fatal signal|\[crash\]/.test(l2) || /AndroidRuntime/.test(l2) && l2.includes(session.packageName)).slice(-20);
|
|
222851
|
+
};
|
|
222852
|
+
|
|
222370
222853
|
// src/commands/dev.ts
|
|
222371
222854
|
init_util();
|
|
222372
222855
|
var DEFAULT_PORT2 = 4499;
|
|
@@ -222395,24 +222878,24 @@ var lanCandidates = () => {
|
|
|
222395
222878
|
return out.sort((x2, y2) => x2.rank - y2.rank).map(({ address, iface }) => ({ address, iface }));
|
|
222396
222879
|
};
|
|
222397
222880
|
var loadDevToken = (root) => {
|
|
222398
|
-
const path2 =
|
|
222881
|
+
const path2 = join31(root, LECODES_DIR, "dev.json");
|
|
222399
222882
|
try {
|
|
222400
|
-
const token2 = JSON.parse(
|
|
222883
|
+
const token2 = JSON.parse(readFileSync24(path2, "utf8")).token;
|
|
222401
222884
|
if (token2 && /^[a-z0-9]{8,}$/.test(token2))
|
|
222402
222885
|
return token2;
|
|
222403
222886
|
} catch {}
|
|
222404
222887
|
const token = randomBytes3(6).toString("hex");
|
|
222405
|
-
|
|
222406
|
-
|
|
222888
|
+
mkdirSync20(join31(root, LECODES_DIR), { recursive: true });
|
|
222889
|
+
writeFileSync22(path2, JSON.stringify({ token }, null, 2) + `
|
|
222407
222890
|
`);
|
|
222408
222891
|
return token;
|
|
222409
222892
|
};
|
|
222410
222893
|
var printQr = async (url) => {
|
|
222411
222894
|
try {
|
|
222412
222895
|
const qrcode = (await Promise.resolve().then(() => __toESM(require_main(), 1))).default;
|
|
222413
|
-
await new Promise((
|
|
222896
|
+
await new Promise((resolve12) => qrcode.generate(url, { small: true }, (out) => {
|
|
222414
222897
|
log(out);
|
|
222415
|
-
|
|
222898
|
+
resolve12();
|
|
222416
222899
|
}));
|
|
222417
222900
|
} catch {
|
|
222418
222901
|
note("(couldn't render a QR code — open the URL below on the device)");
|
|
@@ -222428,7 +222911,7 @@ var dev = async (args) => {
|
|
|
222428
222911
|
root = process.cwd();
|
|
222429
222912
|
manifest = null;
|
|
222430
222913
|
}
|
|
222431
|
-
const name = manifest?.name ??
|
|
222914
|
+
const name = manifest?.name ?? basename12(root);
|
|
222432
222915
|
if (typeof globalThis.Bun !== "undefined") {
|
|
222433
222916
|
warnErr("Running under Bun: the websocket reload channel doesn't work here — run `lecodes dev` with Node (or use ?t=poll on the device URL).");
|
|
222434
222917
|
}
|
|
@@ -222440,6 +222923,10 @@ var dev = async (args) => {
|
|
|
222440
222923
|
const alternatives = candidates.filter((c4) => c4.address !== host);
|
|
222441
222924
|
const origin = `http://${host}:${port}`;
|
|
222442
222925
|
const token = loadDevToken(root);
|
|
222926
|
+
const webDir = resolveWebRunnerDir();
|
|
222927
|
+
if (flagBool(args, "web") && !webDir) {
|
|
222928
|
+
throw new CliError("No Web Lite runner found for --web: this CLI has no runtime/web (a monorepo checkout needs `bun run build:embed` in packages/viewer-lite).");
|
|
222929
|
+
}
|
|
222443
222930
|
const server2 = await startDevServer({
|
|
222444
222931
|
root,
|
|
222445
222932
|
manifest,
|
|
@@ -222448,6 +222935,7 @@ var dev = async (args) => {
|
|
|
222448
222935
|
port,
|
|
222449
222936
|
token,
|
|
222450
222937
|
entryOverride: flagStr(args, "entry"),
|
|
222938
|
+
webDir,
|
|
222451
222939
|
log: note
|
|
222452
222940
|
}).catch((e2) => {
|
|
222453
222941
|
const msg = e2 instanceof Error ? e2.message : String(e2);
|
|
@@ -222481,6 +222969,7 @@ ${first.error}`);
|
|
|
222481
222969
|
return true;
|
|
222482
222970
|
};
|
|
222483
222971
|
const debug2 = process.env.LECODES_DEV_DEBUG === "1";
|
|
222972
|
+
const underBun = typeof globalThis.Bun !== "undefined";
|
|
222484
222973
|
let timer = null;
|
|
222485
222974
|
const onChange = (file) => {
|
|
222486
222975
|
if (debug2)
|
|
@@ -222516,14 +223005,63 @@ ${result.error}`);
|
|
|
222516
223005
|
if (!flagBool(args, "no-qr"))
|
|
222517
223006
|
await printQr(server2.url);
|
|
222518
223007
|
log(`Scan the QR with the LeCodes app, or open: ${c.bold(server2.url)}`);
|
|
223008
|
+
const webLocal = server2.webUrl ? server2.webUrl.replace(`http://${host}:`, "http://localhost:") : null;
|
|
223009
|
+
if (server2.webUrl && webLocal)
|
|
223010
|
+
log(`In the browser (Web Lite): ${c.bold(webLocal)} (LAN: ${server2.webUrl})`);
|
|
222519
223011
|
note(`Older desktop builds (no websocket bridge): ${server2.url}?t=poll`);
|
|
222520
223012
|
if (alternatives.length > 0) {
|
|
222521
223013
|
note(`Device can't reach it? This machine also has ${alternatives.map((a2) => `${a2.address} (${a2.iface})`).join(", ")} — retry with --host <ip>.`);
|
|
222522
223014
|
}
|
|
222523
|
-
if (
|
|
223015
|
+
if (existsSync29(join31(root, LECODES_DIR, "manifest.json")))
|
|
222524
223016
|
note(`Token stored in ${LECODES_DIR}/dev.json (delete it to rotate the URL).`);
|
|
222525
223017
|
log("");
|
|
222526
223018
|
log(`Watching ${root} — save a file to hot-reload. Ctrl+C to stop.`);
|
|
223019
|
+
if (flagBool(args, "web") && webLocal) {
|
|
223020
|
+
note("Opening the browser…");
|
|
223021
|
+
openBrowser(webLocal);
|
|
223022
|
+
}
|
|
223023
|
+
if (flagBool(args, "android")) {
|
|
223024
|
+
const seconds = Number(flagStr(args, "time")) || 0;
|
|
223025
|
+
const png2 = flagStr(args, "png");
|
|
223026
|
+
const deviceUrl = `http://127.0.0.1:${port}${server2.url.slice(origin.length)}`.replace(/\/dev\.js$/, underBun ? "/dev-poll.js" : "/dev.js");
|
|
223027
|
+
let session;
|
|
223028
|
+
try {
|
|
223029
|
+
session = launchOnAndroid({ port, url: deviceUrl, serial: flagStr(args, "serial"), apk: flagStr(args, "apk"), packageName: flagStr(args, "package") });
|
|
223030
|
+
} catch (e2) {
|
|
223031
|
+
server2.close();
|
|
223032
|
+
watcher?.close();
|
|
223033
|
+
throw e2;
|
|
223034
|
+
}
|
|
223035
|
+
note(`Android: ${session.serial} → ${deviceUrl} (adb reverse; device logs stream here as [device:log])`);
|
|
223036
|
+
const finish = (code) => {
|
|
223037
|
+
session.stop();
|
|
223038
|
+
server2.close();
|
|
223039
|
+
watcher?.close();
|
|
223040
|
+
process.exit(code);
|
|
223041
|
+
};
|
|
223042
|
+
process.on("SIGINT", () => {
|
|
223043
|
+
note("Stopping the app on the phone…");
|
|
223044
|
+
finish(0);
|
|
223045
|
+
});
|
|
223046
|
+
if (seconds > 0) {
|
|
223047
|
+
setTimeout(() => {
|
|
223048
|
+
const crashes = androidCrashLines(session);
|
|
223049
|
+
if (png2) {
|
|
223050
|
+
if (session.screenshot(png2))
|
|
223051
|
+
note(`Wrote ${png2} (device screenshot after ${seconds} s)`);
|
|
223052
|
+
else
|
|
223053
|
+
warnErr(`Screenshot failed (adb screencap).`);
|
|
223054
|
+
}
|
|
223055
|
+
if (crashes.length > 0) {
|
|
223056
|
+
warnErr(`The app crashed on the phone:
|
|
223057
|
+
${crashes.join(`
|
|
223058
|
+
`)}`);
|
|
223059
|
+
finish(1);
|
|
223060
|
+
}
|
|
223061
|
+
finish(0);
|
|
223062
|
+
}, seconds * 1000);
|
|
223063
|
+
}
|
|
223064
|
+
}
|
|
222527
223065
|
if (flagBool(args, "desktop")) {
|
|
222528
223066
|
const appCfg = readAppConfigAt(root);
|
|
222529
223067
|
const win = appCfg?.desktop?.window;
|
|
@@ -222549,8 +223087,8 @@ init_localFiles();
|
|
|
222549
223087
|
|
|
222550
223088
|
// src/serverDiff.ts
|
|
222551
223089
|
init_localFiles();
|
|
222552
|
-
import { readFileSync as
|
|
222553
|
-
import { basename as
|
|
223090
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
223091
|
+
import { basename as basename13 } from "node:path";
|
|
222554
223092
|
var isTextAsset = (t2) => t2 === "text" || t2 === "shader" || t2 === "scene";
|
|
222555
223093
|
var computeServerDiff = (project, scan2) => {
|
|
222556
223094
|
const pathById = buildPaths(project.assets);
|
|
@@ -222567,8 +223105,8 @@ var computeServerDiff = (project, scan2) => {
|
|
|
222567
223105
|
const server2 = serverByPath.get(path2);
|
|
222568
223106
|
const local = localByPath.get(path2);
|
|
222569
223107
|
if (local && !server2) {
|
|
222570
|
-
const text = isTextKind(classifyKind(
|
|
222571
|
-
entries.push({ path: path2, status: "added", binary: !text, serverText: "", localText: text ?
|
|
223108
|
+
const text = isTextKind(classifyKind(basename13(path2)));
|
|
223109
|
+
entries.push({ path: path2, status: "added", binary: !text, serverText: "", localText: text ? readFileSync25(local.absPath, "utf8") : undefined });
|
|
222572
223110
|
continue;
|
|
222573
223111
|
}
|
|
222574
223112
|
if (server2 && !local) {
|
|
@@ -222579,12 +223117,12 @@ var computeServerDiff = (project, scan2) => {
|
|
|
222579
223117
|
if (server2 && local) {
|
|
222580
223118
|
if (isTextAsset(server2.type)) {
|
|
222581
223119
|
const serverText = server2.text ?? "";
|
|
222582
|
-
const localText =
|
|
223120
|
+
const localText = readFileSync25(local.absPath, "utf8");
|
|
222583
223121
|
if (serverText !== localText)
|
|
222584
223122
|
entries.push({ path: path2, status: "modified", binary: false, serverText, localText });
|
|
222585
223123
|
} else {
|
|
222586
223124
|
const serverSize = server2.file?.size;
|
|
222587
|
-
const localSize = Buffer.byteLength(
|
|
223125
|
+
const localSize = Buffer.byteLength(readFileSync25(local.absPath));
|
|
222588
223126
|
if (serverSize !== undefined && serverSize !== localSize) {
|
|
222589
223127
|
entries.push({ path: path2, status: "modified", binary: true });
|
|
222590
223128
|
}
|
|
@@ -222742,8 +223280,8 @@ var statusLabel = (e2) => e2.status === "added" ? c.green(" added ") : e2.st
|
|
|
222742
223280
|
init_manifest();
|
|
222743
223281
|
init_types4();
|
|
222744
223282
|
init_ignore();
|
|
222745
|
-
import { existsSync as
|
|
222746
|
-
import { basename as
|
|
223283
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync21, writeFileSync as writeFileSync23 } from "node:fs";
|
|
223284
|
+
import { basename as basename14, dirname as dirname16, join as join32, resolve as resolve12 } from "node:path";
|
|
222747
223285
|
init_util();
|
|
222748
223286
|
var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
|
|
222749
223287
|
// Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes compile -o app.js\`.
|
|
@@ -222851,18 +223389,18 @@ Full command list: \`lecodes --help\`.
|
|
|
222851
223389
|
`;
|
|
222852
223390
|
var init2 = async (args) => {
|
|
222853
223391
|
const dirArg = (args._[0] ?? ".").replace(/[/\\]+$/, "") || ".";
|
|
222854
|
-
const root =
|
|
222855
|
-
const name =
|
|
222856
|
-
|
|
223392
|
+
const root = resolve12(dirArg);
|
|
223393
|
+
const name = basename14(root);
|
|
223394
|
+
mkdirSync21(root, { recursive: true });
|
|
222857
223395
|
let created = 0;
|
|
222858
223396
|
const write = (rel, content) => {
|
|
222859
|
-
const abs =
|
|
222860
|
-
if (
|
|
223397
|
+
const abs = join32(root, rel);
|
|
223398
|
+
if (existsSync30(abs)) {
|
|
222861
223399
|
note(`kept ${rel}`);
|
|
222862
223400
|
return;
|
|
222863
223401
|
}
|
|
222864
|
-
|
|
222865
|
-
|
|
223402
|
+
mkdirSync21(dirname16(abs), { recursive: true });
|
|
223403
|
+
writeFileSync23(abs, content);
|
|
222866
223404
|
log(`${c.green("created")} ${rel}`);
|
|
222867
223405
|
created++;
|
|
222868
223406
|
};
|
|
@@ -222909,8 +223447,8 @@ init_config();
|
|
|
222909
223447
|
init_ignore();
|
|
222910
223448
|
init_localFiles();
|
|
222911
223449
|
init_manifest();
|
|
222912
|
-
import { existsSync as
|
|
222913
|
-
import { basename as
|
|
223450
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
223451
|
+
import { basename as basename16, join as join33, resolve as resolve13 } from "node:path";
|
|
222914
223452
|
init_types4();
|
|
222915
223453
|
init_util();
|
|
222916
223454
|
|
|
@@ -222919,8 +223457,8 @@ init_api();
|
|
|
222919
223457
|
init_config();
|
|
222920
223458
|
init_manifest();
|
|
222921
223459
|
init_localFiles();
|
|
222922
|
-
import { basename as
|
|
222923
|
-
import { readFileSync as
|
|
223460
|
+
import { basename as basename15 } from "node:path";
|
|
223461
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
222924
223462
|
init_util();
|
|
222925
223463
|
var push = async (args) => {
|
|
222926
223464
|
const root = findProjectRoot(process.cwd());
|
|
@@ -222976,14 +223514,14 @@ var push = async (args) => {
|
|
|
222976
223514
|
let uploaded = 0;
|
|
222977
223515
|
for (const file of scan2.files) {
|
|
222978
223516
|
const source = sourceFor(file.path);
|
|
222979
|
-
const kind = source?.type ?? classifyKind(
|
|
223517
|
+
const kind = source?.type ?? classifyKind(basename15(file.path));
|
|
222980
223518
|
const entry = { path: file.path, type: kind };
|
|
222981
223519
|
if (source)
|
|
222982
223520
|
entry.assetId = source.id;
|
|
222983
223521
|
if (isTextKind(kind)) {
|
|
222984
|
-
entry.text =
|
|
223522
|
+
entry.text = readFileSync26(file.absPath, "utf8");
|
|
222985
223523
|
} else if (!source || source.sha !== file.sha) {
|
|
222986
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid,
|
|
223524
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename15(file.path), readFileSync26(file.absPath));
|
|
222987
223525
|
entry.blobFileId = blob.id;
|
|
222988
223526
|
uploaded++;
|
|
222989
223527
|
}
|
|
@@ -223090,10 +223628,10 @@ var link = async (args) => {
|
|
|
223090
223628
|
const apiUrl = requireApiUrl(config);
|
|
223091
223629
|
const token = requireToken(config);
|
|
223092
223630
|
const dirArg = args._[0];
|
|
223093
|
-
const root = dirArg ?
|
|
223094
|
-
if (!
|
|
223631
|
+
const root = dirArg ? resolve13(dirArg) : process.cwd();
|
|
223632
|
+
if (!existsSync31(root))
|
|
223095
223633
|
throw new CliError(`No such directory: ${root}`);
|
|
223096
|
-
if (
|
|
223634
|
+
if (existsSync31(join33(root, LECODES_DIR, "manifest.json")) && !flagBool(args, "force")) {
|
|
223097
223635
|
throw new CliError("This folder is already linked (.lecodes/manifest.json exists) — use `lecodes push`, or `lecodes link --force` to re-link it to another project.");
|
|
223098
223636
|
}
|
|
223099
223637
|
let enclosing = null;
|
|
@@ -223120,7 +223658,7 @@ var link = async (args) => {
|
|
|
223120
223658
|
}
|
|
223121
223659
|
name = project.name;
|
|
223122
223660
|
} else {
|
|
223123
|
-
name = flagStr(args, "name") ??
|
|
223661
|
+
name = flagStr(args, "name") ?? basename16(root);
|
|
223124
223662
|
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
223125
223663
|
uuid = (await createProject(apiUrl, token, name, flagStr(args, "team"))).uuid;
|
|
223126
223664
|
files = {};
|
|
@@ -223157,10 +223695,10 @@ var link = async (args) => {
|
|
|
223157
223695
|
|
|
223158
223696
|
// src/commands/lightmap.ts
|
|
223159
223697
|
init_manifest();
|
|
223160
|
-
import { existsSync as
|
|
223161
|
-
import { spawn as spawn6, spawnSync as
|
|
223698
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync22, mkdtempSync as mkdtempSync4, readFileSync as readFileSync27, renameSync as renameSync3, rmSync as rmSync9, statSync as statSync11, unlinkSync, writeFileSync as writeFileSync24 } from "node:fs";
|
|
223699
|
+
import { spawn as spawn6, spawnSync as spawnSync8 } from "node:child_process";
|
|
223162
223700
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
223163
|
-
import { join as
|
|
223701
|
+
import { join as join34, relative as relative4, resolve as resolve14 } from "node:path";
|
|
223164
223702
|
init_util();
|
|
223165
223703
|
var num = (args, key, fallback, min, max) => {
|
|
223166
223704
|
const raw = flagStr(args, key);
|
|
@@ -223173,20 +223711,20 @@ var num = (args, key, fallback, min, max) => {
|
|
|
223173
223711
|
};
|
|
223174
223712
|
var findToktx = () => {
|
|
223175
223713
|
const env = process.env.KTX_TOKTX;
|
|
223176
|
-
if (env &&
|
|
223714
|
+
if (env && existsSync32(env))
|
|
223177
223715
|
return env;
|
|
223178
|
-
for (const base of [process.env.LOCALAPPDATA &&
|
|
223716
|
+
for (const base of [process.env.LOCALAPPDATA && join34(process.env.LOCALAPPDATA, "Programs"), process.env.ProgramFiles]) {
|
|
223179
223717
|
if (!base)
|
|
223180
223718
|
continue;
|
|
223181
|
-
const guess =
|
|
223182
|
-
if (
|
|
223719
|
+
const guess = join34(base, "KTX-Software", "bin", "toktx.exe");
|
|
223720
|
+
if (existsSync32(guess))
|
|
223183
223721
|
return guess;
|
|
223184
223722
|
}
|
|
223185
223723
|
return "toktx";
|
|
223186
223724
|
};
|
|
223187
223725
|
var checkToktx = (toktx) => {
|
|
223188
223726
|
try {
|
|
223189
|
-
const p2 =
|
|
223727
|
+
const p2 = spawnSync8(toktx, ["--version"], { encoding: "utf8" });
|
|
223190
223728
|
if (p2.status === 0)
|
|
223191
223729
|
return (p2.stdout ?? "").toString().trim() || "toktx";
|
|
223192
223730
|
} catch {}
|
|
@@ -223200,7 +223738,7 @@ var requireToktx = () => {
|
|
|
223200
223738
|
}
|
|
223201
223739
|
return toktx;
|
|
223202
223740
|
};
|
|
223203
|
-
var encodeKtx2 = (toktx, png2, ktx2) => new Promise((
|
|
223741
|
+
var encodeKtx2 = (toktx, png2, ktx2) => new Promise((resolve15, reject) => {
|
|
223204
223742
|
const args = ["--t2", "--encode", "uastc", "--uastc_quality", "2", "--zcmp", "18", "--genmipmap", "--assign_oetf", "srgb", ktx2, png2];
|
|
223205
223743
|
const p2 = spawn6(toktx, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
223206
223744
|
let out = "";
|
|
@@ -223213,7 +223751,7 @@ var encodeKtx2 = (toktx, png2, ktx2) => new Promise((resolve14, reject) => {
|
|
|
223213
223751
|
p2.on("error", (e2) => reject(new CliError(`toktx failed (${e2.message}) while encoding ${ktx2}`)));
|
|
223214
223752
|
p2.on("close", (status2) => {
|
|
223215
223753
|
if (status2 === 0)
|
|
223216
|
-
|
|
223754
|
+
resolve15();
|
|
223217
223755
|
else
|
|
223218
223756
|
reject(new CliError(`toktx failed (${status2 ?? "?"}) while encoding ${ktx2}:
|
|
223219
223757
|
${out.trim()}`));
|
|
@@ -223228,13 +223766,13 @@ var bake = async (args) => {
|
|
|
223228
223766
|
root = process.cwd();
|
|
223229
223767
|
}
|
|
223230
223768
|
const toktx = requireToktx();
|
|
223231
|
-
const outDir =
|
|
223769
|
+
const outDir = resolve14(root, flagStr(args, "out") ?? "assets/lightmap");
|
|
223232
223770
|
const stem = flagStr(args, "name") ?? "lightmap";
|
|
223233
223771
|
if (!/^[A-Za-z0-9_-]+$/.test(stem))
|
|
223234
223772
|
throw new CliError(`--name wants a plain file stem (letters, digits, - and _), got "${stem}".`);
|
|
223235
223773
|
const entryFlag = flagStr(args, "entry");
|
|
223236
223774
|
const entryFile = entryFlag ?? "main.ts";
|
|
223237
|
-
if (!
|
|
223775
|
+
if (!existsSync32(resolve14(root, entryFile))) {
|
|
223238
223776
|
throw new CliError(`No ${entryFile} in ${root} — pass --entry <file> (the file that builds the scene and calls Lightmap.load).`);
|
|
223239
223777
|
}
|
|
223240
223778
|
const cfg = {
|
|
@@ -223266,7 +223804,7 @@ var bake = async (args) => {
|
|
|
223266
223804
|
const entryText = `globalThis.__lecodesLightmap = ${JSON.stringify(cfg)}
|
|
223267
223805
|
import ${JSON.stringify(importPath)}
|
|
223268
223806
|
`;
|
|
223269
|
-
|
|
223807
|
+
mkdirSync22(outDir, { recursive: true });
|
|
223270
223808
|
ensurePlaceholders(outDir, stem);
|
|
223271
223809
|
const desktopRenderer = resolveDesktopRenderer(root, args);
|
|
223272
223810
|
const { js, resources } = await buildBundle(args, undefined, {
|
|
@@ -223275,8 +223813,8 @@ import ${JSON.stringify(importPath)}
|
|
|
223275
223813
|
extraEntries: [{ path: entryPath, type: "text", text: entryText }],
|
|
223276
223814
|
entryOverride: entryPath
|
|
223277
223815
|
});
|
|
223278
|
-
const scratch = mkdtempSync4(
|
|
223279
|
-
const outPng =
|
|
223816
|
+
const scratch = mkdtempSync4(join34(tmpdir4(), "lecodes-lightmap-"));
|
|
223817
|
+
const outPng = join34(scratch, "frame.png");
|
|
223280
223818
|
note(`Baking up to ${cfg.pages} × ${cfg.size}² at ${(cfg.texel * 100).toFixed(1)} cm texels (${cfg.sunRays} sun + ${cfg.aoRays} AO + ${cfg.lightRays} light rays)${cfg.volumeCell ? ` + a ${cfg.volumeCell} m light volume` : ""} → ${relative4(root, outDir) || "."}/${stem}.*…`);
|
|
223281
223819
|
let output = "";
|
|
223282
223820
|
try {
|
|
@@ -223310,22 +223848,22 @@ import ${JSON.stringify(importPath)}
|
|
|
223310
223848
|
logErr(text.startsWith("error") ? c.red(`✗ ${text}`) : text.startsWith("warning") ? c.yellow(` ${text}`) : c.dim(` ${text}`));
|
|
223311
223849
|
}
|
|
223312
223850
|
if (/\[lightmap\] done/.test(output)) {
|
|
223313
|
-
const bakePath =
|
|
223314
|
-
if (!
|
|
223851
|
+
const bakePath = join34(outDir, "lightmap.bake");
|
|
223852
|
+
if (!existsSync32(bakePath))
|
|
223315
223853
|
throw new CliError(`The host reported a bake but ${bakePath} is missing.`);
|
|
223316
|
-
const data = JSON.parse(
|
|
223854
|
+
const data = JSON.parse(readFileSync27(bakePath, "utf8"));
|
|
223317
223855
|
const pages = Math.max(1, data.pages ?? 1);
|
|
223318
223856
|
const hasLight = !!data.lightTextures?.length;
|
|
223319
223857
|
const t0 = Date.now();
|
|
223320
223858
|
let pngKb = 0, ktxKb = 0;
|
|
223321
223859
|
const encodePage = async (page, light) => {
|
|
223322
|
-
const png2 =
|
|
223323
|
-
const ktx2 =
|
|
223324
|
-
if (!
|
|
223860
|
+
const png2 = join34(outDir, pageFile("lightmap", page, light, ".png"));
|
|
223861
|
+
const ktx2 = join34(outDir, pageFile(stem, page, light));
|
|
223862
|
+
if (!existsSync32(png2))
|
|
223325
223863
|
throw new CliError(`The host reported a bake but ${png2} is missing.`);
|
|
223326
223864
|
await encodeKtx2(toktx, png2, ktx2);
|
|
223327
|
-
pngKb +=
|
|
223328
|
-
ktxKb +=
|
|
223865
|
+
pngKb += statSync11(png2).size / 1024;
|
|
223866
|
+
ktxKb += statSync11(ktx2).size / 1024;
|
|
223329
223867
|
unlinkSync(png2);
|
|
223330
223868
|
};
|
|
223331
223869
|
const encodes = [];
|
|
@@ -223337,15 +223875,15 @@ import ${JSON.stringify(importPath)}
|
|
|
223337
223875
|
await Promise.all(encodes);
|
|
223338
223876
|
for (let p2 = pages;p2 < 16; p2++) {
|
|
223339
223877
|
for (const light of [false, true]) {
|
|
223340
|
-
const stale =
|
|
223341
|
-
if (
|
|
223342
|
-
|
|
223878
|
+
const stale = join34(outDir, pageFile(stem, p2, light));
|
|
223879
|
+
if (existsSync32(stale) && statSync11(stale).size !== PLACEHOLDER_KTX2.length)
|
|
223880
|
+
writeFileSync24(stale, PLACEHOLDER_KTX2);
|
|
223343
223881
|
}
|
|
223344
223882
|
}
|
|
223345
223883
|
if (!hasLight) {
|
|
223346
|
-
const light =
|
|
223347
|
-
if (
|
|
223348
|
-
|
|
223884
|
+
const light = join34(outDir, pageFile(stem, 0, true));
|
|
223885
|
+
if (existsSync32(light) && statSync11(light).size !== PLACEHOLDER_KTX2.length)
|
|
223886
|
+
writeFileSync24(light, PLACEHOLDER_KTX2);
|
|
223349
223887
|
}
|
|
223350
223888
|
data.texture = pageFile(stem, 0, false);
|
|
223351
223889
|
data.textures = Array.from({ length: pages }, (_, p2) => pageFile(stem, p2, false));
|
|
@@ -223353,19 +223891,19 @@ import ${JSON.stringify(importPath)}
|
|
|
223353
223891
|
data.lightTextures = Array.from({ length: pages }, (_, p2) => pageFile(stem, p2, true));
|
|
223354
223892
|
if (data.volume)
|
|
223355
223893
|
data.volume.file = `${stem}.volume`;
|
|
223356
|
-
|
|
223894
|
+
writeFileSync24(join34(outDir, `${stem}.bake`), JSON.stringify(data, null, 1) + `
|
|
223357
223895
|
`);
|
|
223358
223896
|
if (stem !== "lightmap")
|
|
223359
223897
|
unlinkSync(bakePath);
|
|
223360
|
-
const volumeSrc =
|
|
223361
|
-
const hasVolume = cfg.volumeCell > 0 &&
|
|
223898
|
+
const volumeSrc = join34(outDir, "lightmap.volume");
|
|
223899
|
+
const hasVolume = cfg.volumeCell > 0 && existsSync32(volumeSrc);
|
|
223362
223900
|
if (hasVolume && stem !== "lightmap") {
|
|
223363
|
-
rmSync9(
|
|
223364
|
-
renameSync3(volumeSrc,
|
|
223901
|
+
rmSync9(join34(outDir, `${stem}.volume`), { force: true });
|
|
223902
|
+
renameSync3(volumeSrc, join34(outDir, `${stem}.volume`));
|
|
223365
223903
|
}
|
|
223366
223904
|
logErr(c.dim(` encoded ${pages} page(s)${hasLight ? " + the light atlas" : ""} (UASTC + mips, ${Math.round(ktxKb)} KB from ${Math.round(pngKb)} KB of rasters) in ${((Date.now() - t0) / 1000).toFixed(1)} s`));
|
|
223367
223905
|
logErr(c.green(`✓ baked → ${relative4(root, outDir) || "."}/${stem}.bake + ${data.textures.join(", ")}${hasLight ? ` + ${data.lightTextures.join(", ")}` : ""}${hasVolume ? ` + ${stem}.volume` : ""}`));
|
|
223368
|
-
const rel = (f3) => relative4(root,
|
|
223906
|
+
const rel = (f3) => relative4(root, join34(outDir, f3)).replace(/\\/g, "/");
|
|
223369
223907
|
const assets = (files) => files.length === 1 ? `asset('<rel>/${rel(files[0])}')` : `[${files.map((f3) => `asset('<rel>/${rel(f3)}')`).join(", ")}]`;
|
|
223370
223908
|
logErr(c.dim(` scene file: env.lightmap: { data: asset('<rel>/${rel(`${stem}.bake`)}'), texture: ${assets(data.textures)}${hasLight ? `, light: ${assets(data.lightTextures)}` : ""}${hasVolume ? `, volume: asset('<rel>/${rel(`${stem}.volume`)}')` : ""} } — asset() paths are relative to the scene file`));
|
|
223371
223909
|
return;
|
|
@@ -223395,20 +223933,20 @@ var placeholderVolume = () => {
|
|
|
223395
223933
|
return b;
|
|
223396
223934
|
};
|
|
223397
223935
|
var ensurePlaceholders = (outDir, stem) => {
|
|
223398
|
-
const missing = (f3) => !
|
|
223399
|
-
const bake2 =
|
|
223400
|
-
const ktx2 =
|
|
223401
|
-
const light =
|
|
223402
|
-
const volume =
|
|
223936
|
+
const missing = (f3) => !existsSync32(f3) || statSync11(f3).size === 0;
|
|
223937
|
+
const bake2 = join34(outDir, `${stem}.bake`);
|
|
223938
|
+
const ktx2 = join34(outDir, pageFile(stem, 0, false));
|
|
223939
|
+
const light = join34(outDir, pageFile(stem, 0, true));
|
|
223940
|
+
const volume = join34(outDir, `${stem}.volume`);
|
|
223403
223941
|
if (missing(bake2))
|
|
223404
|
-
|
|
223942
|
+
writeFileSync24(bake2, JSON.stringify({ version: 2, placeholder: true, size: 1, texel: 0, pages: 1, instances: [] }) + `
|
|
223405
223943
|
`);
|
|
223406
223944
|
if (missing(ktx2))
|
|
223407
|
-
|
|
223945
|
+
writeFileSync24(ktx2, PLACEHOLDER_KTX2);
|
|
223408
223946
|
if (missing(light))
|
|
223409
|
-
|
|
223947
|
+
writeFileSync24(light, PLACEHOLDER_KTX2);
|
|
223410
223948
|
if (missing(volume))
|
|
223411
|
-
|
|
223949
|
+
writeFileSync24(volume, placeholderVolume());
|
|
223412
223950
|
};
|
|
223413
223951
|
var lightmap = async (args) => {
|
|
223414
223952
|
const sub = args._[0];
|
|
@@ -223419,9 +223957,9 @@ var lightmap = async (args) => {
|
|
|
223419
223957
|
|
|
223420
223958
|
// src/commands/navmesh.ts
|
|
223421
223959
|
init_manifest();
|
|
223422
|
-
import { existsSync as
|
|
223960
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync5, readdirSync as readdirSync15, readFileSync as readFileSync28, rmSync as rmSync10, statSync as statSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync25 } from "node:fs";
|
|
223423
223961
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
223424
|
-
import { basename as
|
|
223962
|
+
import { basename as basename17, join as join35, relative as relative5, resolve as resolve15 } from "node:path";
|
|
223425
223963
|
init_util();
|
|
223426
223964
|
|
|
223427
223965
|
// ../creator-nav/dist/creatorNav.js
|
|
@@ -223430,16 +223968,16 @@ var createCreatorNav = (() => {
|
|
|
223430
223968
|
var moduleRtn;
|
|
223431
223969
|
var Module = moduleArg;
|
|
223432
223970
|
var readyPromiseResolve, readyPromiseReject;
|
|
223433
|
-
var readyPromise = new Promise((
|
|
223434
|
-
readyPromiseResolve =
|
|
223971
|
+
var readyPromise = new Promise((resolve15, reject) => {
|
|
223972
|
+
readyPromiseResolve = resolve15;
|
|
223435
223973
|
readyPromiseReject = reject;
|
|
223436
223974
|
});
|
|
223437
223975
|
var ENVIRONMENT_IS_WEB = typeof window == "object";
|
|
223438
223976
|
var ENVIRONMENT_IS_WORKER = typeof WorkerGlobalScope != "undefined";
|
|
223439
223977
|
var ENVIRONMENT_IS_NODE = typeof process == "object" && process.versions?.node && process.type != "renderer";
|
|
223440
223978
|
if (ENVIRONMENT_IS_NODE) {
|
|
223441
|
-
const { createRequire:
|
|
223442
|
-
var require2 =
|
|
223979
|
+
const { createRequire: createRequire7 } = await import("module");
|
|
223980
|
+
var require2 = createRequire7(import.meta.url);
|
|
223443
223981
|
}
|
|
223444
223982
|
var arguments_ = [];
|
|
223445
223983
|
var thisProgram = "./this.program";
|
|
@@ -223609,9 +224147,9 @@ var createCreatorNav = (() => {
|
|
|
223609
224147
|
}
|
|
223610
224148
|
var info2 = getWasmImports();
|
|
223611
224149
|
if (Module["instantiateWasm"]) {
|
|
223612
|
-
return new Promise((
|
|
224150
|
+
return new Promise((resolve15, reject) => {
|
|
223613
224151
|
Module["instantiateWasm"](info2, (mod, inst) => {
|
|
223614
|
-
|
|
224152
|
+
resolve15(receiveInstance(mod, inst));
|
|
223615
224153
|
});
|
|
223616
224154
|
});
|
|
223617
224155
|
}
|
|
@@ -223967,14 +224505,14 @@ var createCreatorNav = (() => {
|
|
|
223967
224505
|
var __emscripten_stack_restore = wasmExports["_emscripten_stack_restore"];
|
|
223968
224506
|
var __emscripten_stack_alloc = wasmExports["_emscripten_stack_alloc"];
|
|
223969
224507
|
var _emscripten_stack_get_current = wasmExports["emscripten_stack_get_current"];
|
|
223970
|
-
function
|
|
224508
|
+
function run3() {
|
|
223971
224509
|
if (runDependencies > 0) {
|
|
223972
|
-
dependenciesFulfilled =
|
|
224510
|
+
dependenciesFulfilled = run3;
|
|
223973
224511
|
return;
|
|
223974
224512
|
}
|
|
223975
224513
|
preRun();
|
|
223976
224514
|
if (runDependencies > 0) {
|
|
223977
|
-
dependenciesFulfilled =
|
|
224515
|
+
dependenciesFulfilled = run3;
|
|
223978
224516
|
return;
|
|
223979
224517
|
}
|
|
223980
224518
|
function doRun() {
|
|
@@ -224006,7 +224544,7 @@ var createCreatorNav = (() => {
|
|
|
224006
224544
|
}
|
|
224007
224545
|
}
|
|
224008
224546
|
preInit();
|
|
224009
|
-
|
|
224547
|
+
run3();
|
|
224010
224548
|
moduleRtn = readyPromise;
|
|
224011
224549
|
return moduleRtn;
|
|
224012
224550
|
};
|
|
@@ -224218,13 +224756,13 @@ var num2 = (args, key, fallback, min, max) => {
|
|
|
224218
224756
|
return n2;
|
|
224219
224757
|
};
|
|
224220
224758
|
var guessName = (root) => {
|
|
224221
|
-
const dirs = [
|
|
224759
|
+
const dirs = [join35(root, "src"), root];
|
|
224222
224760
|
for (const dir of dirs) {
|
|
224223
|
-
if (!
|
|
224761
|
+
if (!existsSync33(dir))
|
|
224224
224762
|
continue;
|
|
224225
224763
|
let entries;
|
|
224226
224764
|
try {
|
|
224227
|
-
entries =
|
|
224765
|
+
entries = readdirSync15(dir);
|
|
224228
224766
|
} catch {
|
|
224229
224767
|
continue;
|
|
224230
224768
|
}
|
|
@@ -224232,7 +224770,7 @@ var guessName = (root) => {
|
|
|
224232
224770
|
if (!f3.endsWith(".scene.ts"))
|
|
224233
224771
|
continue;
|
|
224234
224772
|
try {
|
|
224235
|
-
if (/\bnavmesh\s*:/.test(
|
|
224773
|
+
if (/\bnavmesh\s*:/.test(readFileSync28(join35(dir, f3), "utf8")))
|
|
224236
224774
|
return f3.slice(0, -".scene.ts".length);
|
|
224237
224775
|
} catch {}
|
|
224238
224776
|
}
|
|
@@ -224261,7 +224799,7 @@ var writeObj = (path2, tris) => {
|
|
|
224261
224799
|
lines.push(`v ${tris[i2].toFixed(4)} ${tris[i2 + 1].toFixed(4)} ${tris[i2 + 2].toFixed(4)}`);
|
|
224262
224800
|
for (let i2 = 0;i2 < tris.length / 3; i2 += 3)
|
|
224263
224801
|
lines.push(`f ${i2 + 1} ${i2 + 2} ${i2 + 3}`);
|
|
224264
|
-
|
|
224802
|
+
writeFileSync25(path2, lines.join(`
|
|
224265
224803
|
`) + `
|
|
224266
224804
|
`);
|
|
224267
224805
|
};
|
|
@@ -224272,10 +224810,10 @@ var bake2 = async (args) => {
|
|
|
224272
224810
|
} catch {
|
|
224273
224811
|
root = process.cwd();
|
|
224274
224812
|
}
|
|
224275
|
-
const outDir =
|
|
224813
|
+
const outDir = resolve15(root, flagStr(args, "out") ?? "assets/nav");
|
|
224276
224814
|
const entryFlag = flagStr(args, "entry");
|
|
224277
224815
|
const entryFile = entryFlag ?? "main.ts";
|
|
224278
|
-
if (!
|
|
224816
|
+
if (!existsSync33(resolve15(root, entryFile))) {
|
|
224279
224817
|
throw new CliError(`No ${entryFile} in ${root} — pass --entry <file> (the file that builds the level: a scene file with env.navmesh, or code calling NavMesh.load).`);
|
|
224280
224818
|
}
|
|
224281
224819
|
const name = flagStr(args, "name") ?? guessName(root) ?? "scene";
|
|
@@ -224286,10 +224824,10 @@ var bake2 = async (args) => {
|
|
|
224286
224824
|
const logs = flagBool(args, "logs");
|
|
224287
224825
|
const keepGeometry = flagBool(args, "keep-geometry");
|
|
224288
224826
|
const obj = flagBool(args, "obj");
|
|
224289
|
-
|
|
224290
|
-
const navmeshPath =
|
|
224291
|
-
if (!
|
|
224292
|
-
|
|
224827
|
+
mkdirSync23(outDir, { recursive: true });
|
|
224828
|
+
const navmeshPath = join35(outDir, `${name}.navmesh`);
|
|
224829
|
+
if (!existsSync33(navmeshPath))
|
|
224830
|
+
writeFileSync25(navmeshPath, placeholderLnav());
|
|
224293
224831
|
const importPath = "./" + entryFile.replace(/\\/g, "/").replace(/\.tsx?$/, "");
|
|
224294
224832
|
const entryPath = "/.navmesh-entry.ts";
|
|
224295
224833
|
const cfg = { outDir: outDir.replace(/\\/g, "/"), name };
|
|
@@ -224303,8 +224841,8 @@ import ${JSON.stringify(importPath)}
|
|
|
224303
224841
|
extraEntries: [{ path: entryPath, type: "text", text: entryText }],
|
|
224304
224842
|
entryOverride: entryPath
|
|
224305
224843
|
});
|
|
224306
|
-
const scratch = mkdtempSync5(
|
|
224307
|
-
const outPng =
|
|
224844
|
+
const scratch = mkdtempSync5(join35(tmpdir5(), "lecodes-navmesh-"));
|
|
224845
|
+
const outPng = join35(scratch, "frame.png");
|
|
224308
224846
|
note(`Dumping the level's static collision geometry through the desktop host → ${relative5(root, outDir) || "."}/${name}.navgeo…`);
|
|
224309
224847
|
let output = "";
|
|
224310
224848
|
try {
|
|
@@ -224357,10 +224895,10 @@ import ${JSON.stringify(importPath)}
|
|
|
224357
224895
|
}
|
|
224358
224896
|
if (!config)
|
|
224359
224897
|
throw new CliError("The host reported a dump but no `[navmesh] config` line — SDK/host mismatch?");
|
|
224360
|
-
const geoPath =
|
|
224361
|
-
if (!
|
|
224898
|
+
const geoPath = join35(outDir, `${name}.navgeo`);
|
|
224899
|
+
if (!existsSync33(geoPath))
|
|
224362
224900
|
throw new CliError(`The host reported a dump but ${geoPath} is missing.`);
|
|
224363
|
-
const geo = readNgeo(new Uint8Array(
|
|
224901
|
+
const geo = readNgeo(new Uint8Array(readFileSync28(geoPath)));
|
|
224364
224902
|
const params = {
|
|
224365
224903
|
agentRadius: num2(args, "radius", config.agent.radius, 0.05, 10),
|
|
224366
224904
|
agentHeight: num2(args, "height", config.agent.height, 0.1, 20),
|
|
@@ -224406,20 +224944,20 @@ import ${JSON.stringify(importPath)}
|
|
|
224406
224944
|
if (result.polys === 0) {
|
|
224407
224945
|
throw new CliError("The build produced no polygons — nothing walkable for this agent. Check the cell size against the level's scale, the slope, and that the floor is a STATIC body (Shape + Physics { motion: 'static' }).");
|
|
224408
224946
|
}
|
|
224409
|
-
|
|
224947
|
+
writeFileSync25(navmeshPath, result.data);
|
|
224410
224948
|
if (obj) {
|
|
224411
|
-
const objPath =
|
|
224949
|
+
const objPath = join35(outDir, `${name}.navmesh.obj`);
|
|
224412
224950
|
writeObj(objPath, navMeshTriangles(mod, result.data));
|
|
224413
224951
|
logErr(c.dim(` wrote ${relative5(root, objPath)} (the polygon mesh, for inspection)`));
|
|
224414
224952
|
}
|
|
224415
224953
|
if (!keepGeometry)
|
|
224416
224954
|
unlinkSync2(geoPath);
|
|
224417
|
-
const kb = Math.round(
|
|
224955
|
+
const kb = Math.round(statSync12(navmeshPath).size / 1024);
|
|
224418
224956
|
const b = result.bounds;
|
|
224419
224957
|
logErr(c.dim(` ${result.tiles} tiles, ${result.polys} polygons, ${(b.max[0] - b.min[0]).toFixed(0)} × ${(b.max[2] - b.min[2]).toFixed(0)} m, ${result.time.toFixed(1)} s`));
|
|
224420
224958
|
logErr(c.green(`✓ baked → ${relative5(root, navmeshPath).replace(/\\/g, "/")} (${kb} KB)`));
|
|
224421
224959
|
if (!config.data) {
|
|
224422
|
-
logErr(c.dim(` load it with env.navmesh: { data: asset('<rel>/${
|
|
224960
|
+
logErr(c.dim(` load it with env.navmesh: { data: asset('<rel>/${basename17(navmeshPath)}') } in the scene file, or NavMesh.load(scene, { data: asset(...) })`));
|
|
224423
224961
|
}
|
|
224424
224962
|
};
|
|
224425
224963
|
var navmesh = async (args) => {
|
|
@@ -224446,8 +224984,131 @@ var login = async (args) => {
|
|
|
224446
224984
|
|
|
224447
224985
|
// src/commands/shaders.ts
|
|
224448
224986
|
init_manifest();
|
|
224449
|
-
import { readFileSync as
|
|
224987
|
+
import { readFileSync as readFileSync30 } from "node:fs";
|
|
224988
|
+
import { relative as relative7 } from "node:path";
|
|
224989
|
+
init_util();
|
|
224990
|
+
|
|
224991
|
+
// src/commands/shadersNew.ts
|
|
224992
|
+
init_compile();
|
|
224993
|
+
init_distRoot();
|
|
224450
224994
|
init_util();
|
|
224995
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync24, readdirSync as readdirSync16, readFileSync as readFileSync29, writeFileSync as writeFileSync26 } from "node:fs";
|
|
224996
|
+
import { basename as basename18, join as join36, relative as relative6, resolve as resolve16 } from "node:path";
|
|
224997
|
+
var engineMaterialsDir = () => {
|
|
224998
|
+
const candidates = [
|
|
224999
|
+
join36(distRoot, "runtime", "materials"),
|
|
225000
|
+
join36(distRoot, "..", "creator-gl", "materials", "src")
|
|
225001
|
+
];
|
|
225002
|
+
for (const c4 of candidates)
|
|
225003
|
+
if (existsSync34(join36(c4, "unlit.mat")))
|
|
225004
|
+
return c4;
|
|
225005
|
+
return null;
|
|
225006
|
+
};
|
|
225007
|
+
var engineMaterialNames = () => {
|
|
225008
|
+
const dir = engineMaterialsDir();
|
|
225009
|
+
if (!dir)
|
|
225010
|
+
return [];
|
|
225011
|
+
return readdirSync16(dir).filter((f3) => f3.endsWith(".mat") && !f3.startsWith("camera") && f3 !== "shader360.mat").map((f3) => f3.slice(0, -4)).sort();
|
|
225012
|
+
};
|
|
225013
|
+
var NAME_RE = /^[a-z][a-z0-9_-]*$/i;
|
|
225014
|
+
var matName = (name) => name.replace(/-/g, "_");
|
|
225015
|
+
var starterMat = (name) => `// ${name}.mat — a custom material (lecodes shaders new).
|
|
225016
|
+
//
|
|
225017
|
+
// Filament's material language: the \`material\` block declares the parameters and the render state,
|
|
225018
|
+
// \`fragment\` computes the surface. Read it with \`Material.load(asset('./${name}.mat'))\` or the
|
|
225019
|
+
// ${name}.material.ts asset beside it; set parameters with material.set('tint', '#ff8800').
|
|
225020
|
+
// \`lecodes shaders compile\` (or \`lecodes dev\`) compiles it for every host into .lecodes/shaders.
|
|
225021
|
+
//
|
|
225022
|
+
// The comment on a parameter's line is what the scene editor shows for it:
|
|
225023
|
+
// @color a colour picker (also inferred from a name like tint / color / emissive)
|
|
225024
|
+
// @range(min, max) a slider, @step(s) its step, @default(v) the value a new material starts with
|
|
225025
|
+
// the rest of the line is the tooltip
|
|
225026
|
+
|
|
225027
|
+
material {
|
|
225028
|
+
name : ${matName(name)},
|
|
225029
|
+
shadingModel : unlit,
|
|
225030
|
+
parameters : [
|
|
225031
|
+
{ type : float3, name : tint }, // @color @default(#ffffff) the flat colour
|
|
225032
|
+
{ type : float, name : glow }, // @range(0, 2) @step(0.05) @default(1) brightness multiplier
|
|
225033
|
+
{ type : sampler2d, name : map } // the texture, white when unset
|
|
225034
|
+
],
|
|
225035
|
+
requires : [
|
|
225036
|
+
uv0
|
|
225037
|
+
]
|
|
225038
|
+
}
|
|
225039
|
+
|
|
225040
|
+
fragment {
|
|
225041
|
+
void material(inout MaterialInputs material) {
|
|
225042
|
+
prepareMaterial(material);
|
|
225043
|
+
vec4 base = texture(materialParams_map, getUV0());
|
|
225044
|
+
material.baseColor.rgb = base.rgb * materialParams.tint * materialParams.glow;
|
|
225045
|
+
}
|
|
225046
|
+
}
|
|
225047
|
+
`;
|
|
225048
|
+
var refMat = (name, ref, source) => {
|
|
225049
|
+
const renamed = source.replace(/(\bname\s*:\s*)([A-Za-z_][A-Za-z0-9_]*)/, `$1${matName(name)}`);
|
|
225050
|
+
return `// ${name}.mat — started from the engine's ${ref}.mat (lecodes shaders new --ref ${ref}); edit freely,
|
|
225051
|
+
// the engine keeps its own. \`lecodes shaders compile\` builds it for every host into .lecodes/shaders.
|
|
225052
|
+
// Load it with Material.load(asset('./${name}.mat')) or the ${name}.material.ts asset beside it.
|
|
225053
|
+
|
|
225054
|
+
${renamed}`;
|
|
225055
|
+
};
|
|
225056
|
+
var materialAsset = (name, mat) => {
|
|
225057
|
+
const schema = parseShaderSchema(mat);
|
|
225058
|
+
const lines = [];
|
|
225059
|
+
for (const p2 of schema.params) {
|
|
225060
|
+
if (p2.editor === "texture") {
|
|
225061
|
+
lines.push(` // ${p2.name}: asset('./texture.png'), // a sampler takes an image asset`);
|
|
225062
|
+
continue;
|
|
225063
|
+
}
|
|
225064
|
+
if (p2.default === null || p2.default === undefined)
|
|
225065
|
+
continue;
|
|
225066
|
+
lines.push(` ${p2.name}: ${JSON.stringify(p2.default)},${p2.tooltip ? ` // ${p2.tooltip}` : ""}`);
|
|
225067
|
+
}
|
|
225068
|
+
return `// ${name}.material.ts — the material as a project asset: ONE shared instance a scene file takes as
|
|
225069
|
+
// \`material: ${camel(name)}\` and code loads with \`await ${camel(name)}.load()\`. The parameters and their
|
|
225070
|
+
// widgets come from the annotations in ${name}.mat.
|
|
225071
|
+
export default defineMaterial({
|
|
225072
|
+
shader: asset('./${name}.mat'),
|
|
225073
|
+
params: {
|
|
225074
|
+
${lines.join(`
|
|
225075
|
+
`)}
|
|
225076
|
+
},
|
|
225077
|
+
})
|
|
225078
|
+
`;
|
|
225079
|
+
};
|
|
225080
|
+
var camel = (name) => name.replace(/[-_]+(.)/g, (_, ch) => ch.toUpperCase());
|
|
225081
|
+
var writeNewShader = (o2) => {
|
|
225082
|
+
const name = o2.name.endsWith(".mat") ? o2.name.slice(0, -4) : o2.name;
|
|
225083
|
+
if (!NAME_RE.test(name))
|
|
225084
|
+
throw new CliError(`"${o2.name}" is not a shader name — letters, digits, - and _ (starting with a letter), e.g. hologram or rim-light`);
|
|
225085
|
+
const dir = resolve16(o2.cwd ?? process.cwd(), o2.dir ?? ".");
|
|
225086
|
+
const matPath = join36(dir, `${name}.mat`), assetPath = join36(dir, `${name}.material.ts`);
|
|
225087
|
+
if (!o2.force) {
|
|
225088
|
+
for (const p2 of [matPath, assetPath])
|
|
225089
|
+
if (existsSync34(p2))
|
|
225090
|
+
throw new CliError(`${relative6(o2.cwd ?? process.cwd(), p2) || basename18(p2)} exists — pick another name or pass --force to overwrite`);
|
|
225091
|
+
}
|
|
225092
|
+
let mat;
|
|
225093
|
+
let ref = null;
|
|
225094
|
+
if (o2.ref) {
|
|
225095
|
+
ref = o2.ref.endsWith(".mat") ? o2.ref.slice(0, -4) : o2.ref;
|
|
225096
|
+
const srcDir = engineMaterialsDir();
|
|
225097
|
+
if (!srcDir)
|
|
225098
|
+
throw new CliError("this CLI carries no engine material sources (runtime/materials) — --ref needs a CLI built with them");
|
|
225099
|
+
const known = engineMaterialNames();
|
|
225100
|
+
if (!known.includes(ref))
|
|
225101
|
+
throw new CliError(`no engine material "${ref}". Known: ${known.join(", ")}`);
|
|
225102
|
+
mat = refMat(name, ref, readFileSync29(join36(srcDir, `${ref}.mat`), "utf8"));
|
|
225103
|
+
} else
|
|
225104
|
+
mat = starterMat(name);
|
|
225105
|
+
mkdirSync24(dir, { recursive: true });
|
|
225106
|
+
writeFileSync26(matPath, mat);
|
|
225107
|
+
writeFileSync26(assetPath, materialAsset(name, mat));
|
|
225108
|
+
return { mat: matPath, asset: assetPath, ref };
|
|
225109
|
+
};
|
|
225110
|
+
|
|
225111
|
+
// src/commands/shaders.ts
|
|
224451
225112
|
init_compile();
|
|
224452
225113
|
var collectShaders = () => {
|
|
224453
225114
|
let root;
|
|
@@ -224478,7 +225139,7 @@ var status2 = async () => {
|
|
|
224478
225139
|
if (matcId === null)
|
|
224479
225140
|
state = c.dim("unknown (matc not downloaded)");
|
|
224480
225141
|
else {
|
|
224481
|
-
const text =
|
|
225142
|
+
const text = readFileSync30(shader.absPath, "utf8");
|
|
224482
225143
|
if (shaderIsCompiled(root, text, matcId))
|
|
224483
225144
|
state = "compiled";
|
|
224484
225145
|
else {
|
|
@@ -224487,7 +225148,7 @@ var status2 = async () => {
|
|
|
224487
225148
|
}
|
|
224488
225149
|
}
|
|
224489
225150
|
log(` ${shader.path.padEnd(width)} ${state}`);
|
|
224490
|
-
const schema = parseShaderSchema(
|
|
225151
|
+
const schema = parseShaderSchema(readFileSync30(shader.absPath, "utf8"));
|
|
224491
225152
|
for (const p2 of schema.params) {
|
|
224492
225153
|
const range = p2.min !== undefined ? ` [${p2.min}..${p2.max}]` : "";
|
|
224493
225154
|
const def = p2.default !== null && p2.default !== undefined ? ` = ${JSON.stringify(p2.default)}` : "";
|
|
@@ -224530,7 +225191,7 @@ var compile3 = async (args) => {
|
|
|
224530
225191
|
}
|
|
224531
225192
|
const matcExe = await resolveMatcExe();
|
|
224532
225193
|
const matcId = matcIdentity(matcExe);
|
|
224533
|
-
const wasCompiled = new Map(shaders.map((s2) => [s2.path, shaderIsCompiled(root,
|
|
225194
|
+
const wasCompiled = new Map(shaders.map((s2) => [s2.path, shaderIsCompiled(root, readFileSync30(s2.absPath, "utf8"), matcId)]));
|
|
224534
225195
|
const errors2 = [];
|
|
224535
225196
|
for (const shader of shaders) {
|
|
224536
225197
|
try {
|
|
@@ -224552,6 +225213,16 @@ ${c.red("✗")} ${e2}`);
|
|
|
224552
225213
|
const scope = platforms ? ` [${platforms.join(", ")}]` : "";
|
|
224553
225214
|
note(`${shaders.length} shader${shaders.length === 1 ? "" : "s"} compiled${scope} → ${shadersCacheDir(root)}`);
|
|
224554
225215
|
};
|
|
225216
|
+
var newShader = (args) => {
|
|
225217
|
+
const name = args._[1];
|
|
225218
|
+
if (!name)
|
|
225219
|
+
throw new CliError(`lecodes shaders new <name> [--ref ${engineMaterialNames().join("|") || "lit"}] [--dir <folder>] [--force]`);
|
|
225220
|
+
const res = writeNewShader({ name, ref: flagStr(args, "ref"), dir: flagStr(args, "dir"), force: args.flags.force === true });
|
|
225221
|
+
const rel = (p2) => relative7(process.cwd(), p2);
|
|
225222
|
+
log(`${rel(res.mat)} ${c.dim(res.ref ? `(from the engine's ${res.ref}.mat)` : "(the starter template)")}`);
|
|
225223
|
+
log(`${rel(res.asset)} ${c.dim("(defineMaterial asset — a scene takes it as `material:`)")}`);
|
|
225224
|
+
note(`next: edit it, then \`lecodes shaders compile\` (or just \`lecodes dev\`); in code: Material.load(asset('./${name.replace(/\.mat$/, "")}.mat'))`);
|
|
225225
|
+
};
|
|
224555
225226
|
var shaders = async (args) => {
|
|
224556
225227
|
const sub = args._[0];
|
|
224557
225228
|
if (sub === undefined)
|
|
@@ -224560,12 +225231,14 @@ var shaders = async (args) => {
|
|
|
224560
225231
|
return compile3(args);
|
|
224561
225232
|
if (sub === "update")
|
|
224562
225233
|
return updateMatc();
|
|
224563
|
-
|
|
225234
|
+
if (sub === "new")
|
|
225235
|
+
return newShader(args);
|
|
225236
|
+
throw new CliError(`Unknown subcommand "shaders ${sub}". Use \`lecodes shaders\` (status), \`lecodes shaders compile [--platform a,b]\`, \`lecodes shaders new <name> [--ref lit]\` or \`lecodes shaders update\`.`);
|
|
224564
225237
|
};
|
|
224565
225238
|
|
|
224566
225239
|
// src/commands/render.ts
|
|
224567
|
-
import { mkdirSync as
|
|
224568
|
-
import { dirname as
|
|
225240
|
+
import { mkdirSync as mkdirSync25, readFileSync as readFileSync32, writeFileSync as writeFileSync27 } from "node:fs";
|
|
225241
|
+
import { dirname as dirname17, resolve as resolve19 } from "node:path";
|
|
224569
225242
|
|
|
224570
225243
|
// src/compile/headlessBundle.ts
|
|
224571
225244
|
init_api();
|
|
@@ -224649,23 +225322,23 @@ var compileHeadlessBundle = async (args, opts = {}) => {
|
|
|
224649
225322
|
// src/compile/screenEntry.ts
|
|
224650
225323
|
init_designMeta();
|
|
224651
225324
|
init_util();
|
|
224652
|
-
import { existsSync as
|
|
224653
|
-
import { isAbsolute, join as
|
|
225325
|
+
import { existsSync as existsSync35, readFileSync as readFileSync31, statSync as statSync13 } from "node:fs";
|
|
225326
|
+
import { isAbsolute, join as join37, relative as relative8, resolve as resolve17 } from "node:path";
|
|
224654
225327
|
var SCREEN_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs"];
|
|
224655
225328
|
var SCREEN_ENTRY_PATH = "/.screen-entry.ts";
|
|
224656
225329
|
var DEFAULT_DESIGN_DIR = "design";
|
|
224657
225330
|
var screenArg = (args) => args._[0];
|
|
224658
225331
|
var hasSeparator = (file) => file.includes("/") || file.includes("\\");
|
|
224659
225332
|
var findScreenFile = (root, file, dirFlag) => {
|
|
224660
|
-
const candidates = [
|
|
225333
|
+
const candidates = [resolve17(process.cwd(), file)];
|
|
224661
225334
|
if (!hasSeparator(file)) {
|
|
224662
|
-
candidates.push(
|
|
225335
|
+
candidates.push(join37(root, (dirFlag ?? DEFAULT_DESIGN_DIR).replace(/[/\\]+$/, ""), "screens", file));
|
|
224663
225336
|
}
|
|
224664
225337
|
for (const base of candidates) {
|
|
224665
|
-
if (
|
|
225338
|
+
if (existsSync35(base) && statSync13(base).isFile())
|
|
224666
225339
|
return base;
|
|
224667
225340
|
for (const ext2 of SCREEN_EXTS)
|
|
224668
|
-
if (
|
|
225341
|
+
if (existsSync35(base + ext2))
|
|
224669
225342
|
return base + ext2;
|
|
224670
225343
|
}
|
|
224671
225344
|
throw new CliError(`No screen file at "${file}".` + (hasSeparator(file) ? "" : ` Pass a path, or a screen id that exists in ${dirFlag ?? DEFAULT_DESIGN_DIR}/screens/.`));
|
|
@@ -224674,17 +225347,17 @@ var designDirOf = (root, key) => {
|
|
|
224674
225347
|
const m = /^\/(.*)screens\/([^/]+)\.[^./]+$/.exec(key);
|
|
224675
225348
|
if (!m)
|
|
224676
225349
|
return null;
|
|
224677
|
-
const dir =
|
|
224678
|
-
return
|
|
225350
|
+
const dir = join37(root, m[1]);
|
|
225351
|
+
return existsSync35(join37(dir, "meta.json")) ? { dir, id: m[2] } : null;
|
|
224679
225352
|
};
|
|
224680
225353
|
var resolveScreenTarget = (root, file, opts = {}) => {
|
|
224681
225354
|
const abs = findScreenFile(root, file, opts.dir);
|
|
224682
|
-
const rel =
|
|
225355
|
+
const rel = relative8(root, abs);
|
|
224683
225356
|
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
224684
225357
|
throw new CliError(`"${file}" is outside the project (${root}) — a screen renders as part of its project, so its ` + `imports and asset() files resolve from the same tree.`);
|
|
224685
225358
|
}
|
|
224686
225359
|
const key = toKey(rel);
|
|
224687
|
-
if (!/^\s*export\s+default\b/m.test(
|
|
225360
|
+
if (!/^\s*export\s+default\b/m.test(readFileSync31(abs, "utf8"))) {
|
|
224688
225361
|
throw new CliError(`${key.slice(1)} has no default export. A screen file ends with ` + `\`export default UIScreen([...])\` — or \`export default (state) => UIScreen([...])\` for screen states.`);
|
|
224689
225362
|
}
|
|
224690
225363
|
const invoke = opts.state !== undefined ? `screen(${JSON.stringify(opts.state)})` : "screen()";
|
|
@@ -224723,11 +225396,11 @@ init_manifest();
|
|
|
224723
225396
|
|
|
224724
225397
|
// src/desktopScript.ts
|
|
224725
225398
|
init_util();
|
|
224726
|
-
import { resolve as
|
|
225399
|
+
import { resolve as resolve18 } from "node:path";
|
|
224727
225400
|
var isPoint = (v) => typeof v === "object" && v !== null && typeof v.x === "number" && typeof v.y === "number";
|
|
224728
225401
|
var FRAME_MS = 16;
|
|
224729
225402
|
var ACTIONS = ["tap", "type", "fill", "scroll", "wait", "frames", "drag", "hold", "key", "keyDown", "keyUp", "look", "mouseTo", "wheel", "stick", "gamepad", "camera", "view"];
|
|
224730
|
-
var SEMANTIC_EXPECT = ["text", "notText", "node", "notNode", "screen", "value"];
|
|
225403
|
+
var SEMANTIC_EXPECT = ["text", "notText", "node", "notNode", "screen", "value", "audio"];
|
|
224731
225404
|
var VIEW_NAMES = ["iso", "top", "front", "side", "back"];
|
|
224732
225405
|
var isVec3 = (v) => Array.isArray(v) && v.length === 3 && v.every((n2) => typeof n2 === "number");
|
|
224733
225406
|
var pt = (p2) => `(${p2.x},${p2.y})`;
|
|
@@ -224924,7 +225597,7 @@ var translateScenario = (rawSteps, opts) => {
|
|
|
224924
225597
|
if (s2.screenshot !== undefined) {
|
|
224925
225598
|
if (typeof s2.screenshot !== "string")
|
|
224926
225599
|
throw new CliError(`${at}: "screenshot" must be a path.`);
|
|
224927
|
-
plan.screenshot =
|
|
225600
|
+
plan.screenshot = resolve18(opts.outDir, s2.screenshot);
|
|
224928
225601
|
plan.lines.push(emit(`frames 2`));
|
|
224929
225602
|
plan.lines.push(emit(`shot ${plan.screenshot}`));
|
|
224930
225603
|
budgetMs += 3 * FRAME_MS;
|
|
@@ -225004,6 +225677,7 @@ var evaluateDesktopRun = (plan, output) => {
|
|
|
225004
225677
|
|
|
225005
225678
|
// src/commands/render.ts
|
|
225006
225679
|
init_util();
|
|
225680
|
+
init_peerInstall();
|
|
225007
225681
|
var DEVICE_SAFE_AREA = {
|
|
225008
225682
|
none: { insets: [0, 0, 0, 0], bars: [] },
|
|
225009
225683
|
iphone: { insets: [59, 0, 34, 0], bars: [] },
|
|
@@ -225024,18 +225698,12 @@ var resolveSafeAreaChoice = (explicit, device) => {
|
|
|
225024
225698
|
}
|
|
225025
225699
|
return;
|
|
225026
225700
|
};
|
|
225027
|
-
var requireRenderer = async () => {
|
|
225028
|
-
try {
|
|
225029
|
-
return await import("lecodes-renderer/headless");
|
|
225030
|
-
} catch {
|
|
225031
|
-
throw new CliError("The renderer isn't installed. Run: npm install -g lecodes-renderer");
|
|
225032
|
-
}
|
|
225033
|
-
};
|
|
225701
|
+
var requireRenderer = async () => loadPeer("lecodes-renderer", "headless", { for: "lecodes render" });
|
|
225034
225702
|
var fileScenarioIO = (dir) => ({
|
|
225035
225703
|
write: (relPath, data) => {
|
|
225036
|
-
const abs =
|
|
225037
|
-
|
|
225038
|
-
|
|
225704
|
+
const abs = resolve19(dir, relPath);
|
|
225705
|
+
mkdirSync25(dirname17(abs), { recursive: true });
|
|
225706
|
+
writeFileSync27(abs, data);
|
|
225039
225707
|
}
|
|
225040
225708
|
});
|
|
225041
225709
|
var resolveFixtures = (value, baseDir) => {
|
|
@@ -225043,9 +225711,9 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
225043
225711
|
return;
|
|
225044
225712
|
let map = value;
|
|
225045
225713
|
if (typeof value === "string") {
|
|
225046
|
-
const abs =
|
|
225714
|
+
const abs = resolve19(baseDir, value);
|
|
225047
225715
|
try {
|
|
225048
|
-
map = JSON.parse(
|
|
225716
|
+
map = JSON.parse(readFileSync32(abs, "utf8"));
|
|
225049
225717
|
} catch (e2) {
|
|
225050
225718
|
throw new CliError(`Can't read fixtures "${value}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
225051
225719
|
}
|
|
@@ -225058,7 +225726,7 @@ var resolveFixtures = (value, baseDir) => {
|
|
|
225058
225726
|
var readScenarioFile = (path2) => {
|
|
225059
225727
|
let raw;
|
|
225060
225728
|
try {
|
|
225061
|
-
raw = JSON.parse(
|
|
225729
|
+
raw = JSON.parse(readFileSync32(resolve19(path2), "utf8"));
|
|
225062
225730
|
} catch (e2) {
|
|
225063
225731
|
throw new CliError(`Can't read scenario "${path2}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
225064
225732
|
}
|
|
@@ -225109,23 +225777,25 @@ var renderDesktop = async (args) => {
|
|
|
225109
225777
|
if ((camera || view) && scenario)
|
|
225110
225778
|
throw new CliError('--camera/--view apply to a plain render; in a --script run use a { "view": "iso" } or { "camera": { "at", "lookAt" } } step before the screenshot.');
|
|
225111
225779
|
const desktopRenderer = resolveDesktopRenderer(root, args);
|
|
225780
|
+
const { dirs: pluginDirs } = desktopPluginLibraries(root);
|
|
225112
225781
|
const { js, resources } = await buildBundle(args, undefined, {
|
|
225113
225782
|
localAssets: true,
|
|
225114
225783
|
desktopRenderer,
|
|
225115
225784
|
extraEntries: screen2 ? [screen2.entry] : undefined,
|
|
225116
225785
|
entryOverride: screen2?.entryPath
|
|
225117
225786
|
});
|
|
225118
|
-
|
|
225787
|
+
mkdirSync25(dirname17(resolve19(pngFlag)), { recursive: true });
|
|
225119
225788
|
if (scenario) {
|
|
225120
225789
|
const outDir = flagStr(args, "out-dir") ?? ".";
|
|
225121
225790
|
const plan = translateScenario(scenario.steps, { outDir, headroomFrames: frames });
|
|
225122
225791
|
for (const st of plan.steps)
|
|
225123
225792
|
if (st.screenshot)
|
|
225124
|
-
|
|
225793
|
+
mkdirSync25(dirname17(st.screenshot), { recursive: true });
|
|
225125
225794
|
const timeoutMs2 = Number(flagStr(args, "timeout")) || 60000 + plan.steps.length * 5000;
|
|
225126
225795
|
note(`Running ${plan.steps.length} step${plan.steps.length === 1 ? "" : "s"} (native)…`);
|
|
225127
225796
|
const { output } = await runDesktopRender(js, resources, {
|
|
225128
225797
|
renderer: desktopRenderer,
|
|
225798
|
+
pluginDirs,
|
|
225129
225799
|
width,
|
|
225130
225800
|
height,
|
|
225131
225801
|
frames: plan.frameCap,
|
|
@@ -225158,6 +225828,7 @@ var renderDesktop = async (args) => {
|
|
|
225158
225828
|
note(screen2 ? `Rendering ${screen2.label} (native)…` : "Rendering (native)…");
|
|
225159
225829
|
await runDesktopRender(js, resources, {
|
|
225160
225830
|
renderer: desktopRenderer,
|
|
225831
|
+
pluginDirs,
|
|
225161
225832
|
width,
|
|
225162
225833
|
height,
|
|
225163
225834
|
frames,
|
|
@@ -225225,7 +225896,7 @@ var render = async (args) => {
|
|
|
225225
225896
|
const settleStr = flagStr(args, "settle") ?? (scenario?.settle !== undefined ? String(scenario.settle) : undefined);
|
|
225226
225897
|
const timeStr = flagStr(args, "time");
|
|
225227
225898
|
const onConsole = flagBool(args, "logs") ? (level, text2) => logErr(`${c.dim(`[${level}]`)} ${text2}`) : undefined;
|
|
225228
|
-
const envelopeFixtures = resolveFixtures(scenario?.fixtures, scriptFlag !== undefined ?
|
|
225899
|
+
const envelopeFixtures = resolveFixtures(scenario?.fixtures, scriptFlag !== undefined ? dirname17(resolve19(scriptFlag)) : ".");
|
|
225229
225900
|
const flagFixtures = resolveFixtures(flagStr(args, "fixtures"), ".");
|
|
225230
225901
|
const fixtures = envelopeFixtures || flagFixtures ? { ...envelopeFixtures, ...flagFixtures } : undefined;
|
|
225231
225902
|
if (fixtures) {
|
|
@@ -225264,7 +225935,7 @@ var render = async (args) => {
|
|
|
225264
225935
|
${c.red(st.error ?? "failed")}`);
|
|
225265
225936
|
}
|
|
225266
225937
|
if (!result2.ok && result2.failureArtifacts.length > 0) {
|
|
225267
|
-
logErr(c.dim(` failing frame: ${result2.failureArtifacts.map((p2) =>
|
|
225938
|
+
logErr(c.dim(` failing frame: ${result2.failureArtifacts.map((p2) => resolve19(outDir, p2)).join(", ")}`));
|
|
225268
225939
|
}
|
|
225269
225940
|
logErr(result2.ok ? c.green(`✓ ${result2.steps.length}/${steps.length} steps passed`) : c.red(`✗ failed at step ${result2.steps.length} of ${steps.length}`));
|
|
225270
225941
|
return done((cb) => cb(), result2.ok ? 0 : 1);
|
|
@@ -225285,10 +225956,10 @@ var render = async (args) => {
|
|
|
225285
225956
|
for (const w of [...warnings, ...mapWarnings])
|
|
225286
225957
|
warnErr(w);
|
|
225287
225958
|
const pngPath = mapFlag.endsWith(".png") ? mapFlag : mapFlag + ".png";
|
|
225288
|
-
|
|
225289
|
-
|
|
225959
|
+
mkdirSync25(dirname17(resolve19(pngPath)), { recursive: true });
|
|
225960
|
+
writeFileSync27(resolve19(pngPath), png2);
|
|
225290
225961
|
const jsonPath = pngPath.slice(0, -4) + ".json";
|
|
225291
|
-
|
|
225962
|
+
writeFileSync27(resolve19(jsonPath), JSON.stringify(data, null, 2));
|
|
225292
225963
|
note(`Wrote ${pngPath} + ${jsonPath}`);
|
|
225293
225964
|
return done((cb) => cb());
|
|
225294
225965
|
}
|
|
@@ -225308,8 +225979,8 @@ var render = async (args) => {
|
|
|
225308
225979
|
if (isNullSink(pngFlag)) {
|
|
225309
225980
|
note(`Discarded PNG (${pngFlag})`);
|
|
225310
225981
|
} else {
|
|
225311
|
-
|
|
225312
|
-
|
|
225982
|
+
mkdirSync25(dirname17(resolve19(pngFlag)), { recursive: true });
|
|
225983
|
+
writeFileSync27(resolve19(pngFlag), png2);
|
|
225313
225984
|
note(`Wrote ${pngFlag} (${png2.length} bytes, ${outW}x${outH}${clip !== undefined ? ", clipped" : ""})`);
|
|
225314
225985
|
}
|
|
225315
225986
|
return done((cb) => cb());
|
|
@@ -225324,7 +225995,7 @@ var render = async (args) => {
|
|
|
225324
225995
|
return done((cb) => cb());
|
|
225325
225996
|
}
|
|
225326
225997
|
if (out) {
|
|
225327
|
-
|
|
225998
|
+
writeFileSync27(resolve19(out), result.text);
|
|
225328
225999
|
note(`Wrote ${out} (${result.text.length} bytes)`);
|
|
225329
226000
|
return done((cb) => cb());
|
|
225330
226001
|
}
|
|
@@ -225339,8 +226010,8 @@ init_api();
|
|
|
225339
226010
|
init_config();
|
|
225340
226011
|
init_manifest();
|
|
225341
226012
|
init_util();
|
|
225342
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
225343
|
-
import { join as
|
|
226013
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync36, readFileSync as readFileSync33, writeFileSync as writeFileSync28 } from "node:fs";
|
|
226014
|
+
import { join as join38 } from "node:path";
|
|
225344
226015
|
var ENV_VAR = "LECODES_PUSH_KEY";
|
|
225345
226016
|
var context = () => {
|
|
225346
226017
|
const root = findProjectRoot(process.cwd());
|
|
@@ -225484,11 +226155,11 @@ var pnKeysList = async (ctx) => {
|
|
|
225484
226155
|
};
|
|
225485
226156
|
var writeEnv = (root, key) => {
|
|
225486
226157
|
const touched = [];
|
|
225487
|
-
const envPath =
|
|
226158
|
+
const envPath = join38(root, ".env");
|
|
225488
226159
|
const line = `${ENV_VAR}=${key}`;
|
|
225489
|
-
const existing =
|
|
226160
|
+
const existing = existsSync36(envPath) ? readFileSync33(envPath, "utf8") : "";
|
|
225490
226161
|
if (new RegExp(`^${ENV_VAR}=`, "m").test(existing)) {
|
|
225491
|
-
|
|
226162
|
+
writeFileSync28(envPath, existing.replace(new RegExp(`^${ENV_VAR}=.*$`, "m"), line));
|
|
225492
226163
|
} else {
|
|
225493
226164
|
const separator = !existing || existing.endsWith(`
|
|
225494
226165
|
`) ? "" : `
|
|
@@ -225497,8 +226168,8 @@ var writeEnv = (root, key) => {
|
|
|
225497
226168
|
`);
|
|
225498
226169
|
}
|
|
225499
226170
|
touched.push(".env");
|
|
225500
|
-
const ignorePath =
|
|
225501
|
-
const ignore =
|
|
226171
|
+
const ignorePath = join38(root, ".gitignore");
|
|
226172
|
+
const ignore = existsSync36(ignorePath) ? readFileSync33(ignorePath, "utf8") : "";
|
|
225502
226173
|
if (!ignore.split(`
|
|
225503
226174
|
`).some((l2) => l2.trim() === ".env")) {
|
|
225504
226175
|
const separator = !ignore || ignore.endsWith(`
|
|
@@ -225597,8 +226268,8 @@ var pn = async (args) => {
|
|
|
225597
226268
|
init_api();
|
|
225598
226269
|
init_config();
|
|
225599
226270
|
init_manifest();
|
|
225600
|
-
import { existsSync as
|
|
225601
|
-
import { dirname as
|
|
226271
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync26, rmSync as rmSync11, writeFileSync as writeFileSync29 } from "node:fs";
|
|
226272
|
+
import { dirname as dirname18, join as join39 } from "node:path";
|
|
225602
226273
|
init_types4();
|
|
225603
226274
|
init_localFiles();
|
|
225604
226275
|
init_util();
|
|
@@ -225621,8 +226292,8 @@ var pull = async (args) => {
|
|
|
225621
226292
|
const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a2) => a2.id === id)?.type !== "folder").map(([, path2]) => path2));
|
|
225622
226293
|
for (const path2 of Object.keys(manifest.files)) {
|
|
225623
226294
|
if (!remotePaths.has(path2)) {
|
|
225624
|
-
const abs =
|
|
225625
|
-
if (
|
|
226295
|
+
const abs = join39(root, ...path2.replace(/^\//, "").split("/"));
|
|
226296
|
+
if (existsSync37(abs))
|
|
225626
226297
|
rmSync11(abs);
|
|
225627
226298
|
}
|
|
225628
226299
|
}
|
|
@@ -225660,18 +226331,18 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
225660
226331
|
const files = { ...manifest.files };
|
|
225661
226332
|
for (const asset of targets) {
|
|
225662
226333
|
const key = pathById.get(asset.id);
|
|
225663
|
-
const abs =
|
|
225664
|
-
|
|
226334
|
+
const abs = join39(root, ...key.replace(/^\//, "").split("/"));
|
|
226335
|
+
mkdirSync26(dirname18(abs), { recursive: true });
|
|
225665
226336
|
let sha;
|
|
225666
226337
|
if (asset.type === "file") {
|
|
225667
226338
|
if (!asset.file?.src)
|
|
225668
226339
|
continue;
|
|
225669
226340
|
const bytes = await downloadBlob(apiUrl, asset.file.src);
|
|
225670
|
-
|
|
226341
|
+
writeFileSync29(abs, bytes);
|
|
225671
226342
|
sha = sha256(bytes);
|
|
225672
226343
|
} else {
|
|
225673
226344
|
const text = asset.text ?? "";
|
|
225674
|
-
|
|
226345
|
+
writeFileSync29(abs, text);
|
|
225675
226346
|
sha = sha256(text);
|
|
225676
226347
|
}
|
|
225677
226348
|
files[key] = { id: asset.id, type: kindOf3(asset.type), sha };
|
|
@@ -225684,8 +226355,8 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
225684
226355
|
|
|
225685
226356
|
// src/commands/scene.ts
|
|
225686
226357
|
init_manifest();
|
|
225687
|
-
import { existsSync as
|
|
225688
|
-
import { basename as
|
|
226358
|
+
import { existsSync as existsSync39, readFileSync as readFileSync35, readdirSync as readdirSync17, statSync as statSync14, writeFileSync as writeFileSync31 } from "node:fs";
|
|
226359
|
+
import { basename as basename19, dirname as dirname19, join as join41, resolve as resolve20 } from "node:path";
|
|
225689
226360
|
|
|
225690
226361
|
// src/compile/sceneCompile.ts
|
|
225691
226362
|
init_localFiles();
|
|
@@ -225694,18 +226365,18 @@ init_distRoot();
|
|
|
225694
226365
|
init_util();
|
|
225695
226366
|
init_projectCompile();
|
|
225696
226367
|
import { createHash as createHash8 } from "node:crypto";
|
|
225697
|
-
import { existsSync as
|
|
225698
|
-
import { join as
|
|
226368
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync27, readFileSync as readFileSync34, writeFileSync as writeFileSync30 } from "node:fs";
|
|
226369
|
+
import { join as join40 } from "node:path";
|
|
225699
226370
|
var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
|
|
225700
226371
|
var rel = (key) => key.replace(/^\//, "");
|
|
225701
226372
|
var resolveHarness = (loadSceneHarness2) => {
|
|
225702
|
-
const inRepo =
|
|
226373
|
+
const inRepo = join40(distRoot, "..", "projects", "src", "scene-editor");
|
|
225703
226374
|
try {
|
|
225704
|
-
return loadSceneHarness2(
|
|
226375
|
+
return loadSceneHarness2(existsSync38(inRepo) ? inRepo : undefined);
|
|
225705
226376
|
} catch {
|
|
225706
|
-
const vendored =
|
|
225707
|
-
if (
|
|
225708
|
-
return JSON.parse(
|
|
226377
|
+
const vendored = join40(distRoot, "runtime", "scene-harness.json");
|
|
226378
|
+
if (existsSync38(vendored))
|
|
226379
|
+
return JSON.parse(readFileSync34(vendored, "utf8"));
|
|
225709
226380
|
throw new CliError("scene-editor harness sources not found — reinstall the CLI or set LECODES_SCENE_HARNESS_DIR.");
|
|
225710
226381
|
}
|
|
225711
226382
|
};
|
|
@@ -225717,9 +226388,9 @@ var compileSceneEditor = async (opts) => {
|
|
|
225717
226388
|
for (const file of scan2.files) {
|
|
225718
226389
|
const path2 = "./" + rel(file.path);
|
|
225719
226390
|
if (TEXT_EXT.test(path2)) {
|
|
225720
|
-
entries.push({ path: path2, type: "text", text:
|
|
226391
|
+
entries.push({ path: path2, type: "text", text: readFileSync34(file.absPath, "utf8") });
|
|
225721
226392
|
} else if (/\.mat$/i.test(path2)) {
|
|
225722
|
-
entries.push({ path: path2, type: "shader", text:
|
|
226393
|
+
entries.push({ path: path2, type: "shader", text: readFileSync34(file.absPath, "utf8"), absPath: file.absPath });
|
|
225723
226394
|
} else {
|
|
225724
226395
|
entries.push({ path: path2, type: "resource", fileSrc: "/project/" + rel(file.path) });
|
|
225725
226396
|
}
|
|
@@ -225735,10 +226406,10 @@ var compileSceneEditor = async (opts) => {
|
|
|
225735
226406
|
continue;
|
|
225736
226407
|
const hash = createHash8("sha256").update(e2.text ?? "").digest("hex").slice(0, 16);
|
|
225737
226408
|
const dir = shadersCacheDir(root);
|
|
225738
|
-
|
|
226409
|
+
mkdirSync27(dir, { recursive: true });
|
|
225739
226410
|
const liteName = `${hash}_webgl-lite.filamat`;
|
|
225740
|
-
if (!
|
|
225741
|
-
|
|
226411
|
+
if (!existsSync38(join40(dir, liteName)))
|
|
226412
|
+
writeFileSync30(join40(dir, liteName), JSON.stringify(buildLiteMaterialArtifact2(e2.text ?? "")));
|
|
225742
226413
|
const src = `/project/.lecodes/shaders/${hash}_opengl.filamat`;
|
|
225743
226414
|
e2.shaders = ["opengl", "webgl-lite"].map((platform) => ({ platform, src }));
|
|
225744
226415
|
}
|
|
@@ -225768,13 +226439,14 @@ var compileSceneEditor = async (opts) => {
|
|
|
225768
226439
|
// src/commands/scene.ts
|
|
225769
226440
|
init_browserAuth();
|
|
225770
226441
|
init_util();
|
|
226442
|
+
init_peerInstall();
|
|
225771
226443
|
var DEFAULT_PORT3 = 4488;
|
|
225772
226444
|
var findAppRoot = (start) => {
|
|
225773
226445
|
let dir = start;
|
|
225774
226446
|
while (true) {
|
|
225775
|
-
if (
|
|
226447
|
+
if (existsSync39(join41(dir, "app.json")))
|
|
225776
226448
|
return dir;
|
|
225777
|
-
const parent =
|
|
226449
|
+
const parent = dirname19(dir);
|
|
225778
226450
|
if (parent === dir)
|
|
225779
226451
|
return null;
|
|
225780
226452
|
dir = parent;
|
|
@@ -225798,26 +226470,26 @@ var getContext2 = (args) => {
|
|
|
225798
226470
|
manifest = null;
|
|
225799
226471
|
const pathArg = args._[0];
|
|
225800
226472
|
if (pathArg) {
|
|
225801
|
-
const abs =
|
|
225802
|
-
if (
|
|
226473
|
+
const abs = resolve20(process.cwd(), pathArg);
|
|
226474
|
+
if (existsSync39(abs) && statSync14(abs).isDirectory())
|
|
225803
226475
|
root = abs;
|
|
225804
|
-
else if (abs.endsWith(".scene.ts") &&
|
|
225805
|
-
root = findAppRoot(
|
|
226476
|
+
else if (abs.endsWith(".scene.ts") && existsSync39(abs))
|
|
226477
|
+
root = findAppRoot(dirname19(abs)) ?? dirname19(abs);
|
|
225806
226478
|
else
|
|
225807
226479
|
throw new CliError(`No such folder or .scene.ts: ${pathArg}`);
|
|
225808
226480
|
} else {
|
|
225809
226481
|
root = findAppRoot(root) ?? root;
|
|
225810
226482
|
}
|
|
225811
226483
|
}
|
|
225812
|
-
return { root, manifest, name: manifest?.name ??
|
|
226484
|
+
return { root, manifest, name: manifest?.name ?? basename19(root) };
|
|
225813
226485
|
};
|
|
225814
226486
|
var writeMcpConfig2 = (root, port) => {
|
|
225815
|
-
const path2 =
|
|
226487
|
+
const path2 = join41(root, ".mcp.json");
|
|
225816
226488
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
225817
226489
|
let config = {};
|
|
225818
|
-
if (
|
|
226490
|
+
if (existsSync39(path2)) {
|
|
225819
226491
|
try {
|
|
225820
|
-
config = JSON.parse(
|
|
226492
|
+
config = JSON.parse(readFileSync35(path2, "utf8"));
|
|
225821
226493
|
} catch {
|
|
225822
226494
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-scene server by hand.");
|
|
225823
226495
|
return;
|
|
@@ -225830,17 +226502,17 @@ var writeMcpConfig2 = (root, port) => {
|
|
|
225830
226502
|
return;
|
|
225831
226503
|
}
|
|
225832
226504
|
config.mcpServers["lecodes-scene"] = entry;
|
|
225833
|
-
|
|
226505
|
+
writeFileSync31(path2, JSON.stringify(config, null, 2) + `
|
|
225834
226506
|
`);
|
|
225835
226507
|
note(`${existing ? "updated" : "created"} .mcp.json (lecodes-scene MCP → ${entry.url})`);
|
|
225836
226508
|
};
|
|
225837
226509
|
var countScenes = (dir) => {
|
|
225838
226510
|
let n2 = 0;
|
|
225839
|
-
for (const name of
|
|
226511
|
+
for (const name of readdirSync17(dir)) {
|
|
225840
226512
|
if (name === "node_modules" || name.startsWith("."))
|
|
225841
226513
|
continue;
|
|
225842
|
-
const full =
|
|
225843
|
-
if (
|
|
226514
|
+
const full = join41(dir, name);
|
|
226515
|
+
if (statSync14(full).isDirectory())
|
|
225844
226516
|
n2 += countScenes(full);
|
|
225845
226517
|
else if (name.endsWith(".scene.ts"))
|
|
225846
226518
|
n2++;
|
|
@@ -225849,12 +226521,7 @@ var countScenes = (dir) => {
|
|
|
225849
226521
|
};
|
|
225850
226522
|
var scene = async (args) => {
|
|
225851
226523
|
const ctx = getContext2(args);
|
|
225852
|
-
|
|
225853
|
-
try {
|
|
225854
|
-
sceneServer = await import("lecodes-3d-editor/server");
|
|
225855
|
-
} catch {
|
|
225856
|
-
throw new CliError("The scene editor isn't installed. Run: npm install -g lecodes-3d-editor");
|
|
225857
|
-
}
|
|
226524
|
+
const sceneServer = await loadPeer("lecodes-3d-editor", "server", { for: "lecodes scene" });
|
|
225858
226525
|
const port = Number(flagStr(args, "port")) || DEFAULT_PORT3;
|
|
225859
226526
|
if (countScenes(ctx.root) === 0) {
|
|
225860
226527
|
warnErr(`No .scene.ts file under ${ctx.root} yet — add one and the editor will pick it up.`);
|
|
@@ -225882,11 +226549,12 @@ var scene = async (args) => {
|
|
|
225882
226549
|
};
|
|
225883
226550
|
|
|
225884
226551
|
// src/commands/thumbs.ts
|
|
225885
|
-
|
|
226552
|
+
init_peerInstall();
|
|
226553
|
+
import { createRequire as createRequire7 } from "node:module";
|
|
225886
226554
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
225887
|
-
import { existsSync as
|
|
226555
|
+
import { existsSync as existsSync40, mkdtempSync as mkdtempSync6, readdirSync as readdirSync18, rmSync as rmSync12, statSync as statSync15 } from "node:fs";
|
|
225888
226556
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
225889
|
-
import { extname, join as
|
|
226557
|
+
import { extname, join as join42, relative as relative9, resolve as resolve21 } from "node:path";
|
|
225890
226558
|
init_util();
|
|
225891
226559
|
var DEFAULT_SIZE = 256;
|
|
225892
226560
|
var IMAGE_EXT = /\.(png|jpe?g|webp|bmp|gif|tiff?|ktx2)$/i;
|
|
@@ -225897,13 +226565,13 @@ var findKtx = () => {
|
|
|
225897
226565
|
process.env.LECODES_KTX,
|
|
225898
226566
|
process.env.KTX_CLI,
|
|
225899
226567
|
process.env.KTX_TOKTX?.replace(/toktx(\.exe)?$/i, exe),
|
|
225900
|
-
process.platform === "win32" ?
|
|
225901
|
-
process.platform === "win32" ?
|
|
226568
|
+
process.platform === "win32" ? join42(process.env.LOCALAPPDATA ?? "", "Programs", "KTX-Software", "bin", exe) : null,
|
|
226569
|
+
process.platform === "win32" ? join42(process.env.ProgramFiles ?? "", "KTX-Software", "bin", exe) : null,
|
|
225902
226570
|
"/usr/local/bin/ktx",
|
|
225903
226571
|
"/opt/homebrew/bin/ktx"
|
|
225904
226572
|
].filter((p2) => !!p2);
|
|
225905
226573
|
for (const path2 of candidates) {
|
|
225906
|
-
if (
|
|
226574
|
+
if (existsSync40(path2))
|
|
225907
226575
|
return path2;
|
|
225908
226576
|
}
|
|
225909
226577
|
try {
|
|
@@ -225915,8 +226583,8 @@ var findKtx = () => {
|
|
|
225915
226583
|
};
|
|
225916
226584
|
var sharpResizer = () => {
|
|
225917
226585
|
try {
|
|
225918
|
-
const req =
|
|
225919
|
-
const sharp =
|
|
226586
|
+
const req = createRequire7(import.meta.url);
|
|
226587
|
+
const sharp = createRequire7(req.resolve("lecodes-assets/cli"))("sharp");
|
|
225920
226588
|
return async (file, size) => sharp(file).resize({ width: size, height: size, fit: "inside", withoutEnlargement: true }).png().toBuffer();
|
|
225921
226589
|
} catch {
|
|
225922
226590
|
return null;
|
|
@@ -225930,22 +226598,22 @@ var jimpResizer = () => async (file, size) => {
|
|
|
225930
226598
|
return await img.getBuffer("image/png");
|
|
225931
226599
|
};
|
|
225932
226600
|
var ktxToPng = (ktx, file, out) => {
|
|
225933
|
-
const
|
|
226601
|
+
const run3 = (args) => execFileSync7(ktx, args, { stdio: ["ignore", "ignore", "pipe"], encoding: "utf8" });
|
|
225934
226602
|
try {
|
|
225935
|
-
|
|
226603
|
+
run3(["extract", "--transcode", "rgba8", "--level", "0", file, out]);
|
|
225936
226604
|
} catch (e2) {
|
|
225937
|
-
|
|
225938
|
-
if (!
|
|
226605
|
+
run3(["extract", "--level", "0", file, out]);
|
|
226606
|
+
if (!existsSync40(out))
|
|
225939
226607
|
throw e2;
|
|
225940
226608
|
}
|
|
225941
226609
|
};
|
|
225942
226610
|
var collect = (dir) => {
|
|
225943
226611
|
const out = [];
|
|
225944
|
-
for (const name of
|
|
226612
|
+
for (const name of readdirSync18(dir)) {
|
|
225945
226613
|
if (name === "node_modules" || name.startsWith("."))
|
|
225946
226614
|
continue;
|
|
225947
|
-
const full =
|
|
225948
|
-
if (
|
|
226615
|
+
const full = join42(dir, name);
|
|
226616
|
+
if (statSync15(full).isDirectory())
|
|
225949
226617
|
out.push(...collect(full));
|
|
225950
226618
|
else if (IMAGE_EXT.test(name))
|
|
225951
226619
|
out.push(full);
|
|
@@ -225954,22 +226622,17 @@ var collect = (dir) => {
|
|
|
225954
226622
|
};
|
|
225955
226623
|
var thumbs = async (args) => {
|
|
225956
226624
|
const root = projectRootOf(process.cwd());
|
|
225957
|
-
|
|
225958
|
-
try {
|
|
225959
|
-
sceneServer = await import("lecodes-3d-editor/server");
|
|
225960
|
-
} catch {
|
|
225961
|
-
throw new CliError("The scene editor isn't installed (it owns the preview cache). Run: npm install -g lecodes-3d-editor");
|
|
225962
|
-
}
|
|
226625
|
+
const sceneServer = await loadPeer("lecodes-3d-editor", "server", { for: "lecodes thumbs" });
|
|
225963
226626
|
const size = Number(flagStr(args, "size")) || DEFAULT_SIZE;
|
|
225964
226627
|
const force = flagBool(args, "force");
|
|
225965
226628
|
let files = [];
|
|
225966
226629
|
const rendered = [];
|
|
225967
226630
|
if (args._.length > 0) {
|
|
225968
226631
|
for (const arg of args._) {
|
|
225969
|
-
const abs =
|
|
225970
|
-
if (!
|
|
226632
|
+
const abs = resolve21(process.cwd(), arg);
|
|
226633
|
+
if (!existsSync40(abs))
|
|
225971
226634
|
throw new CliError(`No such file or folder: ${arg}`);
|
|
225972
|
-
if (
|
|
226635
|
+
if (statSync15(abs).isDirectory())
|
|
225973
226636
|
files.push(...collect(abs));
|
|
225974
226637
|
else if (IMAGE_EXT.test(abs))
|
|
225975
226638
|
files.push(abs);
|
|
@@ -225989,12 +226652,12 @@ var thumbs = async (args) => {
|
|
|
225989
226652
|
const ktx = files.some((f3) => f3.toLowerCase().endsWith(".ktx2")) ? findKtx() : null;
|
|
225990
226653
|
const sharp = sharpResizer();
|
|
225991
226654
|
const resize = sharp ?? jimpResizer();
|
|
225992
|
-
const tmp = mkdtempSync6(
|
|
226655
|
+
const tmp = mkdtempSync6(join42(tmpdir6(), "lecodes-thumbs-"));
|
|
225993
226656
|
let made = 0, fresh = 0;
|
|
225994
226657
|
const skipped = [];
|
|
225995
226658
|
try {
|
|
225996
226659
|
for (const [i2, file] of files.entries()) {
|
|
225997
|
-
const asset = "./" + toKey(
|
|
226660
|
+
const asset = "./" + toKey(relative9(root, file));
|
|
225998
226661
|
progress(i2, files.length, asset);
|
|
225999
226662
|
const hash = sceneServer.hashAsset(root, asset);
|
|
226000
226663
|
if (!hash)
|
|
@@ -226015,7 +226678,7 @@ var thumbs = async (args) => {
|
|
|
226015
226678
|
skipped.push(`${asset} (KTX2 needs KTX-Software's \`ktx\` — set LECODES_KTX or put it on PATH)`);
|
|
226016
226679
|
continue;
|
|
226017
226680
|
}
|
|
226018
|
-
source =
|
|
226681
|
+
source = join42(tmp, `${i2}.png`);
|
|
226019
226682
|
ktxToPng(ktx, file, source);
|
|
226020
226683
|
}
|
|
226021
226684
|
sceneServer.writePreviewFile(root, asset, await resize(source, size));
|
|
@@ -226035,16 +226698,13 @@ var thumbs = async (args) => {
|
|
|
226035
226698
|
if (rendered.length > 0) {
|
|
226036
226699
|
warnErr(`Models, materials and prefabs are RENDERED, not decoded — the scene editor does those: ${rendered.join(", ")}`);
|
|
226037
226700
|
}
|
|
226038
|
-
note(`Cache: ${
|
|
226701
|
+
note(`Cache: ${join42(root, ".lecodes", "preview")} (re-runs skip everything unchanged)`);
|
|
226039
226702
|
};
|
|
226040
226703
|
|
|
226041
|
-
// src/commands/assets.ts
|
|
226042
|
-
init_util();
|
|
226043
|
-
|
|
226044
226704
|
// src/cmgenTool.ts
|
|
226045
|
-
import { existsSync as
|
|
226046
|
-
import { homedir as
|
|
226047
|
-
import { join as
|
|
226705
|
+
import { existsSync as existsSync41, readdirSync as readdirSync19 } from "node:fs";
|
|
226706
|
+
import { homedir as homedir9 } from "node:os";
|
|
226707
|
+
import { join as join43, resolve as resolve22 } from "node:path";
|
|
226048
226708
|
init_util();
|
|
226049
226709
|
var exeName2 = () => process.platform === "win32" ? "cmgen.exe" : "cmgen";
|
|
226050
226710
|
var platformKey3 = () => {
|
|
@@ -226054,10 +226714,10 @@ var platformKey3 = () => {
|
|
|
226054
226714
|
return "linux-x64";
|
|
226055
226715
|
throw new CliError(process.platform === "darwin" ? "cmgen isn't published for macOS yet — build filament's host tools locally and set LECODES_CMGEN." : `cmgen isn't published for ${process.platform}.`);
|
|
226056
226716
|
};
|
|
226057
|
-
var cacheRoot3 = () =>
|
|
226717
|
+
var cacheRoot3 = () => join43(homedir9(), ".lecodes", "cmgen");
|
|
226058
226718
|
var findCmgenIn = (dir) => {
|
|
226059
|
-
for (const entry of
|
|
226060
|
-
const p2 =
|
|
226719
|
+
for (const entry of readdirSync19(dir, { withFileTypes: true })) {
|
|
226720
|
+
const p2 = join43(dir, entry.name);
|
|
226061
226721
|
if (entry.isFile() && entry.name === exeName2())
|
|
226062
226722
|
return p2;
|
|
226063
226723
|
if (entry.isDirectory()) {
|
|
@@ -226070,18 +226730,21 @@ var findCmgenIn = (dir) => {
|
|
|
226070
226730
|
};
|
|
226071
226731
|
var installedCmgenVersions = () => {
|
|
226072
226732
|
try {
|
|
226073
|
-
return
|
|
226733
|
+
return readdirSync19(cacheRoot3(), { withFileTypes: true }).filter((e2) => e2.isDirectory() && e2.name.startsWith("cmgen-")).map((e2) => e2.name.slice("cmgen-".length)).filter((v) => findCmgenIn(join43(cacheRoot3(), `cmgen-${v}`)) !== null).sort(semverNewer);
|
|
226074
226734
|
} catch {
|
|
226075
226735
|
return [];
|
|
226076
226736
|
}
|
|
226077
226737
|
};
|
|
226078
226738
|
var fetchCmgenManifest = () => fetchReleaseProduct("cmgen", "cmgen (environment tool)");
|
|
226079
|
-
var
|
|
226739
|
+
var pickCmgen = (versions) => {
|
|
226080
226740
|
const key = platformKey3();
|
|
226081
|
-
const
|
|
226082
|
-
if (!
|
|
226083
|
-
throw new CliError(`cmgen
|
|
226741
|
+
const hit = newestForPlatform(versions, key);
|
|
226742
|
+
if (!hit) {
|
|
226743
|
+
throw new CliError(`cmgen isn't published for ${key} — build filament's host tools locally and point ` + `LECODES_CMGEN at the binary.`);
|
|
226084
226744
|
}
|
|
226745
|
+
return hit;
|
|
226746
|
+
};
|
|
226747
|
+
var downloadCmgen = async (version, file, dest) => {
|
|
226085
226748
|
note(`Downloading the environment tool (cmgen ${version}, ${(file.size / 1024 / 1024).toFixed(1)} MB)…`);
|
|
226086
226749
|
await downloadReleaseArchive(file, {
|
|
226087
226750
|
cacheRoot: cacheRoot3(),
|
|
@@ -226092,20 +226755,21 @@ var downloadCmgen = async (version, versions, dest) => {
|
|
|
226092
226755
|
var resolveCmgenExeCached = () => {
|
|
226093
226756
|
const override = process.env.LECODES_CMGEN;
|
|
226094
226757
|
if (override) {
|
|
226095
|
-
if (!
|
|
226758
|
+
if (!existsSync41(override))
|
|
226096
226759
|
throw new CliError(`LECODES_CMGEN points at "${override}" which doesn't exist.`);
|
|
226097
|
-
return
|
|
226760
|
+
return resolve22(override);
|
|
226098
226761
|
}
|
|
226099
226762
|
const cached = installedCmgenVersions()[0];
|
|
226100
|
-
return cached ? findCmgenIn(
|
|
226763
|
+
return cached ? findCmgenIn(join43(cacheRoot3(), `cmgen-${cached}`)) : null;
|
|
226101
226764
|
};
|
|
226102
226765
|
var resolveCmgenExe = async () => {
|
|
226103
226766
|
const cached = resolveCmgenExeCached();
|
|
226104
226767
|
if (cached)
|
|
226105
226768
|
return cached;
|
|
226106
|
-
const {
|
|
226107
|
-
const
|
|
226108
|
-
|
|
226769
|
+
const { versions } = await fetchCmgenManifest();
|
|
226770
|
+
const { version, file } = pickCmgen(versions);
|
|
226771
|
+
const dir = join43(cacheRoot3(), `cmgen-${version}`);
|
|
226772
|
+
await downloadCmgen(version, file, dir);
|
|
226109
226773
|
const exe = findCmgenIn(dir);
|
|
226110
226774
|
if (!exe)
|
|
226111
226775
|
throw new CliError(`cmgen cache at ${dir} is broken — delete the folder and retry.`);
|
|
@@ -226113,13 +226777,9 @@ var resolveCmgenExe = async () => {
|
|
|
226113
226777
|
};
|
|
226114
226778
|
|
|
226115
226779
|
// src/commands/assets.ts
|
|
226780
|
+
init_peerInstall();
|
|
226116
226781
|
var assets = async (args) => {
|
|
226117
|
-
|
|
226118
|
-
try {
|
|
226119
|
-
mod = await import("lecodes-assets/cli");
|
|
226120
|
-
} catch {
|
|
226121
|
-
throw new CliError("The asset pipeline isn't installed. Run: npm install -g lecodes-assets");
|
|
226122
|
-
}
|
|
226782
|
+
const mod = await loadPeer("lecodes-assets", "cli", { for: "lecodes assets" });
|
|
226123
226783
|
const raw = args._raw ?? args._;
|
|
226124
226784
|
if (raw[0] === "sky" && !process.env.LECODES_CMGEN) {
|
|
226125
226785
|
process.env.LECODES_CMGEN = await resolveCmgenExe();
|
|
@@ -226162,8 +226822,8 @@ var status3 = async (_args) => {
|
|
|
226162
226822
|
};
|
|
226163
226823
|
|
|
226164
226824
|
// src/commands/test.ts
|
|
226165
|
-
import { existsSync as
|
|
226166
|
-
import { dirname as
|
|
226825
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync28, readFileSync as readFileSync36, readdirSync as readdirSync20 } from "node:fs";
|
|
226826
|
+
import { dirname as dirname20, resolve as resolve23 } from "node:path";
|
|
226167
226827
|
init_manifest();
|
|
226168
226828
|
init_util();
|
|
226169
226829
|
var deriveDesignFlows = (meta, entryScreen) => {
|
|
@@ -226226,7 +226886,7 @@ var slug3 = (s2) => s2.replace(/[^a-zA-Z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").
|
|
|
226226
226886
|
var readFlowFile = (abs, displayName) => {
|
|
226227
226887
|
let raw;
|
|
226228
226888
|
try {
|
|
226229
|
-
raw = JSON.parse(
|
|
226889
|
+
raw = JSON.parse(readFileSync36(abs, "utf8"));
|
|
226230
226890
|
} catch (e2) {
|
|
226231
226891
|
throw new CliError(`Can't read "${displayName}": ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
226232
226892
|
}
|
|
@@ -226240,7 +226900,7 @@ var readFlowFile = (abs, displayName) => {
|
|
|
226240
226900
|
name: file.name ?? displayName,
|
|
226241
226901
|
kind: "file",
|
|
226242
226902
|
steps: file.steps,
|
|
226243
|
-
fixtures: resolveFixtures(file.fixtures,
|
|
226903
|
+
fixtures: resolveFixtures(file.fixtures, dirname20(abs)),
|
|
226244
226904
|
width: file.width,
|
|
226245
226905
|
height: file.height,
|
|
226246
226906
|
settle: file.settle,
|
|
@@ -226281,40 +226941,40 @@ var test = async (args) => {
|
|
|
226281
226941
|
if (!Number.isFinite(nativeFixedDt) || nativeFixedDt < 0 || nativeFixedDt > 1000)
|
|
226282
226942
|
throw new CliError(`--fixed-dt wants a number of ms (0 = wall clock, max 1000), got "${fixedDtFlag}".`);
|
|
226283
226943
|
const projectViewport = desktopDefaultViewport(root);
|
|
226284
|
-
const testsDir =
|
|
226285
|
-
const designMetaPath =
|
|
226944
|
+
const testsDir = resolve23(root, flagStr(args, "dir") ?? "tests");
|
|
226945
|
+
const designMetaPath = resolve23(root, flagStr(args, "design-dir") ?? "design", "meta.json");
|
|
226286
226946
|
const asJson = flagBool(args, "json");
|
|
226287
226947
|
const out = asJson ? logErr : log;
|
|
226288
226948
|
const cases = [];
|
|
226289
226949
|
if (args._.length > 0) {
|
|
226290
226950
|
for (const given of args._) {
|
|
226291
|
-
const candidates = [
|
|
226292
|
-
const abs = candidates.find((p2) =>
|
|
226951
|
+
const candidates = [resolve23(given), resolve23(testsDir, given), resolve23(testsDir, `${given}.flow.json`)];
|
|
226952
|
+
const abs = candidates.find((p2) => existsSync42(p2));
|
|
226293
226953
|
if (!abs)
|
|
226294
226954
|
throw new CliError(`No such test: "${given}" (looked in ${testsDir}).`);
|
|
226295
226955
|
cases.push(readFlowFile(abs, given));
|
|
226296
226956
|
}
|
|
226297
|
-
} else if (
|
|
226298
|
-
for (const f3 of
|
|
226299
|
-
cases.push(readFlowFile(
|
|
226957
|
+
} else if (existsSync42(testsDir)) {
|
|
226958
|
+
for (const f3 of readdirSync20(testsDir).filter((f4) => f4.endsWith(".flow.json")).sort()) {
|
|
226959
|
+
cases.push(readFlowFile(resolve23(testsDir, f3), f3));
|
|
226300
226960
|
}
|
|
226301
226961
|
}
|
|
226302
226962
|
const fixturesFlag = flagStr(args, "fixtures");
|
|
226303
|
-
const defaultPack =
|
|
226304
|
-
const flowFixtures = fixturesFlag !== undefined ? resolveFixtures(fixturesFlag, ".") :
|
|
226963
|
+
const defaultPack = resolve23(testsDir, "fixtures.json");
|
|
226964
|
+
const flowFixtures = fixturesFlag !== undefined ? resolveFixtures(fixturesFlag, ".") : existsSync42(defaultPack) ? resolveFixtures("fixtures.json", testsDir) : undefined;
|
|
226305
226965
|
const flowsOnly = flagBool(args, "flows");
|
|
226306
226966
|
const wantFlows = !flagBool(args, "no-flows") && args._.length === 0;
|
|
226307
226967
|
if (flowsOnly)
|
|
226308
226968
|
cases.length = 0;
|
|
226309
226969
|
let skipped = [];
|
|
226310
226970
|
let entryScreen = null;
|
|
226311
|
-
if (desktop2 && wantFlows &&
|
|
226971
|
+
if (desktop2 && wantFlows && existsSync42(designMetaPath)) {
|
|
226312
226972
|
note("Design-derived flows skipped with --desktop (they tap by name; run `lecodes test` headless for them).");
|
|
226313
226973
|
}
|
|
226314
|
-
if (!desktop2 && (wantFlows || flowsOnly) &&
|
|
226974
|
+
if (!desktop2 && (wantFlows || flowsOnly) && existsSync42(designMetaPath)) {
|
|
226315
226975
|
let meta;
|
|
226316
226976
|
try {
|
|
226317
|
-
meta = JSON.parse(
|
|
226977
|
+
meta = JSON.parse(readFileSync36(designMetaPath, "utf8"));
|
|
226318
226978
|
} catch (e2) {
|
|
226319
226979
|
throw new CliError(`Can't read ${designMetaPath}: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
226320
226980
|
}
|
|
@@ -226346,18 +227006,19 @@ var test = async (args) => {
|
|
|
226346
227006
|
}, timeoutMs);
|
|
226347
227007
|
killer.unref?.();
|
|
226348
227008
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
226349
|
-
const artifactsRoot =
|
|
227009
|
+
const artifactsRoot = resolve23(root, flagStr(args, "artifacts") ?? resolve23(testsDir, ".artifacts"));
|
|
226350
227010
|
const results = [];
|
|
226351
227011
|
const runNativeCase = async (kase) => {
|
|
226352
|
-
const caseDir =
|
|
227012
|
+
const caseDir = resolve23(artifactsRoot, slug3(kase.name));
|
|
226353
227013
|
const plan = translateScenario(kase.steps, { outDir: caseDir, headroomFrames: nativeFrames });
|
|
226354
227014
|
for (const st of plan.steps)
|
|
226355
227015
|
if (st.screenshot)
|
|
226356
|
-
|
|
226357
|
-
|
|
226358
|
-
const finalPng =
|
|
227016
|
+
mkdirSync28(dirname20(st.screenshot), { recursive: true });
|
|
227017
|
+
mkdirSync28(caseDir, { recursive: true });
|
|
227018
|
+
const finalPng = resolve23(caseDir, "final.png");
|
|
226359
227019
|
const { output } = await runDesktopRender(native.js, native.resources, {
|
|
226360
227020
|
renderer: desktopRenderer,
|
|
227021
|
+
pluginDirs: desktopPluginLibraries(root).dirs,
|
|
226361
227022
|
width: Number(flagStr(args, "width")) || kase.width || projectViewport?.width || 390,
|
|
226362
227023
|
height: Number(flagStr(args, "height")) || kase.height || projectViewport?.height || 844,
|
|
226363
227024
|
frames: plan.frameCap,
|
|
@@ -226407,14 +227068,14 @@ var test = async (args) => {
|
|
|
226407
227068
|
onConsole
|
|
226408
227069
|
});
|
|
226409
227070
|
try {
|
|
226410
|
-
const io = fileScenarioIO(
|
|
227071
|
+
const io = fileScenarioIO(resolve23(artifactsRoot, slug3(kase.name)));
|
|
226411
227072
|
const outcome = await renderer.runScenario(session, steps, io);
|
|
226412
227073
|
result = {
|
|
226413
227074
|
name: kase.name,
|
|
226414
227075
|
kind: kase.kind,
|
|
226415
227076
|
ok: outcome.ok,
|
|
226416
227077
|
steps: outcome.steps.map((s2) => ({ desc: s2.desc, ok: s2.ok, error: s2.error })),
|
|
226417
|
-
artifacts: outcome.failureArtifacts.map((p2) =>
|
|
227078
|
+
artifacts: outcome.failureArtifacts.map((p2) => resolve23(artifactsRoot, slug3(kase.name), p2))
|
|
226418
227079
|
};
|
|
226419
227080
|
} finally {
|
|
226420
227081
|
session.dispose();
|
|
@@ -226477,41 +227138,14 @@ var test = async (args) => {
|
|
|
226477
227138
|
};
|
|
226478
227139
|
|
|
226479
227140
|
// src/commands/update.ts
|
|
226480
|
-
|
|
226481
|
-
import { existsSync as existsSync39, readdirSync as readdirSync19 } from "node:fs";
|
|
226482
|
-
import { homedir as homedir9 } from "node:os";
|
|
226483
|
-
import { dirname as dirname20, join as join40, resolve as resolve23 } from "node:path";
|
|
227141
|
+
init_package();
|
|
226484
227142
|
init_util();
|
|
226485
|
-
|
|
226486
|
-
|
|
226487
|
-
import {
|
|
226488
|
-
import {
|
|
226489
|
-
import {
|
|
226490
|
-
|
|
226491
|
-
["lecodes-renderer", "lecodes-renderer/headless"],
|
|
226492
|
-
["lecodes-design", "lecodes-design/server"],
|
|
226493
|
-
["lecodes-3d-editor", "lecodes-3d-editor/server"],
|
|
226494
|
-
["lecodes-assets", "lecodes-assets/cli"]
|
|
226495
|
-
];
|
|
226496
|
-
var peerVersion = (name, probe) => {
|
|
226497
|
-
try {
|
|
226498
|
-
const require2 = createRequire6(import.meta.url);
|
|
226499
|
-
let dir = dirname19(require2.resolve(probe));
|
|
226500
|
-
for (let depth = 0;depth < 16; depth++) {
|
|
226501
|
-
const manifest = join39(dir, "package.json");
|
|
226502
|
-
if (existsSync38(manifest)) {
|
|
226503
|
-
const parsed = JSON.parse(readFileSync34(manifest, "utf8"));
|
|
226504
|
-
if (parsed.name === name)
|
|
226505
|
-
return parsed.version ?? "unknown";
|
|
226506
|
-
}
|
|
226507
|
-
const parent = dirname19(dir);
|
|
226508
|
-
if (parent === dir)
|
|
226509
|
-
break;
|
|
226510
|
-
dir = parent;
|
|
226511
|
-
}
|
|
226512
|
-
} catch {}
|
|
226513
|
-
return null;
|
|
226514
|
-
};
|
|
227143
|
+
init_peers();
|
|
227144
|
+
init_peerInstall();
|
|
227145
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
227146
|
+
import { existsSync as existsSync43, readdirSync as readdirSync21 } from "node:fs";
|
|
227147
|
+
import { homedir as homedir10 } from "node:os";
|
|
227148
|
+
import { dirname as dirname21, join as join44, resolve as resolve25 } from "node:path";
|
|
226515
227149
|
|
|
226516
227150
|
// src/commands/types.ts
|
|
226517
227151
|
init_api();
|
|
@@ -226519,11 +227153,11 @@ init_config();
|
|
|
226519
227153
|
init_manifest();
|
|
226520
227154
|
init_types4();
|
|
226521
227155
|
init_util();
|
|
226522
|
-
import { resolve as
|
|
227156
|
+
import { resolve as resolve24 } from "node:path";
|
|
226523
227157
|
var label = (bundle) => `${Object.keys(bundle).length} file(s)`;
|
|
226524
227158
|
var types4 = async (args) => {
|
|
226525
227159
|
const dirArg = args._[0];
|
|
226526
|
-
const root = dirArg ?
|
|
227160
|
+
const root = dirArg ? resolve24(dirArg) : (() => {
|
|
226527
227161
|
try {
|
|
226528
227162
|
return findProjectRoot(process.cwd());
|
|
226529
227163
|
} catch {
|
|
@@ -226583,12 +227217,12 @@ var types4 = async (args) => {
|
|
|
226583
227217
|
|
|
226584
227218
|
// src/commands/update.ts
|
|
226585
227219
|
var findProjectDir = (start) => {
|
|
226586
|
-
const home =
|
|
226587
|
-
let dir =
|
|
227220
|
+
const home = resolve25(homedir10());
|
|
227221
|
+
let dir = resolve25(start);
|
|
226588
227222
|
while (true) {
|
|
226589
|
-
if (dir !== home && (
|
|
227223
|
+
if (dir !== home && (existsSync43(join44(dir, "app.json")) || existsSync43(join44(dir, ".lecodes", "manifest.json")) || existsSync43(join44(dir, ".lecodes", "types"))))
|
|
226590
227224
|
return dir;
|
|
226591
|
-
const parent =
|
|
227225
|
+
const parent = dirname21(dir);
|
|
226592
227226
|
if (parent === dir)
|
|
226593
227227
|
return null;
|
|
226594
227228
|
dir = parent;
|
|
@@ -226616,12 +227250,12 @@ var semverNewer2 = (a2, b) => {
|
|
|
226616
227250
|
return 0;
|
|
226617
227251
|
};
|
|
226618
227252
|
var FROM_SOURCE = /[\\/]src[\\/]commands[\\/]/.test(import.meta.url);
|
|
226619
|
-
var STANDALONE =
|
|
227253
|
+
var STANDALONE = existsSync43(join44(dirname21(process.execPath), "runtime")) && existsSync43(join44(dirname21(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel"));
|
|
226620
227254
|
var update = async (args) => {
|
|
226621
227255
|
const steps = [];
|
|
226622
227256
|
const heading = (name, what) => log(`
|
|
226623
227257
|
${c.bold(name)} ${c.dim(what)}`);
|
|
226624
|
-
const
|
|
227258
|
+
const run3 = async (name, what, fn) => {
|
|
226625
227259
|
heading(name, what);
|
|
226626
227260
|
try {
|
|
226627
227261
|
const r2 = await fn();
|
|
@@ -226636,77 +227270,88 @@ ${c.bold(name)} ${c.dim(what)}`);
|
|
|
226636
227270
|
}
|
|
226637
227271
|
};
|
|
226638
227272
|
const root = findProjectDir(process.cwd());
|
|
226639
|
-
await
|
|
227273
|
+
await run3("desktop", "LeCodes Desktop host (desktop run / render --desktop)", async () => {
|
|
226640
227274
|
if (process.env.LECODES_DESKTOP_EXE)
|
|
226641
227275
|
return ["skipped", `LECODES_DESKTOP_EXE=${process.env.LECODES_DESKTOP_EXE} (a local build; \`lecodes desktop update\` refreshes the cache anyway)`];
|
|
226642
227276
|
await updateDesktop();
|
|
226643
227277
|
return "ok";
|
|
226644
227278
|
});
|
|
226645
|
-
await
|
|
227279
|
+
await run3("matc", "Filament material compiler for local shader compiles", async () => {
|
|
226646
227280
|
if (process.env.LECODES_MATC)
|
|
226647
227281
|
return ["skipped", `LECODES_MATC=${process.env.LECODES_MATC} (a local matc)`];
|
|
226648
227282
|
await updateMatc();
|
|
226649
227283
|
return "ok";
|
|
226650
227284
|
});
|
|
226651
|
-
await
|
|
227285
|
+
await run3("shaders", "recompile the project's .mat shaders with the current matc", async () => {
|
|
226652
227286
|
if (!root)
|
|
226653
227287
|
return ["skipped", "not inside a project"];
|
|
226654
227288
|
await shaders({ _: ["compile"], flags: {} });
|
|
226655
227289
|
return "ok";
|
|
226656
227290
|
});
|
|
226657
|
-
await
|
|
227291
|
+
await run3("types", flagBool(args, "local") ? "IDE types from the SDK bundled in this CLI" : "IDE types from the server", async () => {
|
|
226658
227292
|
if (!root)
|
|
226659
227293
|
return ["skipped", "not inside a project"];
|
|
226660
227294
|
await types4({ _: [], flags: flagBool(args, "local") ? { local: true } : {} });
|
|
226661
227295
|
return "ok";
|
|
226662
227296
|
});
|
|
226663
|
-
await
|
|
227297
|
+
await run3("shells", "ios/ + android/ pins → newest SDK release + plugin tags, then re-sync", async () => {
|
|
226664
227298
|
if (!root)
|
|
226665
227299
|
return ["skipped", "not inside a project"];
|
|
226666
227300
|
if (flagBool(args, "no-shells"))
|
|
226667
227301
|
return ["skipped", "--no-shells"];
|
|
226668
|
-
const present = ["ios", "android"].filter((d) =>
|
|
227302
|
+
const present = ["ios", "android"].filter((d) => existsSync43(join44(root, d)) && readdirSync21(join44(root, d)).length > 0);
|
|
226669
227303
|
if (present.length === 0)
|
|
226670
227304
|
return ["skipped", "no ios/ or android/ shell in this project (`lecodes app init ios|android`)"];
|
|
226671
227305
|
await app({ _: ["update"], flags: {} });
|
|
226672
227306
|
return "ok";
|
|
226673
227307
|
});
|
|
226674
|
-
await
|
|
227308
|
+
await run3("cli", "lecodes-cli + its optional peers (global npm installs)", async () => {
|
|
226675
227309
|
if (flagBool(args, "no-cli"))
|
|
226676
227310
|
return ["skipped", "--no-cli"];
|
|
226677
227311
|
if (FROM_SOURCE)
|
|
226678
227312
|
return ["skipped", "running from the repo checkout — the CLI and its peers are the repo's own"];
|
|
226679
227313
|
const rows = [];
|
|
226680
227314
|
for (const [name, probe] of OPTIONAL_PEERS) {
|
|
227315
|
+
const where = peerSource(name, probe);
|
|
226681
227316
|
const installed = peerVersion(name, probe);
|
|
226682
|
-
|
|
227317
|
+
const latest = !installed ? null : where === "prefix" && name === "lecodes-renderer" ? package_default.version : await npmLatest(name);
|
|
227318
|
+
rows.push({ name, installed, latest, where });
|
|
226683
227319
|
}
|
|
226684
|
-
rows.push({ name: "lecodes-cli", installed: package_default.version, latest: await npmLatest("lecodes-cli") });
|
|
226685
|
-
if (rows.every((
|
|
227320
|
+
rows.push({ name: "lecodes-cli", installed: package_default.version, latest: await npmLatest("lecodes-cli"), where: "global" });
|
|
227321
|
+
if (rows.every((r2) => r2.latest === null))
|
|
226686
227322
|
return ["skipped", "npm unreachable"];
|
|
226687
|
-
const behind = rows.filter((
|
|
226688
|
-
for (const
|
|
226689
|
-
const state = !
|
|
226690
|
-
log(" " +
|
|
227323
|
+
const behind = rows.filter((r2) => r2.installed && r2.latest && semverNewer2(r2.latest, r2.installed) > 0);
|
|
227324
|
+
for (const r2 of rows) {
|
|
227325
|
+
const state = !r2.installed ? c.dim("not installed (optional — installed on first use)") : !r2.latest ? c.dim("(npm: unknown)") : behind.includes(r2) ? c.yellow("→ " + r2.latest) : c.dim("(up to date)");
|
|
227326
|
+
log(" " + r2.name.padEnd(18) + " " + (r2.installed ?? "").padEnd(8) + " " + (r2.where === "prefix" ? c.dim("peers ") : "") + state);
|
|
226691
227327
|
}
|
|
226692
227328
|
if (behind.length === 0)
|
|
226693
227329
|
return "ok";
|
|
226694
|
-
const
|
|
226695
|
-
|
|
226696
|
-
|
|
226697
|
-
|
|
227330
|
+
const prefixBehind = behind.filter((r2) => r2.where === "prefix");
|
|
227331
|
+
const globalSpecs = behind.filter((r2) => r2.where !== "prefix").map((r2) => r2.name + "@latest");
|
|
227332
|
+
if (flagBool(args, "check")) {
|
|
227333
|
+
return ["updated", behind.length + " behind — run: lecodes update" + (globalSpecs.length ? " (global: npm install -g " + globalSpecs.join(" ") + ")" : "")];
|
|
227334
|
+
}
|
|
227335
|
+
const done = [];
|
|
227336
|
+
if (prefixBehind.length) {
|
|
227337
|
+
await installPeers(prefixBehind.map((r2) => r2.name), "lecodes update", { yes: true });
|
|
227338
|
+
done.push(...prefixBehind.map((r2) => r2.name));
|
|
227339
|
+
}
|
|
227340
|
+
if (STANDALONE && globalSpecs.some((s2) => s2.startsWith("lecodes-cli@"))) {
|
|
226698
227341
|
note("this is the standalone build — the CLI updates by re-installing the release archive, not through npm; peers still go through npm");
|
|
226699
227342
|
}
|
|
226700
|
-
const toInstall = STANDALONE ?
|
|
226701
|
-
if (toInstall.length
|
|
227343
|
+
const toInstall = STANDALONE ? globalSpecs.filter((s2) => !s2.startsWith("lecodes-cli@")) : globalSpecs;
|
|
227344
|
+
if (toInstall.length) {
|
|
227345
|
+
log(" " + c.dim("$") + " npm install -g " + toInstall.join(" "));
|
|
227346
|
+
const r2 = spawnSync9("npm", ["install", "-g", ...toInstall], { stdio: "inherit", shell: true });
|
|
227347
|
+
if (r2.status !== 0)
|
|
227348
|
+
throw new CliError("npm install -g exited with " + (r2.status ?? "a signal"));
|
|
227349
|
+
done.push(...toInstall.map((s2) => s2.replace("@latest", "")));
|
|
227350
|
+
}
|
|
227351
|
+
if (done.length === 0)
|
|
226702
227352
|
return ["skipped", "only the standalone CLI itself is behind — download the release"];
|
|
226703
|
-
|
|
226704
|
-
|
|
226705
|
-
if (r2.status !== 0)
|
|
226706
|
-
throw new CliError("npm install -g exited with " + (r2.status ?? "a signal"));
|
|
226707
|
-
const names3 = toInstall.map((s2) => s2.replace("@latest", "")).join(", ");
|
|
226708
|
-
const cliToo = toInstall.some((s2) => s2.startsWith("lecodes-cli@"));
|
|
226709
|
-
return ["updated", names3 + " → latest" + (cliToo ? " (the new CLI runs from your next command)" : "")];
|
|
227353
|
+
const cliToo = done.includes("lecodes-cli");
|
|
227354
|
+
return ["updated", done.join(", ") + " → latest" + (cliToo ? " (the new CLI runs from your next command)" : "")];
|
|
226710
227355
|
});
|
|
226711
227356
|
log(`
|
|
226712
227357
|
${c.bold("Summary")}`);
|
|
@@ -226720,27 +227365,27 @@ ${c.bold("Summary")}`);
|
|
|
226720
227365
|
};
|
|
226721
227366
|
|
|
226722
227367
|
// src/index.ts
|
|
226723
|
-
import { existsSync as
|
|
226724
|
-
import { delimiter as
|
|
227368
|
+
import { existsSync as existsSync46, realpathSync as realpathSync2 } from "node:fs";
|
|
227369
|
+
import { delimiter as delimiter3, dirname as dirname24, join as join47 } from "node:path";
|
|
226725
227370
|
|
|
226726
227371
|
// src/commands/install.ts
|
|
226727
227372
|
init_util();
|
|
226728
227373
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
226729
|
-
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as cpSync5, existsSync as
|
|
226730
|
-
import { homedir as
|
|
226731
|
-
import { basename as
|
|
227374
|
+
import { appendFileSync as appendFileSync3, chmodSync as chmodSync2, cpSync as cpSync5, existsSync as existsSync44, mkdirSync as mkdirSync29, readFileSync as readFileSync37, realpathSync, rmSync as rmSync13, symlinkSync as symlinkSync2 } from "node:fs";
|
|
227375
|
+
import { homedir as homedir11 } from "node:os";
|
|
227376
|
+
import { basename as basename20, delimiter as delimiter2, dirname as dirname22, join as join45, resolve as resolve26, sep as sep2 } from "node:path";
|
|
226732
227377
|
var isWin = process.platform === "win32";
|
|
226733
|
-
var HOME =
|
|
227378
|
+
var HOME = homedir11();
|
|
226734
227379
|
var safeRealpath = (p2) => {
|
|
226735
227380
|
try {
|
|
226736
227381
|
return realpathSync(p2);
|
|
226737
227382
|
} catch {
|
|
226738
|
-
return
|
|
227383
|
+
return resolve26(p2);
|
|
226739
227384
|
}
|
|
226740
227385
|
};
|
|
226741
|
-
var
|
|
227386
|
+
var onPath2 = (dir) => {
|
|
226742
227387
|
const want = safeRealpath(dir);
|
|
226743
|
-
return (process.env.PATH ?? "").split(
|
|
227388
|
+
return (process.env.PATH ?? "").split(delimiter2).some((p2) => {
|
|
226744
227389
|
if (!p2)
|
|
226745
227390
|
return false;
|
|
226746
227391
|
try {
|
|
@@ -226751,25 +227396,25 @@ var onPath = (dir) => {
|
|
|
226751
227396
|
});
|
|
226752
227397
|
};
|
|
226753
227398
|
var install = async (args) => {
|
|
226754
|
-
const bundle =
|
|
226755
|
-
const exeName3 =
|
|
227399
|
+
const bundle = dirname22(process.execPath);
|
|
227400
|
+
const exeName3 = basename20(process.execPath);
|
|
226756
227401
|
const chiselName = isWin ? "chisel.exe" : "chisel";
|
|
226757
|
-
if (!
|
|
227402
|
+
if (!existsSync44(join45(bundle, "runtime")) || !existsSync44(join45(bundle, chiselName))) {
|
|
226758
227403
|
throw new CliError("`lecodes install` only works from the packaged release build (the archive containing lecodes + " + "chisel + runtime/). You appear to be running from source or an npm install — install globally " + "with your package manager there instead.");
|
|
226759
227404
|
}
|
|
226760
|
-
const
|
|
227405
|
+
const companions2 = [exeName3, chiselName, "runtime", "README.txt"].filter((f3) => existsSync44(join45(bundle, f3)));
|
|
226761
227406
|
if (isWin)
|
|
226762
|
-
return installWindows(args, bundle,
|
|
226763
|
-
return installUnix(args, bundle,
|
|
227407
|
+
return installWindows(args, bundle, companions2);
|
|
227408
|
+
return installUnix(args, bundle, companions2);
|
|
226764
227409
|
};
|
|
226765
|
-
var copyBundle = (bundle, target,
|
|
226766
|
-
|
|
227410
|
+
var copyBundle = (bundle, target, companions2) => {
|
|
227411
|
+
mkdirSync29(target, { recursive: true });
|
|
226767
227412
|
if (safeRealpath(target) === safeRealpath(bundle))
|
|
226768
227413
|
return;
|
|
226769
|
-
for (const f3 of
|
|
226770
|
-
const dest =
|
|
227414
|
+
for (const f3 of companions2) {
|
|
227415
|
+
const dest = join45(target, f3);
|
|
226771
227416
|
rmSync13(dest, { recursive: true, force: true });
|
|
226772
|
-
cpSync5(
|
|
227417
|
+
cpSync5(join45(bundle, f3), dest, { recursive: true });
|
|
226773
227418
|
}
|
|
226774
227419
|
};
|
|
226775
227420
|
var permError = (e2, dir, system) => {
|
|
@@ -226779,43 +227424,43 @@ var permError = (e2, dir, system) => {
|
|
|
226779
227424
|
}
|
|
226780
227425
|
return new CliError(`Install failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
226781
227426
|
};
|
|
226782
|
-
var installUnix = (args, bundle,
|
|
227427
|
+
var installUnix = (args, bundle, companions2) => {
|
|
226783
227428
|
const system = flagBool(args, "system");
|
|
226784
|
-
const libDir =
|
|
226785
|
-
const binDir =
|
|
226786
|
-
const link2 =
|
|
227429
|
+
const libDir = resolve26(flagStr(args, "dir") ?? (system ? "/usr/local/lib/lecodes-cli" : join45(HOME, ".local", "lib", "lecodes-cli")));
|
|
227430
|
+
const binDir = resolve26(flagStr(args, "bin") ?? (system ? "/usr/local/bin" : join45(HOME, ".local", "bin")));
|
|
227431
|
+
const link2 = join45(binDir, "lecodes");
|
|
226787
227432
|
try {
|
|
226788
|
-
copyBundle(bundle, libDir,
|
|
226789
|
-
chmodSync2(
|
|
226790
|
-
chmodSync2(
|
|
227433
|
+
copyBundle(bundle, libDir, companions2);
|
|
227434
|
+
chmodSync2(join45(libDir, "lecodes"), 493);
|
|
227435
|
+
chmodSync2(join45(libDir, "chisel"), 493);
|
|
226791
227436
|
} catch (e2) {
|
|
226792
227437
|
throw permError(e2, libDir, system);
|
|
226793
227438
|
}
|
|
226794
227439
|
try {
|
|
226795
|
-
|
|
227440
|
+
mkdirSync29(binDir, { recursive: true });
|
|
226796
227441
|
rmSync13(link2, { force: true });
|
|
226797
|
-
symlinkSync2(
|
|
227442
|
+
symlinkSync2(join45(libDir, "lecodes"), link2);
|
|
226798
227443
|
} catch (e2) {
|
|
226799
227444
|
throw permError(e2, binDir, system);
|
|
226800
227445
|
}
|
|
226801
227446
|
success(`Installed to ${c.bold(libDir)}`);
|
|
226802
|
-
log(` ${c.dim(link2 + " → " +
|
|
227447
|
+
log(` ${c.dim(link2 + " → " + join45(libDir, "lecodes"))}`);
|
|
226803
227448
|
finishPathUnix(args, binDir);
|
|
226804
227449
|
};
|
|
226805
227450
|
var shellRc = () => {
|
|
226806
|
-
const shell =
|
|
227451
|
+
const shell = basename20(process.env.SHELL ?? "");
|
|
226807
227452
|
if (shell === "zsh")
|
|
226808
|
-
return { file:
|
|
227453
|
+
return { file: join45(HOME, ".zshrc"), name: "~/.zshrc" };
|
|
226809
227454
|
if (shell === "bash")
|
|
226810
|
-
return { file:
|
|
226811
|
-
return { file:
|
|
227455
|
+
return { file: join45(HOME, ".bashrc"), name: "~/.bashrc" };
|
|
227456
|
+
return { file: join45(HOME, ".profile"), name: "~/.profile" };
|
|
226812
227457
|
};
|
|
226813
227458
|
var PATH_MARKER = "# Added by `lecodes install`";
|
|
226814
227459
|
var appendPathToShellRc = (binDir) => {
|
|
226815
227460
|
const { file, name } = shellRc();
|
|
226816
227461
|
const line = `export PATH="${binDir}:$PATH"`;
|
|
226817
227462
|
try {
|
|
226818
|
-
const existing =
|
|
227463
|
+
const existing = existsSync44(file) ? readFileSync37(file, "utf8") : "";
|
|
226819
227464
|
if (existing.includes(line))
|
|
226820
227465
|
return name;
|
|
226821
227466
|
appendFileSync3(file, `${existing && !existing.endsWith(`
|
|
@@ -226830,7 +227475,7 @@ ${line}
|
|
|
226830
227475
|
}
|
|
226831
227476
|
};
|
|
226832
227477
|
var finishPathUnix = (args, binDir) => {
|
|
226833
|
-
if (
|
|
227478
|
+
if (onPath2(binDir)) {
|
|
226834
227479
|
log(` Run ${c.bold("lecodes --version")} in a new shell to verify.`);
|
|
226835
227480
|
return;
|
|
226836
227481
|
}
|
|
@@ -226852,15 +227497,15 @@ var finishPathUnix = (args, binDir) => {
|
|
|
226852
227497
|
log(` • let install do it: re-run with ${c.bold("--modify-path")}`);
|
|
226853
227498
|
log(` • or install system-wide (no PATH edit): ${c.bold("sudo lecodes install --system")}`);
|
|
226854
227499
|
};
|
|
226855
|
-
var installWindows = (args, bundle,
|
|
226856
|
-
const target =
|
|
227500
|
+
var installWindows = (args, bundle, companions2) => {
|
|
227501
|
+
const target = resolve26(flagStr(args, "dir") ?? join45(process.env.LOCALAPPDATA ?? join45(HOME, "AppData", "Local"), "Programs", "lecodes-cli"));
|
|
226857
227502
|
try {
|
|
226858
|
-
copyBundle(bundle, target,
|
|
227503
|
+
copyBundle(bundle, target, companions2);
|
|
226859
227504
|
} catch (e2) {
|
|
226860
227505
|
throw new CliError(`Install failed writing to ${target}: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
226861
227506
|
}
|
|
226862
227507
|
success(`Installed to ${c.bold(target)}`);
|
|
226863
|
-
if (
|
|
227508
|
+
if (onPath2(target)) {
|
|
226864
227509
|
log(` Already on PATH — run ${c.bold("lecodes --version")} in a new terminal.`);
|
|
226865
227510
|
return;
|
|
226866
227511
|
}
|
|
@@ -226892,9 +227537,9 @@ var addToUserPathWindows = (dir) => {
|
|
|
226892
227537
|
init_util();
|
|
226893
227538
|
|
|
226894
227539
|
// src/projectEnv.ts
|
|
226895
|
-
import { existsSync as
|
|
226896
|
-
import { homedir as
|
|
226897
|
-
import { dirname as
|
|
227540
|
+
import { existsSync as existsSync45, readFileSync as readFileSync38 } from "node:fs";
|
|
227541
|
+
import { homedir as homedir12 } from "node:os";
|
|
227542
|
+
import { dirname as dirname23, join as join46, resolve as resolve27 } from "node:path";
|
|
226898
227543
|
var KEY_RE = /^(LECODES|CREATOR)_[A-Z0-9_]+$/;
|
|
226899
227544
|
var parseDotEnv = (text) => {
|
|
226900
227545
|
const out = {};
|
|
@@ -226929,19 +227574,22 @@ var applyDotEnv = (parsed, dir, env) => {
|
|
|
226929
227574
|
continue;
|
|
226930
227575
|
let value = raw;
|
|
226931
227576
|
if (value.startsWith("./") || value.startsWith("../"))
|
|
226932
|
-
value =
|
|
227577
|
+
value = resolve27(dir, value);
|
|
226933
227578
|
else if (value === "~" || value.startsWith("~/"))
|
|
226934
|
-
value =
|
|
227579
|
+
value = join46(homedir12(), value.slice(1));
|
|
226935
227580
|
out[key] = value;
|
|
226936
227581
|
}
|
|
226937
227582
|
return out;
|
|
226938
227583
|
};
|
|
226939
227584
|
var findEnvRoot = (start) => {
|
|
226940
|
-
let dir =
|
|
227585
|
+
let dir = resolve27(start);
|
|
227586
|
+
const home = resolve27(homedir12());
|
|
226941
227587
|
while (true) {
|
|
226942
|
-
if (
|
|
227588
|
+
if (dir === home)
|
|
227589
|
+
return null;
|
|
227590
|
+
if (existsSync45(join46(dir, "app.json")) || existsSync45(join46(dir, ".lecodes")))
|
|
226943
227591
|
return dir;
|
|
226944
|
-
const parent =
|
|
227592
|
+
const parent = dirname23(dir);
|
|
226945
227593
|
if (parent === dir)
|
|
226946
227594
|
return null;
|
|
226947
227595
|
dir = parent;
|
|
@@ -226952,10 +227600,10 @@ var loadProjectEnv = (cwd = process.cwd()) => {
|
|
|
226952
227600
|
const root = findEnvRoot(cwd);
|
|
226953
227601
|
if (!root)
|
|
226954
227602
|
return [];
|
|
226955
|
-
const file =
|
|
226956
|
-
if (!
|
|
227603
|
+
const file = join46(root, ".env");
|
|
227604
|
+
if (!existsSync45(file))
|
|
226957
227605
|
return [];
|
|
226958
|
-
const added = applyDotEnv(parseDotEnv(
|
|
227606
|
+
const added = applyDotEnv(parseDotEnv(readFileSync38(file, "utf8")), root, process.env);
|
|
226959
227607
|
for (const [k, v] of Object.entries(added))
|
|
226960
227608
|
process.env[k] = v;
|
|
226961
227609
|
return Object.keys(added);
|
|
@@ -226965,10 +227613,11 @@ var loadProjectEnv = (cwd = process.cwd()) => {
|
|
|
226965
227613
|
};
|
|
226966
227614
|
|
|
226967
227615
|
// src/index.ts
|
|
227616
|
+
init_peers();
|
|
226968
227617
|
var STANDALONE2 = (() => {
|
|
226969
|
-
const dir =
|
|
227618
|
+
const dir = dirname24(process.execPath);
|
|
226970
227619
|
const chisel = process.platform === "win32" ? "chisel.exe" : "chisel";
|
|
226971
|
-
return
|
|
227620
|
+
return existsSync46(join47(dir, "runtime")) && existsSync46(join47(dir, chisel));
|
|
226972
227621
|
})();
|
|
226973
227622
|
var INSTALLED = STANDALONE2 && (() => {
|
|
226974
227623
|
const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes";
|
|
@@ -226979,12 +227628,12 @@ var INSTALLED = STANDALONE2 && (() => {
|
|
|
226979
227628
|
return process.execPath;
|
|
226980
227629
|
}
|
|
226981
227630
|
})();
|
|
226982
|
-
return (process.env.PATH ?? "").split(
|
|
227631
|
+
return (process.env.PATH ?? "").split(delimiter3).some((d) => {
|
|
226983
227632
|
if (!d)
|
|
226984
227633
|
return false;
|
|
226985
227634
|
try {
|
|
226986
|
-
const cand =
|
|
226987
|
-
return
|
|
227635
|
+
const cand = join47(d, exe);
|
|
227636
|
+
return existsSync46(cand) && realpathSync2(cand) === self2;
|
|
226988
227637
|
} catch {
|
|
226989
227638
|
return false;
|
|
226990
227639
|
}
|
|
@@ -227052,6 +227701,17 @@ ${c.bold("Commands:")}
|
|
|
227052
227701
|
--host <ip> LAN address to advertise (default: auto-detect)
|
|
227053
227702
|
--entry <path> override the entrypoint
|
|
227054
227703
|
--no-qr don't render the QR code
|
|
227704
|
+
--android open the dev url in the LeCodes app on the phone
|
|
227705
|
+
plugged in over adb (adb reverse: no Wi-Fi/LAN
|
|
227706
|
+
address needed). --serial <id> picks a device,
|
|
227707
|
+
--apk <file> installs first, --package <name>
|
|
227708
|
+
(default com.letary.lecodes). Device logs stream
|
|
227709
|
+
here. --time <s> --png <file>: bounded check —
|
|
227710
|
+
screenshot after <s> seconds, force-stop, exit
|
|
227711
|
+
(exit 1 on a crash). Ctrl+C force-stops the app.
|
|
227712
|
+
--web also open the app in the browser (Web Lite host:
|
|
227713
|
+
same bundle + hot reload, camera/map plugins
|
|
227714
|
+
have web twins; the /web/ URL is served always)
|
|
227055
227715
|
--desktop also open the app in the native desktop host
|
|
227056
227716
|
(same hot reload; window from app.json's
|
|
227057
227717
|
desktop block; closing it keeps the server)
|
|
@@ -227244,12 +227904,15 @@ ${c.bold("Commands:")}
|
|
|
227244
227904
|
--cell <m> (0.25) --cell-height <m> (0.1) --tile <cells> (48)
|
|
227245
227905
|
--obj also write <n>.navmesh.obj to inspect
|
|
227246
227906
|
--frames <n> load headroom (900) --logs host output
|
|
227247
|
-
shaders [compile|update]
|
|
227907
|
+
shaders [compile|update|new] The project's custom shaders (.mat) and their local compile:
|
|
227248
227908
|
no args lists the shaders + their compile state; compile
|
|
227249
227909
|
compiles them all into .lecodes/shaders (the same pass
|
|
227250
227910
|
'lecodes dev' runs on the fly — via Filament's matc,
|
|
227251
227911
|
downloaded on first use to ~/.lecodes/matc); update
|
|
227252
|
-
downloads the newest matc (run after an engine upgrade)
|
|
227912
|
+
downloads the newest matc (run after an engine upgrade);
|
|
227913
|
+
new <name> [--ref lit] [--dir d] [--force] writes a fresh
|
|
227914
|
+
.mat + .material.ts: the starter, or a copy of the engine's
|
|
227915
|
+
own lit / unlit / particles / … source to start from
|
|
227253
227916
|
scene [path] LeCodes Scene Editor — visually edit a .scene.ts (tree, inspector,
|
|
227254
227917
|
gizmo) on local files (needs the optional 'lecodes-3d-editor' package;
|
|
227255
227918
|
works inside a cloned project or standalone in any folder)
|
|
@@ -227364,12 +228027,13 @@ LECODES_DESKTOP_EXE, LECODES_MATC, LECODES_NOTARY_PROFILE, LECODES_MACOS_SIGN_ID
|
|
|
227364
228027
|
var commands = { init: init2, login, create, clone: clone3, link, status: status3, diff: diff2, pull, push, compile: compile2, preview: previewLegacy, render, test, design, scene, thumbs, dev, app, pn, desktop, shaders, assets, lightmap, navmesh, update };
|
|
227365
228028
|
if (STANDALONE2)
|
|
227366
228029
|
commands.install = install;
|
|
227367
|
-
var
|
|
228030
|
+
var readVersion2 = () => package_default.version ?? "unknown";
|
|
227368
228031
|
var printVersion = () => {
|
|
227369
|
-
const rows = [["lecodes-cli",
|
|
228032
|
+
const rows = [["lecodes-cli", readVersion2()]];
|
|
227370
228033
|
for (const [name, probe] of OPTIONAL_PEERS) {
|
|
227371
228034
|
const version = peerVersion(name, probe);
|
|
227372
|
-
|
|
228035
|
+
const where = peerSource(name, probe);
|
|
228036
|
+
rows.push([name, version ? where === "prefix" ? `${version} ${c.dim("(~/.lecodes/peers)")}` : version : c.dim("not installed — installed on first use")]);
|
|
227373
228037
|
}
|
|
227374
228038
|
const desktopVersion = selectedDesktopVersion();
|
|
227375
228039
|
rows.push(["lecodes-desktop", installedDesktopVersions().includes(desktopVersion) ? desktopVersion : `${desktopVersion} ${c.dim("(not downloaded — fetched on first `render --desktop`)")}`]);
|
|
@@ -227383,6 +228047,8 @@ var main = async () => {
|
|
|
227383
228047
|
const argv = process.argv.slice(2);
|
|
227384
228048
|
const command = argv[0];
|
|
227385
228049
|
loadProjectEnv();
|
|
228050
|
+
if (argv.includes("--yes") || argv.includes("-y"))
|
|
228051
|
+
process.env.LECODES_YES = "1";
|
|
227386
228052
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
227387
228053
|
log(HELP);
|
|
227388
228054
|
return;
|
|
@@ -227391,14 +228057,14 @@ var main = async () => {
|
|
|
227391
228057
|
printVersion();
|
|
227392
228058
|
return;
|
|
227393
228059
|
}
|
|
227394
|
-
const
|
|
227395
|
-
if (!
|
|
228060
|
+
const run3 = commands[command];
|
|
228061
|
+
if (!run3) {
|
|
227396
228062
|
log(c.red(`Unknown command: ${command}`));
|
|
227397
228063
|
log(HELP);
|
|
227398
228064
|
process.exitCode = 1;
|
|
227399
228065
|
return;
|
|
227400
228066
|
}
|
|
227401
|
-
await
|
|
228067
|
+
await run3(Object.assign(parseArgs(argv.slice(1)), { _raw: argv.slice(1) }));
|
|
227402
228068
|
};
|
|
227403
228069
|
main().catch((e2) => {
|
|
227404
228070
|
if (e2 instanceof CliError)
|