lecodes-cli 0.6.4 → 0.7.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 +920 -636
- package/package.json +9 -4
- package/runtime/scene-harness.json +1 -0
- package/runtime/sdk/compile/assetMacro.ts +4 -3
- package/runtime/sdk/compile/bundler.ts +35 -3
- package/runtime/sdk/compile/compileProject.ts +14 -2
- package/runtime/sdk/compile/libraryImports.ts +47 -0
- package/runtime/sdk/compile/sceneEditor.ts +16 -4
- package/runtime/sdk/core/Aspect.ts +37 -0
- package/runtime/sdk/core/InspectorUI.ts +212 -0
- package/runtime/sdk/core/fields.ts +15 -3
- package/runtime/sdk/g2/Scene2D.ts +51 -5
- package/runtime/sdk/gl/Model.ts +9 -0
- package/runtime/sdk/gl/Node.ts +4 -1
- package/runtime/sdk/gl/Scene.ts +76 -11
- package/runtime/sdk/gl/scenarios.ts +349 -0
- package/runtime/sdk/inject.ts +28 -8
- package/runtime/sdk/kit/UITabs.ts +105 -0
- package/runtime/sdk/plugins/camera.ts +81 -0
- package/runtime/sdk/plugins/geolocation.ts +123 -0
- package/runtime/sdk/plugins/permission.ts +7 -0
- package/runtime/sdk/plugins/qr.ts +61 -24
- package/runtime/sdk/runtime/app.ts +37 -0
- package/runtime/sdk/runtime/appEvents.ts +29 -0
- package/runtime/sdk/runtime/channel.ts +50 -0
- package/runtime/sdk/runtime/clipboard.ts +20 -0
- package/runtime/sdk/runtime/datetime.ts +2 -4
- package/runtime/sdk/runtime/device.ts +137 -4
- package/runtime/sdk/runtime/misc.ts +4 -0
- package/runtime/sdk/runtime/service.ts +82 -0
- package/runtime/sdk/runtime/touch.ts +26 -0
- package/runtime/sdk/scene/defineScene.ts +651 -17
- package/runtime/sdk/scene/editorPlugins.ts +86 -0
- package/runtime/sdk/ui/NativeView.ts +144 -0
- package/runtime/sdk/ui/UI.ts +6 -1
- package/runtime/sdk/ui/UIButton.ts +26 -3
- package/runtime/sdk/ui/UIInput.ts +2 -2
- package/runtime/sdk/ui/UINode.ts +24 -4
- package/runtime/sdk/ui/UIScreen.ts +86 -29
- package/runtime/sdk/ui/UIScreenHost.ts +213 -0
- package/runtime/sdk/ui/UIText.ts +1 -1
- package/runtime/sdk/ui/UIVideo.ts +48 -2
- package/runtime/sdk/ui/UIWidget.ts +34 -1
- package/runtime/sdk/ui/presentable.ts +116 -0
- package/runtime/sdk/ui/router.ts +73 -29
- package/runtime/sdk-types.json +1 -1
- package/runtime/sdk/runtime/camera.ts +0 -31
package/dist/index.js
CHANGED
|
@@ -31,133 +31,538 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
31
31
|
return to;
|
|
32
32
|
};
|
|
33
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
var __returnValue = (v) => v;
|
|
35
|
+
function __exportSetter(name, newValue) {
|
|
36
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
37
|
+
}
|
|
38
|
+
var __export = (target, all) => {
|
|
39
|
+
for (var name in all)
|
|
40
|
+
__defProp(target, name, {
|
|
41
|
+
get: all[name],
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
set: __exportSetter.bind(all, name)
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
34
48
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
35
49
|
|
|
36
|
-
//
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
// ../sdk/src/compile/assetMacro.ts
|
|
51
|
+
var ASSET_RE, hasAssetMacro = (src) => src.includes("asset("), desugarAssetMacros = (src, filePath) => {
|
|
52
|
+
const byPath = new Map;
|
|
53
|
+
let i = 0;
|
|
54
|
+
const out = src.replace(ASSET_RE, (_match, _quote, path) => {
|
|
55
|
+
let name = byPath.get(path);
|
|
56
|
+
if (!name) {
|
|
57
|
+
name = `__asset_${i++}`;
|
|
58
|
+
byPath.set(path, name);
|
|
59
|
+
}
|
|
60
|
+
return name;
|
|
61
|
+
});
|
|
62
|
+
if (/(?<![.\w$])asset\(/.test(out)) {
|
|
63
|
+
throw new Error(`asset() expects a string literal, found a dynamic argument in: ${filePath}`);
|
|
64
|
+
}
|
|
65
|
+
if (byPath.size === 0)
|
|
66
|
+
return src;
|
|
67
|
+
const imports = [...byPath].map(([path, name]) => `import ${name} from ${JSON.stringify(path)};`).join(`
|
|
68
|
+
`);
|
|
69
|
+
return out + `
|
|
70
|
+
` + imports;
|
|
71
|
+
};
|
|
72
|
+
var init_assetMacro = __esm(() => {
|
|
73
|
+
ASSET_RE = /(?<![.\w$])asset\(\s*(["'])((?:[^"'\\]|\\.)*)\1\s*\)/g;
|
|
44
74
|
});
|
|
45
75
|
|
|
46
|
-
//
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
// ../sdk/src/compile/aspectMacro.ts
|
|
77
|
+
var ASPECT_RE, hasAspectExtends = (src) => /\bextends\s+Aspect\s*</.test(src), injectAspectNames = (src) => {
|
|
78
|
+
let out = "";
|
|
79
|
+
let last = 0;
|
|
80
|
+
ASPECT_RE.lastIndex = 0;
|
|
81
|
+
let m;
|
|
82
|
+
while ((m = ASPECT_RE.exec(src)) !== null) {
|
|
83
|
+
const braceEnd = m.index + m[0].length;
|
|
84
|
+
if (!classBodyDeclaresAspect(src, braceEnd)) {
|
|
85
|
+
out += src.slice(last, braceEnd) + ` static aspect = ${JSON.stringify(m[2])};`;
|
|
86
|
+
last = braceEnd;
|
|
87
|
+
}
|
|
52
88
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
89
|
+
return last === 0 ? src : out + src.slice(last);
|
|
90
|
+
}, classBodyDeclaresAspect = (src, from) => {
|
|
91
|
+
let depth = 1;
|
|
92
|
+
let i = from;
|
|
93
|
+
for (;i < src.length && depth > 0; i++) {
|
|
94
|
+
const c2 = src[i];
|
|
95
|
+
if (c2 === "{")
|
|
96
|
+
depth++;
|
|
97
|
+
else if (c2 === "}")
|
|
98
|
+
depth--;
|
|
99
|
+
}
|
|
100
|
+
return /\bstatic\b[^;{}()=]*\baspect\b/.test(src.slice(from, i));
|
|
101
|
+
};
|
|
102
|
+
var init_aspectMacro = __esm(() => {
|
|
103
|
+
ASPECT_RE = /\bclass\s+\w+\s+extends\s+Aspect\s*<\s*(["'])((?:[^"'\\]|\\.)*)\1[^{};]*>\s*\{/g;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ../sdk/src/compile/libraryImports.ts
|
|
107
|
+
var hasImportStatement = (src) => src.includes("import") || src.includes("from"), IMPORT_FROM_RE, SIDE_EFFECT_RE, isRelativeOrAbsolute = (spec) => spec.startsWith("/") || spec.startsWith("./") || spec.startsWith("../"), rewriteLibraryImports = (src, slugs) => {
|
|
108
|
+
const rewriteSpec = (spec) => {
|
|
109
|
+
if (isRelativeOrAbsolute(spec))
|
|
110
|
+
return spec;
|
|
111
|
+
const slash = spec.indexOf("/");
|
|
112
|
+
const slug3 = slash === -1 ? spec : spec.slice(0, slash);
|
|
113
|
+
if (slugs.has(slug3)) {
|
|
114
|
+
const sub = slash === -1 ? "/index" : spec.slice(slash);
|
|
115
|
+
return `/__lib/${slug3}${sub}`;
|
|
61
116
|
}
|
|
117
|
+
throw new Error(`Unknown library "${slug3}" — add it in Settings → Libraries, or use a relative path like "./${slug3}".`);
|
|
62
118
|
};
|
|
63
|
-
|
|
119
|
+
let out = src.replace(IMPORT_FROM_RE, (_m, boundary, head, quote, spec) => `${boundary}${head}${quote}${rewriteSpec(spec)}${quote}`);
|
|
120
|
+
out = out.replace(SIDE_EFFECT_RE, (m, boundary, head, quote, spec) => isRelativeOrAbsolute(spec) ? m : `${boundary}${head}${quote}${rewriteSpec(spec)}${quote}`);
|
|
121
|
+
return out;
|
|
122
|
+
};
|
|
123
|
+
var init_libraryImports = __esm(() => {
|
|
124
|
+
IMPORT_FROM_RE = /(^|[;\n}])(\s*(?:import|export)\b[^"'`;]*?\bfrom\s*)(["'])([^"'\n]+)\3/g;
|
|
125
|
+
SIDE_EFFECT_RE = /(^|[;\n}])(\s*import\s*)(["'])([^"'\n]+)\3/g;
|
|
64
126
|
});
|
|
65
127
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
var i;
|
|
91
|
-
for (i = 8;i < 256; i++) {
|
|
92
|
-
QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
|
|
128
|
+
// ../sdk/src/compile/bundler.ts
|
|
129
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
130
|
+
import { spawnSync } from "node:child_process";
|
|
131
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
132
|
+
import { dirname as dirname5, join as join9, relative as relative2 } from "node:path";
|
|
133
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
134
|
+
var resolveInject = () => {
|
|
135
|
+
const override = process.env.LECODES_SDK_INJECT;
|
|
136
|
+
if (override && existsSync7(override))
|
|
137
|
+
return override;
|
|
138
|
+
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
139
|
+
const src = join9(here, "..", "inject.ts");
|
|
140
|
+
if (existsSync7(src))
|
|
141
|
+
return src;
|
|
142
|
+
throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
|
|
143
|
+
}, normalize = (path) => {
|
|
144
|
+
const parts = [];
|
|
145
|
+
for (const part of path.split("/")) {
|
|
146
|
+
if (!part || part === ".")
|
|
147
|
+
continue;
|
|
148
|
+
if (part === "..")
|
|
149
|
+
parts.pop();
|
|
150
|
+
else
|
|
151
|
+
parts.push(part);
|
|
93
152
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
153
|
+
return "/" + parts.join("/");
|
|
154
|
+
}, CHISEL_DEFINE, CHISEL_KEEP, resolveSdkSrc = () => {
|
|
155
|
+
const inject = resolveInject();
|
|
156
|
+
if (!inject.endsWith("inject.ts")) {
|
|
157
|
+
throw new Error(`chisel needs the SDK source tree (src/inject.ts); got ${inject}. Set LECODES_SDK_INJECT to the source.`);
|
|
97
158
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
offset++;
|
|
159
|
+
return dirname5(inject);
|
|
160
|
+
}, isSdkSource = (path) => /\.[tj]s$/.test(path) && !path.endsWith(".d.ts") && !path.includes("/compile/") && !path.includes("/tests/") && !path.includes("/__tests__/"), sdkFilesCache, collectSdkFiles = (sdkSrc) => {
|
|
161
|
+
const cached = sdkFilesCache.get(sdkSrc);
|
|
162
|
+
if (cached)
|
|
163
|
+
return cached;
|
|
164
|
+
const out = {};
|
|
165
|
+
const walk = (dir) => {
|
|
166
|
+
for (const name of readdirSync4(dir)) {
|
|
167
|
+
const p = join9(dir, name);
|
|
168
|
+
if (statSync(p).isDirectory())
|
|
169
|
+
walk(p);
|
|
170
|
+
else if (isSdkSource(p))
|
|
171
|
+
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] = readFileSync6(p, "utf8");
|
|
112
172
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
173
|
+
};
|
|
174
|
+
walk(sdkSrc);
|
|
175
|
+
sdkFilesCache.set(sdkSrc, out);
|
|
176
|
+
return out;
|
|
177
|
+
}, resolveChiselBin = () => {
|
|
178
|
+
const override = process.env.LECODES_CHISEL_BIN;
|
|
179
|
+
if (override) {
|
|
180
|
+
if (existsSync7(override))
|
|
181
|
+
return override;
|
|
182
|
+
throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const bin = createRequire2(import.meta.url)("@letary/chisel").binaryPath();
|
|
186
|
+
if (bin && existsSync7(bin))
|
|
187
|
+
return bin;
|
|
188
|
+
} catch {}
|
|
189
|
+
throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
|
|
190
|
+
}, bundleProjectChisel = async (entrypoint, files, format, sourcemap, minifyOverride, editor, libraries) => {
|
|
191
|
+
const entry = normalize(entrypoint);
|
|
192
|
+
const slugs = new Set(Object.keys(libraries ?? {}));
|
|
193
|
+
const projFiles = {};
|
|
194
|
+
for (const [k, v] of Object.entries(files)) {
|
|
195
|
+
const path = normalize(k);
|
|
196
|
+
let src = v;
|
|
197
|
+
if (/\.[tj]sx?$/.test(path)) {
|
|
198
|
+
if (hasImportStatement(src))
|
|
199
|
+
src = rewriteLibraryImports(src, slugs);
|
|
200
|
+
if (hasAssetMacro(src))
|
|
201
|
+
src = desugarAssetMacros(src, path);
|
|
202
|
+
if (hasAspectExtends(src))
|
|
203
|
+
src = injectAspectNames(src);
|
|
116
204
|
}
|
|
205
|
+
projFiles[path] = src;
|
|
117
206
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return new QRPolynomial(num, 0);
|
|
133
|
-
},
|
|
134
|
-
mod: function(e) {
|
|
135
|
-
if (this.getLength() - e.getLength() < 0) {
|
|
136
|
-
return this;
|
|
137
|
-
}
|
|
138
|
-
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
|
|
139
|
-
var num = new Array(this.getLength());
|
|
140
|
-
for (var i = 0;i < this.getLength(); i++) {
|
|
141
|
-
num[i] = this.get(i);
|
|
142
|
-
}
|
|
143
|
-
for (var x = 0;x < e.getLength(); x++) {
|
|
144
|
-
num[x] ^= QRMath.gexp(QRMath.glog(e.get(x)) + ratio);
|
|
207
|
+
if (!projFiles[entry])
|
|
208
|
+
throw new Error(`Entrypoint not found: ${entry}`);
|
|
209
|
+
for (const [slug3, libFiles] of Object.entries(libraries ?? {})) {
|
|
210
|
+
for (const [k, v] of Object.entries(libFiles)) {
|
|
211
|
+
const path = normalize(`/__lib/${slug3}/${k}`);
|
|
212
|
+
let src = v;
|
|
213
|
+
if (/\.[tj]sx?$/.test(path)) {
|
|
214
|
+
if (hasAssetMacro(src))
|
|
215
|
+
src = desugarAssetMacros(src, path);
|
|
216
|
+
if (hasAspectExtends(src))
|
|
217
|
+
src = injectAspectNames(src);
|
|
145
218
|
}
|
|
146
|
-
|
|
219
|
+
projFiles[path] = src;
|
|
147
220
|
}
|
|
148
|
-
}
|
|
149
|
-
|
|
221
|
+
}
|
|
222
|
+
const sdkFiles = collectSdkFiles(resolveSdkSrc());
|
|
223
|
+
const minify = minifyOverride ?? process.env.LECODES_CHISEL_MINIFY !== "0";
|
|
224
|
+
const input = JSON.stringify({
|
|
225
|
+
files: { ...sdkFiles, ...projFiles },
|
|
226
|
+
entry,
|
|
227
|
+
inject: ["/__sdk/inject.ts"],
|
|
228
|
+
format,
|
|
229
|
+
minify,
|
|
230
|
+
fuse: process.env.LECODES_CHISEL_FUSE === "1",
|
|
231
|
+
define: { ...CHISEL_DEFINE, EDITOR: editor ? "true" : "false" },
|
|
232
|
+
keep: CHISEL_KEEP,
|
|
233
|
+
sourcemap,
|
|
234
|
+
reactive_ui: process.env.LECODES_CHISEL_REACTIVE_UI !== "0"
|
|
235
|
+
});
|
|
236
|
+
const bin = resolveChiselBin();
|
|
237
|
+
const r = spawnSync(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
|
|
238
|
+
if (r.status === 2 || r.error) {
|
|
239
|
+
throw new Error(`chisel failed: ${r.error?.message ?? r.stderr ?? "unknown error"}`);
|
|
240
|
+
}
|
|
241
|
+
let parsed;
|
|
242
|
+
try {
|
|
243
|
+
parsed = JSON.parse(r.stdout || "{}");
|
|
244
|
+
} catch {
|
|
245
|
+
throw new Error(`chisel produced invalid output: ${(r.stdout || r.stderr || "").slice(0, 200)}`);
|
|
246
|
+
}
|
|
247
|
+
if (parsed.error)
|
|
248
|
+
throw new Error(`chisel: ${parsed.error}`);
|
|
249
|
+
if (parsed.code == null)
|
|
250
|
+
throw new Error("chisel produced no code");
|
|
251
|
+
return { code: parsed.code, map: parsed.map ?? null };
|
|
252
|
+
}, bundleProject = async (entrypoint, files, format = "esm") => (await bundleProjectChisel(entrypoint, files, format, false)).code, bundleProjectWithMap = async (entrypoint, files, format = "esm", minify, editor, libraries) => bundleProjectChisel(entrypoint, files, format, true, minify, editor, libraries);
|
|
253
|
+
var init_bundler = __esm(() => {
|
|
254
|
+
init_assetMacro();
|
|
255
|
+
init_aspectMacro();
|
|
256
|
+
init_libraryImports();
|
|
257
|
+
CHISEL_DEFINE = { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" };
|
|
258
|
+
CHISEL_KEEP = ["_*"];
|
|
259
|
+
sdkFilesCache = new Map;
|
|
150
260
|
});
|
|
151
261
|
|
|
152
|
-
//
|
|
153
|
-
var
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
262
|
+
// ../sdk/src/compile/sourcemap.ts
|
|
263
|
+
var offsetSourceMap = (mapJson, lines) => {
|
|
264
|
+
if (lines <= 0)
|
|
265
|
+
return mapJson;
|
|
266
|
+
const map = JSON.parse(mapJson);
|
|
267
|
+
map.mappings = ";".repeat(lines) + map.mappings;
|
|
268
|
+
return JSON.stringify(map);
|
|
269
|
+
}, inlineSourceMapComment = (mapJson) => {
|
|
270
|
+
const b64 = Buffer.from(mapJson, "utf8").toString("base64");
|
|
271
|
+
return `//# lecodesMap=data:application/json;charset=utf-8;base64,${b64}`;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// ../sdk/src/compile/header.ts
|
|
275
|
+
var buildHeader = (name, code, opts = {}) => {
|
|
276
|
+
let header = `// name: ${name.slice(0, 20)}
|
|
277
|
+
`;
|
|
278
|
+
if (opts.icon)
|
|
279
|
+
header += `// icon: ${opts.icon}
|
|
280
|
+
`;
|
|
281
|
+
header += `// last-updated: ${new Date().toISOString()}
|
|
282
|
+
`;
|
|
283
|
+
const localAssets = new Set;
|
|
284
|
+
for (const m of code.matchAll(/_creatorUtils\.fetchLocal\((.+?)\)/g)) {
|
|
285
|
+
localAssets.add(m[1].replaceAll(/['"]/g, "").trim());
|
|
286
|
+
}
|
|
287
|
+
if (/_creator\.createGlb/.test(code))
|
|
288
|
+
localAssets.add("uberarchive");
|
|
289
|
+
if (/_creator\.captureImage/.test(code))
|
|
290
|
+
localAssets.add("capture");
|
|
291
|
+
if (localAssets.size > 0)
|
|
292
|
+
header += `// preload: ${[...localAssets].join(";")}
|
|
293
|
+
`;
|
|
294
|
+
if (!code.includes("_creator.createScene") && !code.includes("_creator.createEntity"))
|
|
295
|
+
header += `// gl: disable
|
|
296
|
+
`;
|
|
297
|
+
else if (code.includes("_creator.createEngine"))
|
|
298
|
+
header += `// gl: defer
|
|
299
|
+
`;
|
|
300
|
+
if (code.includes("_creator2d."))
|
|
301
|
+
header += `// 2d: enable
|
|
302
|
+
`;
|
|
303
|
+
if (code.includes("_creator.createARController"))
|
|
304
|
+
header += "// ar: enable";
|
|
305
|
+
header += `
|
|
306
|
+
`;
|
|
307
|
+
return header;
|
|
308
|
+
}, detectProjectKind = (code) => {
|
|
309
|
+
if (code.includes("_creator.createARController"))
|
|
310
|
+
return "ar";
|
|
311
|
+
if (code.includes("_creator2d."))
|
|
312
|
+
return "2d";
|
|
313
|
+
if (!code.includes("_creator."))
|
|
314
|
+
return "ui";
|
|
315
|
+
return "3d";
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// ../sdk/src/compile/compileProject.ts
|
|
319
|
+
var detectEntry = (entries) => {
|
|
320
|
+
let entry = "";
|
|
321
|
+
let max = -1;
|
|
322
|
+
for (const e of entries) {
|
|
323
|
+
const ext2 = e.path.split(".").at(-1)?.toLowerCase();
|
|
324
|
+
if (e.path.endsWith(".editor.ts"))
|
|
325
|
+
continue;
|
|
326
|
+
if (e.type === "text" && e.text !== undefined && !e.text.includes("export") && (ext2 === "ts" || ext2 === "js")) {
|
|
327
|
+
if (e.text.length > max) {
|
|
328
|
+
entry = e.path;
|
|
329
|
+
max = e.text.length;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return entry;
|
|
334
|
+
}, compileProject = async (opts) => {
|
|
335
|
+
const entry = opts.entryOverride ?? detectEntry(opts.entries);
|
|
336
|
+
if (!entry) {
|
|
337
|
+
throw new Error("Couldn't find an entrypoint (a top-level .ts/.js file with no `export`). Pass entryOverride.");
|
|
338
|
+
}
|
|
339
|
+
const files = {};
|
|
340
|
+
for (const e of opts.entries) {
|
|
341
|
+
if (e.type === "resource") {
|
|
342
|
+
if (opts.localAssets) {
|
|
343
|
+
const name = e.path.split(/[\\/]/).pop() ?? e.path;
|
|
344
|
+
files[e.path] = `export default ("id:" + _creatorUtils.fetchLocal(${JSON.stringify(name)}))`;
|
|
345
|
+
} else {
|
|
346
|
+
files[e.path] = `export default "${opts.publicUrl}${e.fileSrc ?? ""}"`;
|
|
347
|
+
}
|
|
348
|
+
} else if (e.type === "shader") {
|
|
349
|
+
const shaders = e.shaders ?? [];
|
|
350
|
+
if (shaders.length > 2) {
|
|
351
|
+
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.subBackend ?? _creator.backend}")}\``;
|
|
352
|
+
} else if (shaders.length > 0) {
|
|
353
|
+
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.backend}")}\``;
|
|
354
|
+
} else {
|
|
355
|
+
files[e.path] = `export default ""`;
|
|
356
|
+
}
|
|
357
|
+
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
358
|
+
files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
|
|
359
|
+
} else {
|
|
360
|
+
files[e.path] = e.text ?? "";
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const { code, map } = await bundleProjectWithMap(entry, files, opts.format ?? "esm", opts.minify, opts.editor, opts.libraries);
|
|
364
|
+
if (opts.header === false) {
|
|
365
|
+
return map ? `${code}
|
|
366
|
+
${inlineSourceMapComment(map)}` : code;
|
|
367
|
+
}
|
|
368
|
+
const header = buildHeader(opts.name, code, { icon: opts.icon });
|
|
369
|
+
if (!map)
|
|
370
|
+
return header + code;
|
|
371
|
+
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
372
|
+
return `${header}${code}
|
|
373
|
+
${inlineSourceMapComment(offsetSourceMap(map, headerLines))}`;
|
|
374
|
+
};
|
|
375
|
+
var init_compileProject = __esm(() => {
|
|
376
|
+
init_bundler();
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// ../sdk/src/compile/sceneEditor.ts
|
|
380
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
381
|
+
import { join as join10 } from "node:path";
|
|
382
|
+
var SCENE_HARNESS_DIR = "./.scene-editor", SCENE_ENTRY_PATH = "./.scene-entry.ts", loadSceneHarness = (fallbackDir) => {
|
|
383
|
+
const override = process.env.LECODES_SCENE_HARNESS_DIR;
|
|
384
|
+
const dir = override && existsSync8(override) ? override : fallbackDir && existsSync8(fallbackDir) ? fallbackDir : null;
|
|
385
|
+
if (!dir)
|
|
386
|
+
throw new Error("scene-editor harness sources not found (set LECODES_SCENE_HARNESS_DIR)");
|
|
387
|
+
const out = {};
|
|
388
|
+
for (const f of readdirSync5(dir)) {
|
|
389
|
+
if (f.endsWith(".ts"))
|
|
390
|
+
out[f] = readFileSync7(join10(dir, f), "utf8");
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}, sceneEditorEntries = (scenePath, harness, projectPaths = []) => {
|
|
394
|
+
const entries = Object.entries(harness).map(([name, text]) => ({
|
|
395
|
+
path: `${SCENE_HARNESS_DIR}/${name}`,
|
|
396
|
+
type: "text",
|
|
397
|
+
text
|
|
398
|
+
}));
|
|
399
|
+
entries.push({
|
|
400
|
+
path: `${SCENE_HARNESS_DIR}/.edit-mode.ts`,
|
|
401
|
+
type: "text",
|
|
402
|
+
text: `;(globalThis as any).__lecodesSceneEdit = true
|
|
403
|
+
export {}
|
|
404
|
+
`
|
|
405
|
+
});
|
|
406
|
+
const editorImports = projectPaths.filter((p) => p.endsWith(".editor.ts")).map((p) => `import "${p}"
|
|
407
|
+
`).join("");
|
|
408
|
+
entries.push({
|
|
409
|
+
path: SCENE_ENTRY_PATH,
|
|
410
|
+
type: "text",
|
|
411
|
+
text: `import "${SCENE_HARNESS_DIR}/.edit-mode.ts"
|
|
412
|
+
` + `import "${scenePath}"
|
|
413
|
+
` + editorImports + `import "${SCENE_HARNESS_DIR}/main.ts"
|
|
414
|
+
`
|
|
415
|
+
});
|
|
416
|
+
return entries;
|
|
417
|
+
};
|
|
418
|
+
var init_sceneEditor = () => {};
|
|
419
|
+
|
|
420
|
+
// ../sdk/src/compile/index.ts
|
|
421
|
+
var exports_compile = {};
|
|
422
|
+
__export(exports_compile, {
|
|
423
|
+
sceneEditorEntries: () => sceneEditorEntries,
|
|
424
|
+
offsetSourceMap: () => offsetSourceMap,
|
|
425
|
+
loadSceneHarness: () => loadSceneHarness,
|
|
426
|
+
inlineSourceMapComment: () => inlineSourceMapComment,
|
|
427
|
+
detectProjectKind: () => detectProjectKind,
|
|
428
|
+
compileProject: () => compileProject,
|
|
429
|
+
bundleProjectWithMap: () => bundleProjectWithMap,
|
|
430
|
+
bundleProject: () => bundleProject,
|
|
431
|
+
buildHeader: () => buildHeader,
|
|
432
|
+
SCENE_HARNESS_DIR: () => SCENE_HARNESS_DIR,
|
|
433
|
+
SCENE_ENTRY_PATH: () => SCENE_ENTRY_PATH
|
|
434
|
+
});
|
|
435
|
+
var init_compile = __esm(() => {
|
|
436
|
+
init_bundler();
|
|
437
|
+
init_compileProject();
|
|
438
|
+
init_sceneEditor();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMode.js
|
|
442
|
+
var require_QRMode = __commonJS((exports, module) => {
|
|
443
|
+
module.exports = {
|
|
444
|
+
MODE_NUMBER: 1 << 0,
|
|
445
|
+
MODE_ALPHA_NUM: 1 << 1,
|
|
446
|
+
MODE_8BIT_BYTE: 1 << 2,
|
|
447
|
+
MODE_KANJI: 1 << 3
|
|
448
|
+
};
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QR8bitByte.js
|
|
452
|
+
var require_QR8bitByte = __commonJS((exports, module) => {
|
|
453
|
+
var QRMode = require_QRMode();
|
|
454
|
+
function QR8bitByte(data) {
|
|
455
|
+
this.mode = QRMode.MODE_8BIT_BYTE;
|
|
456
|
+
this.data = data;
|
|
457
|
+
}
|
|
458
|
+
QR8bitByte.prototype = {
|
|
459
|
+
getLength: function() {
|
|
460
|
+
return this.data.length;
|
|
461
|
+
},
|
|
462
|
+
write: function(buffer) {
|
|
463
|
+
for (var i = 0;i < this.data.length; i++) {
|
|
464
|
+
buffer.put(this.data.charCodeAt(i), 8);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
module.exports = QR8bitByte;
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMath.js
|
|
472
|
+
var require_QRMath = __commonJS((exports, module) => {
|
|
473
|
+
var QRMath = {
|
|
474
|
+
glog: function(n) {
|
|
475
|
+
if (n < 1) {
|
|
476
|
+
throw new Error("glog(" + n + ")");
|
|
477
|
+
}
|
|
478
|
+
return QRMath.LOG_TABLE[n];
|
|
479
|
+
},
|
|
480
|
+
gexp: function(n) {
|
|
481
|
+
while (n < 0) {
|
|
482
|
+
n += 255;
|
|
483
|
+
}
|
|
484
|
+
while (n >= 256) {
|
|
485
|
+
n -= 255;
|
|
486
|
+
}
|
|
487
|
+
return QRMath.EXP_TABLE[n];
|
|
488
|
+
},
|
|
489
|
+
EXP_TABLE: new Array(256),
|
|
490
|
+
LOG_TABLE: new Array(256)
|
|
491
|
+
};
|
|
492
|
+
for (i = 0;i < 8; i++) {
|
|
493
|
+
QRMath.EXP_TABLE[i] = 1 << i;
|
|
494
|
+
}
|
|
495
|
+
var i;
|
|
496
|
+
for (i = 8;i < 256; i++) {
|
|
497
|
+
QRMath.EXP_TABLE[i] = QRMath.EXP_TABLE[i - 4] ^ QRMath.EXP_TABLE[i - 5] ^ QRMath.EXP_TABLE[i - 6] ^ QRMath.EXP_TABLE[i - 8];
|
|
498
|
+
}
|
|
499
|
+
var i;
|
|
500
|
+
for (i = 0;i < 255; i++) {
|
|
501
|
+
QRMath.LOG_TABLE[QRMath.EXP_TABLE[i]] = i;
|
|
502
|
+
}
|
|
503
|
+
var i;
|
|
504
|
+
module.exports = QRMath;
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRPolynomial.js
|
|
508
|
+
var require_QRPolynomial = __commonJS((exports, module) => {
|
|
509
|
+
var QRMath = require_QRMath();
|
|
510
|
+
function QRPolynomial(num, shift) {
|
|
511
|
+
if (num.length === undefined) {
|
|
512
|
+
throw new Error(num.length + "/" + shift);
|
|
513
|
+
}
|
|
514
|
+
var offset = 0;
|
|
515
|
+
while (offset < num.length && num[offset] === 0) {
|
|
516
|
+
offset++;
|
|
517
|
+
}
|
|
518
|
+
this.num = new Array(num.length - offset + shift);
|
|
519
|
+
for (var i = 0;i < num.length - offset; i++) {
|
|
520
|
+
this.num[i] = num[i + offset];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
QRPolynomial.prototype = {
|
|
524
|
+
get: function(index) {
|
|
525
|
+
return this.num[index];
|
|
526
|
+
},
|
|
527
|
+
getLength: function() {
|
|
528
|
+
return this.num.length;
|
|
529
|
+
},
|
|
530
|
+
multiply: function(e) {
|
|
531
|
+
var num = new Array(this.getLength() + e.getLength() - 1);
|
|
532
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
533
|
+
for (var j = 0;j < e.getLength(); j++) {
|
|
534
|
+
num[i + j] ^= QRMath.gexp(QRMath.glog(this.get(i)) + QRMath.glog(e.get(j)));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return new QRPolynomial(num, 0);
|
|
538
|
+
},
|
|
539
|
+
mod: function(e) {
|
|
540
|
+
if (this.getLength() - e.getLength() < 0) {
|
|
541
|
+
return this;
|
|
542
|
+
}
|
|
543
|
+
var ratio = QRMath.glog(this.get(0)) - QRMath.glog(e.get(0));
|
|
544
|
+
var num = new Array(this.getLength());
|
|
545
|
+
for (var i = 0;i < this.getLength(); i++) {
|
|
546
|
+
num[i] = this.get(i);
|
|
547
|
+
}
|
|
548
|
+
for (var x = 0;x < e.getLength(); x++) {
|
|
549
|
+
num[x] ^= QRMath.gexp(QRMath.glog(e.get(x)) + ratio);
|
|
550
|
+
}
|
|
551
|
+
return new QRPolynomial(num, 0).mod(e);
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
module.exports = QRPolynomial;
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// ../../node_modules/.bun/qrcode-terminal@0.12.0/node_modules/qrcode-terminal/vendor/QRCode/QRMaskPattern.js
|
|
558
|
+
var require_QRMaskPattern = __commonJS((exports, module) => {
|
|
559
|
+
module.exports = {
|
|
560
|
+
PATTERN000: 0,
|
|
561
|
+
PATTERN001: 1,
|
|
562
|
+
PATTERN010: 2,
|
|
563
|
+
PATTERN011: 3,
|
|
564
|
+
PATTERN100: 4,
|
|
565
|
+
PATTERN101: 5,
|
|
161
566
|
PATTERN110: 6,
|
|
162
567
|
PATTERN111: 7
|
|
163
568
|
};
|
|
@@ -1052,7 +1457,7 @@ var require_main = __commonJS((exports, module) => {
|
|
|
1052
1457
|
// package.json
|
|
1053
1458
|
var package_default = {
|
|
1054
1459
|
name: "lecodes-cli",
|
|
1055
|
-
version: "0.
|
|
1460
|
+
version: "0.7.1",
|
|
1056
1461
|
description: "Clone, edit and push LeCodes projects from your machine.",
|
|
1057
1462
|
type: "module",
|
|
1058
1463
|
license: "MIT",
|
|
@@ -1075,18 +1480,19 @@ var package_default = {
|
|
|
1075
1480
|
icon: "bun scripts/make-icon.ts",
|
|
1076
1481
|
"vendor-runtime": "bun scripts/vendor-runtime.ts",
|
|
1077
1482
|
"sync-peers": "bun scripts/sync-peer-versions.ts",
|
|
1078
|
-
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 --banner="#!/usr/bin/env node"',
|
|
1483
|
+
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 --banner="#!/usr/bin/env node"',
|
|
1079
1484
|
"build:bin": "bun scripts/build-bin.ts",
|
|
1080
1485
|
typecheck: "tsc --noEmit",
|
|
1081
1486
|
dev: "bun run src/index.ts",
|
|
1082
1487
|
prepublishOnly: "bun run sync-peers && bun run build"
|
|
1083
1488
|
},
|
|
1084
1489
|
dependencies: {
|
|
1085
|
-
"@letary/chisel": "^0.
|
|
1490
|
+
"@letary/chisel": "^0.4.1"
|
|
1086
1491
|
},
|
|
1087
1492
|
peerDependencies: {
|
|
1088
1493
|
"lecodes-design": "^0.3.1",
|
|
1089
|
-
"lecodes-renderer": "^0.
|
|
1494
|
+
"lecodes-renderer": "^0.6.1",
|
|
1495
|
+
"lecodes-3d-editor": "^0.1.0"
|
|
1090
1496
|
},
|
|
1091
1497
|
peerDependenciesMeta: {
|
|
1092
1498
|
"lecodes-design": {
|
|
@@ -1094,12 +1500,16 @@ var package_default = {
|
|
|
1094
1500
|
},
|
|
1095
1501
|
"lecodes-renderer": {
|
|
1096
1502
|
optional: true
|
|
1503
|
+
},
|
|
1504
|
+
"lecodes-3d-editor": {
|
|
1505
|
+
optional: true
|
|
1097
1506
|
}
|
|
1098
1507
|
},
|
|
1099
1508
|
devDependencies: {
|
|
1100
1509
|
"lecodes-design": "workspace:*",
|
|
1101
1510
|
"lecodes-renderer": "^0.4.1",
|
|
1102
1511
|
"qrcode-terminal": "^0.12.0",
|
|
1512
|
+
"lecodes-3d-editor": "workspace:*",
|
|
1103
1513
|
sdk: "workspace:*"
|
|
1104
1514
|
}
|
|
1105
1515
|
};
|
|
@@ -1593,441 +2003,159 @@ const scene = new Scene2D({ background: '#10131a' })
|
|
|
1593
2003
|
scene.open()
|
|
1594
2004
|
|
|
1595
2005
|
setLoop((dt) => {
|
|
1596
|
-
// your per-frame logic — dt is seconds since the previous frame
|
|
1597
|
-
})
|
|
1598
|
-
`;
|
|
1599
|
-
var create = async (args) => {
|
|
1600
|
-
const config = loadConfig();
|
|
1601
|
-
const apiUrl = requireApiUrl(config);
|
|
1602
|
-
const token = requireToken(config);
|
|
1603
|
-
const name = args._[0];
|
|
1604
|
-
if (!name)
|
|
1605
|
-
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
1606
|
-
const dir = args._[1] ?? slug2(name);
|
|
1607
|
-
const root = resolve2(dir);
|
|
1608
|
-
if (existsSync6(root) && readdirSync2(root).length > 0) {
|
|
1609
|
-
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
1610
|
-
}
|
|
1611
|
-
mkdirSync6(root, { recursive: true });
|
|
1612
|
-
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
1613
|
-
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
|
|
1614
|
-
info(c.dim("Fetching type definitions…"));
|
|
1615
|
-
await materializeTypes(root, apiUrl);
|
|
1616
|
-
writeTsconfig(root);
|
|
1617
|
-
writeDefaultIgnore(root);
|
|
1618
|
-
writeFileSync6(join7(root, "main.ts"), STARTER);
|
|
1619
|
-
const commits = await getCommits(apiUrl, token, uuid);
|
|
1620
|
-
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
|
|
1621
|
-
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
|
|
1622
|
-
info(c.dim(` cd ${dir} — edit main.ts, then \`lecodes push -m "first commit"\` to save it`));
|
|
1623
|
-
};
|
|
1624
|
-
|
|
1625
|
-
// src/commands/design.ts
|
|
1626
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync8, appendFileSync } from "node:fs";
|
|
1627
|
-
import { basename as basename2, join as join12 } from "node:path";
|
|
1628
|
-
|
|
1629
|
-
// src/compile/designCompile.ts
|
|
1630
|
-
import { readFileSync as readFileSync8 } from "node:fs";
|
|
1631
|
-
import { basename } from "node:path";
|
|
1632
|
-
|
|
1633
|
-
// src/localFiles.ts
|
|
1634
|
-
import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
1635
|
-
import { join as join8, relative } from "node:path";
|
|
1636
|
-
var SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
|
|
1637
|
-
var SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
|
|
1638
|
-
var TEXT_EXTS = new Set([
|
|
1639
|
-
"ts",
|
|
1640
|
-
"tsx",
|
|
1641
|
-
"js",
|
|
1642
|
-
"jsx",
|
|
1643
|
-
"mjs",
|
|
1644
|
-
"cjs",
|
|
1645
|
-
"json",
|
|
1646
|
-
"svg",
|
|
1647
|
-
"glsl",
|
|
1648
|
-
"vert",
|
|
1649
|
-
"frag",
|
|
1650
|
-
"txt",
|
|
1651
|
-
"md",
|
|
1652
|
-
"css",
|
|
1653
|
-
"html",
|
|
1654
|
-
"xml",
|
|
1655
|
-
"yml",
|
|
1656
|
-
"yaml",
|
|
1657
|
-
"csv"
|
|
1658
|
-
]);
|
|
1659
|
-
var ext = (name) => {
|
|
1660
|
-
const i = name.lastIndexOf(".");
|
|
1661
|
-
return i < 0 ? "" : name.slice(i + 1).toLowerCase();
|
|
1662
|
-
};
|
|
1663
|
-
var classifyKind = (name) => {
|
|
1664
|
-
const e = ext(name);
|
|
1665
|
-
if (e === "mat")
|
|
1666
|
-
return "shader";
|
|
1667
|
-
if (e === "scene")
|
|
1668
|
-
return "scene";
|
|
1669
|
-
return TEXT_EXTS.has(e) ? "text" : "resource";
|
|
1670
|
-
};
|
|
1671
|
-
var isTextKind = (kind) => kind !== "resource";
|
|
1672
|
-
var scanLocal = (root, opts = {}) => {
|
|
1673
|
-
const { ignore, tracked } = opts;
|
|
1674
|
-
const files = [];
|
|
1675
|
-
const dirs = [];
|
|
1676
|
-
const ignored = [];
|
|
1677
|
-
const isTracked = (key) => tracked?.has(key) ?? false;
|
|
1678
|
-
const hasTrackedUnder = (key) => {
|
|
1679
|
-
if (!tracked)
|
|
1680
|
-
return false;
|
|
1681
|
-
const prefix = key + "/";
|
|
1682
|
-
for (const t of tracked)
|
|
1683
|
-
if (t.startsWith(prefix))
|
|
1684
|
-
return true;
|
|
1685
|
-
return false;
|
|
1686
|
-
};
|
|
1687
|
-
const walk = (dir, parentIgnored) => {
|
|
1688
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
1689
|
-
const abs = join8(dir, entry.name);
|
|
1690
|
-
const key = toKey(relative(root, abs));
|
|
1691
|
-
if (entry.isDirectory()) {
|
|
1692
|
-
if (SKIP_DIRS.has(entry.name))
|
|
1693
|
-
continue;
|
|
1694
|
-
const dirIgnored = parentIgnored || !!ignore && ignore(key, true);
|
|
1695
|
-
if (dirIgnored && !isTracked(key) && !hasTrackedUnder(key)) {
|
|
1696
|
-
ignored.push(key + "/");
|
|
1697
|
-
continue;
|
|
1698
|
-
}
|
|
1699
|
-
dirs.push(key);
|
|
1700
|
-
walk(abs, dirIgnored);
|
|
1701
|
-
} else if (entry.isFile()) {
|
|
1702
|
-
if (SKIP_FILES.has(entry.name))
|
|
1703
|
-
continue;
|
|
1704
|
-
const fileIgnored = parentIgnored || !!ignore && ignore(key, false);
|
|
1705
|
-
if (fileIgnored && !isTracked(key)) {
|
|
1706
|
-
ignored.push(key);
|
|
1707
|
-
continue;
|
|
1708
|
-
}
|
|
1709
|
-
files.push({ path: key, absPath: abs, sha: sha256(readFileSync5(abs)) });
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
};
|
|
1713
|
-
walk(root, false);
|
|
1714
|
-
return { files, dirs, ignored };
|
|
1715
|
-
};
|
|
1716
|
-
var scanProject = (root, manifest) => scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest.files)) });
|
|
1717
|
-
var diffLocal = (manifest, scan) => {
|
|
1718
|
-
const added = [];
|
|
1719
|
-
const modified = [];
|
|
1720
|
-
const localByPath = new Map(scan.files.map((f) => [f.path, f]));
|
|
1721
|
-
for (const file of scan.files) {
|
|
1722
|
-
const known = manifest.files[file.path];
|
|
1723
|
-
if (!known)
|
|
1724
|
-
added.push(file);
|
|
1725
|
-
else if (known.sha !== file.sha)
|
|
1726
|
-
modified.push(file);
|
|
1727
|
-
}
|
|
1728
|
-
const deleted = Object.keys(manifest.files).filter((path) => !localByPath.has(path));
|
|
1729
|
-
const moves = new Map;
|
|
1730
|
-
const claimed = new Set;
|
|
1731
|
-
for (const file of added) {
|
|
1732
|
-
const from = deleted.find((path) => !claimed.has(path) && manifest.files[path].sha === file.sha);
|
|
1733
|
-
if (from) {
|
|
1734
|
-
moves.set(file.path, from);
|
|
1735
|
-
claimed.add(from);
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
return { added, modified, deleted, moves };
|
|
1739
|
-
};
|
|
1740
|
-
var hasChanges = (diff) => diff.added.length + diff.modified.length + diff.deleted.length > 0;
|
|
1741
|
-
|
|
1742
|
-
// src/compile/projectCompile.ts
|
|
1743
|
-
import { existsSync as existsSync8 } from "node:fs";
|
|
1744
|
-
import { createRequire as createRequire3 } from "node:module";
|
|
1745
|
-
import { dirname as dirname6, join as join10 } from "node:path";
|
|
1746
|
-
// ../sdk/src/compile/bundler.ts
|
|
1747
|
-
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync6, statSync } from "node:fs";
|
|
1748
|
-
import { spawnSync } from "node:child_process";
|
|
1749
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
1750
|
-
import { dirname as dirname5, join as join9, relative as relative2 } from "node:path";
|
|
1751
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1752
|
-
|
|
1753
|
-
// ../sdk/src/compile/assetMacro.ts
|
|
1754
|
-
var ASSET_RE = /\basset\(\s*(["'])((?:[^"'\\]|\\.)*)\1\s*\)/g;
|
|
1755
|
-
var hasAssetMacro = (src) => src.includes("asset(");
|
|
1756
|
-
var desugarAssetMacros = (src, filePath) => {
|
|
1757
|
-
const byPath = new Map;
|
|
1758
|
-
let i = 0;
|
|
1759
|
-
const out = src.replace(ASSET_RE, (_match, _quote, path) => {
|
|
1760
|
-
let name = byPath.get(path);
|
|
1761
|
-
if (!name) {
|
|
1762
|
-
name = `__asset_${i++}`;
|
|
1763
|
-
byPath.set(path, name);
|
|
1764
|
-
}
|
|
1765
|
-
return name;
|
|
1766
|
-
});
|
|
1767
|
-
if (/\basset\(/.test(out)) {
|
|
1768
|
-
throw new Error(`asset() expects a string literal, found a dynamic argument in: ${filePath}`);
|
|
1769
|
-
}
|
|
1770
|
-
if (byPath.size === 0)
|
|
1771
|
-
return src;
|
|
1772
|
-
const imports = [...byPath].map(([path, name]) => `import ${name} from ${JSON.stringify(path)};`).join(`
|
|
1773
|
-
`);
|
|
1774
|
-
return out + `
|
|
1775
|
-
` + imports;
|
|
1776
|
-
};
|
|
1777
|
-
|
|
1778
|
-
// ../sdk/src/compile/aspectMacro.ts
|
|
1779
|
-
var ASPECT_RE = /\bclass\s+\w+\s+extends\s+Aspect\s*<\s*(["'])((?:[^"'\\]|\\.)*)\1[^{};]*>\s*\{/g;
|
|
1780
|
-
var hasAspectExtends = (src) => /\bextends\s+Aspect\s*</.test(src);
|
|
1781
|
-
var injectAspectNames = (src) => {
|
|
1782
|
-
let out = "";
|
|
1783
|
-
let last = 0;
|
|
1784
|
-
ASPECT_RE.lastIndex = 0;
|
|
1785
|
-
let m;
|
|
1786
|
-
while ((m = ASPECT_RE.exec(src)) !== null) {
|
|
1787
|
-
const braceEnd = m.index + m[0].length;
|
|
1788
|
-
if (!classBodyDeclaresAspect(src, braceEnd)) {
|
|
1789
|
-
out += src.slice(last, braceEnd) + ` static aspect = ${JSON.stringify(m[2])};`;
|
|
1790
|
-
last = braceEnd;
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
return last === 0 ? src : out + src.slice(last);
|
|
1794
|
-
};
|
|
1795
|
-
var classBodyDeclaresAspect = (src, from) => {
|
|
1796
|
-
let depth = 1;
|
|
1797
|
-
let i = from;
|
|
1798
|
-
for (;i < src.length && depth > 0; i++) {
|
|
1799
|
-
const c2 = src[i];
|
|
1800
|
-
if (c2 === "{")
|
|
1801
|
-
depth++;
|
|
1802
|
-
else if (c2 === "}")
|
|
1803
|
-
depth--;
|
|
1804
|
-
}
|
|
1805
|
-
return /\bstatic\b[^;{}()=]*\baspect\b/.test(src.slice(from, i));
|
|
1806
|
-
};
|
|
1807
|
-
|
|
1808
|
-
// ../sdk/src/compile/bundler.ts
|
|
1809
|
-
var resolveInject = () => {
|
|
1810
|
-
const override = process.env.LECODES_SDK_INJECT;
|
|
1811
|
-
if (override && existsSync7(override))
|
|
1812
|
-
return override;
|
|
1813
|
-
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
1814
|
-
const src = join9(here, "..", "inject.ts");
|
|
1815
|
-
if (existsSync7(src))
|
|
1816
|
-
return src;
|
|
1817
|
-
throw new Error("sdk: could not locate the inject source (LECODES_SDK_INJECT / src/inject.ts)");
|
|
1818
|
-
};
|
|
1819
|
-
var normalize = (path) => {
|
|
1820
|
-
const parts = [];
|
|
1821
|
-
for (const part of path.split("/")) {
|
|
1822
|
-
if (!part || part === ".")
|
|
1823
|
-
continue;
|
|
1824
|
-
if (part === "..")
|
|
1825
|
-
parts.pop();
|
|
1826
|
-
else
|
|
1827
|
-
parts.push(part);
|
|
1828
|
-
}
|
|
1829
|
-
return "/" + parts.join("/");
|
|
1830
|
-
};
|
|
1831
|
-
var CHISEL_DEFINE = { RAD2DEG: "57.29577951308232", DEG2RAD: "0.017453292519943295" };
|
|
1832
|
-
var CHISEL_KEEP = ["_*"];
|
|
1833
|
-
var resolveSdkSrc = () => {
|
|
1834
|
-
const inject = resolveInject();
|
|
1835
|
-
if (!inject.endsWith("inject.ts")) {
|
|
1836
|
-
throw new Error(`chisel needs the SDK source tree (src/inject.ts); got ${inject}. Set LECODES_SDK_INJECT to the source.`);
|
|
1837
|
-
}
|
|
1838
|
-
return dirname5(inject);
|
|
1839
|
-
};
|
|
1840
|
-
var isSdkSource = (path) => /\.[tj]s$/.test(path) && !path.endsWith(".d.ts") && !path.includes("/compile/") && !path.includes("/tests/") && !path.includes("/__tests__/");
|
|
1841
|
-
var sdkFilesCache = new Map;
|
|
1842
|
-
var collectSdkFiles = (sdkSrc) => {
|
|
1843
|
-
const cached = sdkFilesCache.get(sdkSrc);
|
|
1844
|
-
if (cached)
|
|
1845
|
-
return cached;
|
|
1846
|
-
const out = {};
|
|
1847
|
-
const walk = (dir) => {
|
|
1848
|
-
for (const name of readdirSync4(dir)) {
|
|
1849
|
-
const p = join9(dir, name);
|
|
1850
|
-
if (statSync(p).isDirectory())
|
|
1851
|
-
walk(p);
|
|
1852
|
-
else if (isSdkSource(p))
|
|
1853
|
-
out["/__sdk/" + relative2(sdkSrc, p).split("\\").join("/")] = readFileSync6(p, "utf8");
|
|
1854
|
-
}
|
|
1855
|
-
};
|
|
1856
|
-
walk(sdkSrc);
|
|
1857
|
-
sdkFilesCache.set(sdkSrc, out);
|
|
1858
|
-
return out;
|
|
1859
|
-
};
|
|
1860
|
-
var resolveChiselBin = () => {
|
|
1861
|
-
const override = process.env.LECODES_CHISEL_BIN;
|
|
1862
|
-
if (override) {
|
|
1863
|
-
if (existsSync7(override))
|
|
1864
|
-
return override;
|
|
1865
|
-
throw new Error(`LECODES_CHISEL_BIN points at a missing file: ${override}`);
|
|
1866
|
-
}
|
|
1867
|
-
try {
|
|
1868
|
-
const bin = createRequire2(import.meta.url)("@letary/chisel").binaryPath();
|
|
1869
|
-
if (bin && existsSync7(bin))
|
|
1870
|
-
return bin;
|
|
1871
|
-
} catch {}
|
|
1872
|
-
throw new Error("chisel binary not found. Install @letary/chisel, or set LECODES_CHISEL_BIN.");
|
|
1873
|
-
};
|
|
1874
|
-
var bundleProjectChisel = async (entrypoint, files, format, sourcemap) => {
|
|
1875
|
-
const entry = normalize(entrypoint);
|
|
1876
|
-
const projFiles = {};
|
|
1877
|
-
for (const [k, v] of Object.entries(files)) {
|
|
1878
|
-
const path = normalize(k);
|
|
1879
|
-
let src = v;
|
|
1880
|
-
if (/\.[tj]sx?$/.test(path)) {
|
|
1881
|
-
if (hasAssetMacro(src))
|
|
1882
|
-
src = desugarAssetMacros(src, path);
|
|
1883
|
-
if (hasAspectExtends(src))
|
|
1884
|
-
src = injectAspectNames(src);
|
|
1885
|
-
}
|
|
1886
|
-
projFiles[path] = src;
|
|
1887
|
-
}
|
|
1888
|
-
if (!projFiles[entry])
|
|
1889
|
-
throw new Error(`Entrypoint not found: ${entry}`);
|
|
1890
|
-
const sdkFiles = collectSdkFiles(resolveSdkSrc());
|
|
1891
|
-
const minify = process.env.LECODES_CHISEL_MINIFY !== "0";
|
|
1892
|
-
const input = JSON.stringify({
|
|
1893
|
-
files: { ...sdkFiles, ...projFiles },
|
|
1894
|
-
entry,
|
|
1895
|
-
inject: ["/__sdk/inject.ts"],
|
|
1896
|
-
format,
|
|
1897
|
-
minify,
|
|
1898
|
-
fuse: process.env.LECODES_CHISEL_FUSE === "1",
|
|
1899
|
-
define: CHISEL_DEFINE,
|
|
1900
|
-
keep: CHISEL_KEEP,
|
|
1901
|
-
sourcemap,
|
|
1902
|
-
reactive_ui: process.env.LECODES_CHISEL_REACTIVE_UI !== "0"
|
|
1903
|
-
});
|
|
1904
|
-
const bin = resolveChiselBin();
|
|
1905
|
-
const r = spawnSync(bin, [], { input, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
|
|
1906
|
-
if (r.status === 2 || r.error) {
|
|
1907
|
-
throw new Error(`chisel failed: ${r.error?.message ?? r.stderr ?? "unknown error"}`);
|
|
1908
|
-
}
|
|
1909
|
-
let parsed;
|
|
1910
|
-
try {
|
|
1911
|
-
parsed = JSON.parse(r.stdout || "{}");
|
|
1912
|
-
} catch {
|
|
1913
|
-
throw new Error(`chisel produced invalid output: ${(r.stdout || r.stderr || "").slice(0, 200)}`);
|
|
1914
|
-
}
|
|
1915
|
-
if (parsed.error)
|
|
1916
|
-
throw new Error(`chisel: ${parsed.error}`);
|
|
1917
|
-
if (parsed.code == null)
|
|
1918
|
-
throw new Error("chisel produced no code");
|
|
1919
|
-
return { code: parsed.code, map: parsed.map ?? null };
|
|
1920
|
-
};
|
|
1921
|
-
var bundleProjectWithMap = async (entrypoint, files, format = "esm") => bundleProjectChisel(entrypoint, files, format, true);
|
|
1922
|
-
|
|
1923
|
-
// ../sdk/src/compile/header.ts
|
|
1924
|
-
var buildHeader = (name, code, opts = {}) => {
|
|
1925
|
-
let header = `// name: ${name.slice(0, 20)}
|
|
1926
|
-
`;
|
|
1927
|
-
if (opts.icon)
|
|
1928
|
-
header += `// icon: ${opts.icon}
|
|
1929
|
-
`;
|
|
1930
|
-
header += `// last-updated: ${new Date().toISOString()}
|
|
1931
|
-
`;
|
|
1932
|
-
const localAssets = new Set;
|
|
1933
|
-
for (const m of code.matchAll(/_creatorUtils\.fetchLocal\((.+?)\)/g)) {
|
|
1934
|
-
localAssets.add(m[1].replaceAll(/['"]/g, "").trim());
|
|
1935
|
-
}
|
|
1936
|
-
if (/_creator\.createGlb/.test(code))
|
|
1937
|
-
localAssets.add("uberarchive");
|
|
1938
|
-
if (/_creator\.captureImage/.test(code))
|
|
1939
|
-
localAssets.add("capture");
|
|
1940
|
-
if (localAssets.size > 0)
|
|
1941
|
-
header += `// preload: ${[...localAssets].join(";")}
|
|
1942
|
-
`;
|
|
1943
|
-
if (!code.includes("_creator.createScene") && !code.includes("_creator.createEntity"))
|
|
1944
|
-
header += `// gl: disable
|
|
1945
|
-
`;
|
|
1946
|
-
else if (code.includes("_creator.createEngine"))
|
|
1947
|
-
header += `// gl: defer
|
|
1948
|
-
`;
|
|
1949
|
-
if (code.includes("_creator2d."))
|
|
1950
|
-
header += `// 2d: enable
|
|
1951
|
-
`;
|
|
1952
|
-
if (code.includes("_creator.createARController"))
|
|
1953
|
-
header += "// ar: enable";
|
|
1954
|
-
header += `
|
|
2006
|
+
// your per-frame logic — dt is seconds since the previous frame
|
|
2007
|
+
})
|
|
1955
2008
|
`;
|
|
1956
|
-
|
|
2009
|
+
var create = async (args) => {
|
|
2010
|
+
const config = loadConfig();
|
|
2011
|
+
const apiUrl = requireApiUrl(config);
|
|
2012
|
+
const token = requireToken(config);
|
|
2013
|
+
const name = args._[0];
|
|
2014
|
+
if (!name)
|
|
2015
|
+
throw new CliError("Usage: lecodes create <name> [directory]");
|
|
2016
|
+
const dir = args._[1] ?? slug2(name);
|
|
2017
|
+
const root = resolve2(dir);
|
|
2018
|
+
if (existsSync6(root) && readdirSync2(root).length > 0) {
|
|
2019
|
+
throw new CliError(`Directory "${dir}" already exists and is not empty.`);
|
|
2020
|
+
}
|
|
2021
|
+
mkdirSync6(root, { recursive: true });
|
|
2022
|
+
info(c.dim(`Creating project ${c.bold(name)}…`));
|
|
2023
|
+
const { uuid } = await createProject(apiUrl, token, name, flagStr(args, "team"));
|
|
2024
|
+
info(c.dim("Fetching type definitions…"));
|
|
2025
|
+
await materializeTypes(root, apiUrl);
|
|
2026
|
+
writeTsconfig(root);
|
|
2027
|
+
writeDefaultIgnore(root);
|
|
2028
|
+
writeFileSync6(join7(root, "main.ts"), STARTER);
|
|
2029
|
+
const commits = await getCommits(apiUrl, token, uuid);
|
|
2030
|
+
writeManifest(root, { uuid, apiUrl, name, baseHeadId: commits.headId, files: {} });
|
|
2031
|
+
success(`Created ${c.bold(name)} in ${c.bold(dir)}/`);
|
|
2032
|
+
info(c.dim(` cd ${dir} — edit main.ts, then \`lecodes push -m "first commit"\` to save it`));
|
|
1957
2033
|
};
|
|
1958
2034
|
|
|
1959
|
-
//
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2035
|
+
// src/commands/design.ts
|
|
2036
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync10, readdirSync as readdirSync6, writeFileSync as writeFileSync8, appendFileSync } from "node:fs";
|
|
2037
|
+
import { basename as basename2, join as join13 } from "node:path";
|
|
2038
|
+
|
|
2039
|
+
// src/compile/designCompile.ts
|
|
2040
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
2041
|
+
import { basename } from "node:path";
|
|
2042
|
+
|
|
2043
|
+
// src/localFiles.ts
|
|
2044
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
|
|
2045
|
+
import { join as join8, relative } from "node:path";
|
|
2046
|
+
var SKIP_DIRS = new Set([".lecodes", ".git", "node_modules"]);
|
|
2047
|
+
var SKIP_FILES = new Set(["tsconfig.json", "jsconfig.json", ".gitignore", ".lecodesignore", ".DS_Store"]);
|
|
2048
|
+
var TEXT_EXTS = new Set([
|
|
2049
|
+
"ts",
|
|
2050
|
+
"tsx",
|
|
2051
|
+
"js",
|
|
2052
|
+
"jsx",
|
|
2053
|
+
"mjs",
|
|
2054
|
+
"cjs",
|
|
2055
|
+
"json",
|
|
2056
|
+
"svg",
|
|
2057
|
+
"glsl",
|
|
2058
|
+
"vert",
|
|
2059
|
+
"frag",
|
|
2060
|
+
"txt",
|
|
2061
|
+
"md",
|
|
2062
|
+
"css",
|
|
2063
|
+
"html",
|
|
2064
|
+
"xml",
|
|
2065
|
+
"yml",
|
|
2066
|
+
"yaml",
|
|
2067
|
+
"csv"
|
|
2068
|
+
]);
|
|
2069
|
+
var ext = (name) => {
|
|
2070
|
+
const i = name.lastIndexOf(".");
|
|
2071
|
+
return i < 0 ? "" : name.slice(i + 1).toLowerCase();
|
|
1966
2072
|
};
|
|
1967
|
-
var
|
|
1968
|
-
const
|
|
1969
|
-
|
|
2073
|
+
var classifyKind = (name) => {
|
|
2074
|
+
const e = ext(name);
|
|
2075
|
+
if (e === "mat")
|
|
2076
|
+
return "shader";
|
|
2077
|
+
if (e === "scene")
|
|
2078
|
+
return "scene";
|
|
2079
|
+
return TEXT_EXTS.has(e) ? "text" : "resource";
|
|
1970
2080
|
};
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
2081
|
+
var isTextKind = (kind) => kind !== "resource";
|
|
2082
|
+
var scanLocal = (root, opts = {}) => {
|
|
2083
|
+
const { ignore, tracked } = opts;
|
|
2084
|
+
const files = [];
|
|
2085
|
+
const dirs = [];
|
|
2086
|
+
const ignored = [];
|
|
2087
|
+
const isTracked = (key) => tracked?.has(key) ?? false;
|
|
2088
|
+
const hasTrackedUnder = (key) => {
|
|
2089
|
+
if (!tracked)
|
|
2090
|
+
return false;
|
|
2091
|
+
const prefix = key + "/";
|
|
2092
|
+
for (const t of tracked)
|
|
2093
|
+
if (t.startsWith(prefix))
|
|
2094
|
+
return true;
|
|
2095
|
+
return false;
|
|
2096
|
+
};
|
|
2097
|
+
const walk = (dir, parentIgnored) => {
|
|
2098
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
2099
|
+
const abs = join8(dir, entry.name);
|
|
2100
|
+
const key = toKey(relative(root, abs));
|
|
2101
|
+
if (entry.isDirectory()) {
|
|
2102
|
+
if (SKIP_DIRS.has(entry.name))
|
|
2103
|
+
continue;
|
|
2104
|
+
const dirIgnored = parentIgnored || !!ignore && ignore(key, true);
|
|
2105
|
+
if (dirIgnored && !isTracked(key) && !hasTrackedUnder(key)) {
|
|
2106
|
+
ignored.push(key + "/");
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
dirs.push(key);
|
|
2110
|
+
walk(abs, dirIgnored);
|
|
2111
|
+
} else if (entry.isFile()) {
|
|
2112
|
+
if (SKIP_FILES.has(entry.name))
|
|
2113
|
+
continue;
|
|
2114
|
+
const fileIgnored = parentIgnored || !!ignore && ignore(key, false);
|
|
2115
|
+
if (fileIgnored && !isTracked(key)) {
|
|
2116
|
+
ignored.push(key);
|
|
2117
|
+
continue;
|
|
2118
|
+
}
|
|
2119
|
+
files.push({ path: key, absPath: abs, sha: sha256(readFileSync5(abs)) });
|
|
1982
2120
|
}
|
|
1983
2121
|
}
|
|
1984
|
-
}
|
|
1985
|
-
|
|
2122
|
+
};
|
|
2123
|
+
walk(root, false);
|
|
2124
|
+
return { files, dirs, ignored };
|
|
1986
2125
|
};
|
|
1987
|
-
var
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
2126
|
+
var scanProject = (root, manifest) => scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(manifest.files)) });
|
|
2127
|
+
var diffLocal = (manifest, scan) => {
|
|
2128
|
+
const added = [];
|
|
2129
|
+
const modified = [];
|
|
2130
|
+
const localByPath = new Map(scan.files.map((f) => [f.path, f]));
|
|
2131
|
+
for (const file of scan.files) {
|
|
2132
|
+
const known = manifest.files[file.path];
|
|
2133
|
+
if (!known)
|
|
2134
|
+
added.push(file);
|
|
2135
|
+
else if (known.sha !== file.sha)
|
|
2136
|
+
modified.push(file);
|
|
1991
2137
|
}
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
}
|
|
2001
|
-
} else if (e.type === "shader") {
|
|
2002
|
-
const shaders = e.shaders ?? [];
|
|
2003
|
-
if (shaders.length > 2) {
|
|
2004
|
-
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.subBackend ?? _creator.backend}")}\``;
|
|
2005
|
-
} else if (shaders.length > 0) {
|
|
2006
|
-
files[e.path] = `export default \`${opts.publicUrl}${shaders[0].src.replace("_opengl", "_${_creator.backend}")}\``;
|
|
2007
|
-
} else {
|
|
2008
|
-
files[e.path] = `export default ""`;
|
|
2009
|
-
}
|
|
2010
|
-
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
2011
|
-
files[e.path] = `export default SvgSource(\`${e.text ?? ""}\`)`;
|
|
2012
|
-
} else {
|
|
2013
|
-
files[e.path] = e.text ?? "";
|
|
2138
|
+
const deleted = Object.keys(manifest.files).filter((path) => !localByPath.has(path));
|
|
2139
|
+
const moves = new Map;
|
|
2140
|
+
const claimed = new Set;
|
|
2141
|
+
for (const file of added) {
|
|
2142
|
+
const from = deleted.find((path) => !claimed.has(path) && manifest.files[path].sha === file.sha);
|
|
2143
|
+
if (from) {
|
|
2144
|
+
moves.set(file.path, from);
|
|
2145
|
+
claimed.add(from);
|
|
2014
2146
|
}
|
|
2015
2147
|
}
|
|
2016
|
-
|
|
2017
|
-
if (opts.header === false) {
|
|
2018
|
-
return map ? `${code}
|
|
2019
|
-
${inlineSourceMapComment(map)}` : code;
|
|
2020
|
-
}
|
|
2021
|
-
const header = buildHeader(opts.name, code, { icon: opts.icon });
|
|
2022
|
-
if (!map)
|
|
2023
|
-
return header + code;
|
|
2024
|
-
const headerLines = (header.match(/\n/g) ?? []).length;
|
|
2025
|
-
return `${header}${code}
|
|
2026
|
-
${inlineSourceMapComment(offsetSourceMap(map, headerLines))}`;
|
|
2148
|
+
return { added, modified, deleted, moves };
|
|
2027
2149
|
};
|
|
2150
|
+
var hasChanges = (diff) => diff.added.length + diff.modified.length + diff.deleted.length > 0;
|
|
2151
|
+
|
|
2028
2152
|
// src/compile/projectCompile.ts
|
|
2029
|
-
|
|
2030
|
-
|
|
2153
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
2154
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
2155
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
2156
|
+
init_compile();
|
|
2157
|
+
var vendoredInject = join11(distRoot, "runtime", "sdk", "inject.ts");
|
|
2158
|
+
if (existsSync9(vendoredInject))
|
|
2031
2159
|
process.env.LECODES_SDK_INJECT = vendoredInject;
|
|
2032
2160
|
var resolveChiselBin2 = () => {
|
|
2033
2161
|
if (process.env.LECODES_CHISEL_BIN)
|
|
@@ -2037,8 +2165,8 @@ var resolveChiselBin2 = () => {
|
|
|
2037
2165
|
if (fromPkg)
|
|
2038
2166
|
return fromPkg;
|
|
2039
2167
|
} catch {}
|
|
2040
|
-
const local =
|
|
2041
|
-
return
|
|
2168
|
+
const local = join11(dirname6(process.execPath), process.platform === "win32" ? "chisel.exe" : "chisel");
|
|
2169
|
+
return existsSync9(local) ? local : null;
|
|
2042
2170
|
};
|
|
2043
2171
|
var chiselBin = resolveChiselBin2();
|
|
2044
2172
|
if (chiselBin)
|
|
@@ -2050,13 +2178,15 @@ var compileProject2 = (opts) => compileProject({
|
|
|
2050
2178
|
entryOverride: opts.entryOverride,
|
|
2051
2179
|
header: opts.header,
|
|
2052
2180
|
format: opts.format,
|
|
2053
|
-
localAssets: opts.localAssets
|
|
2181
|
+
localAssets: opts.localAssets,
|
|
2182
|
+
minify: opts.minify,
|
|
2183
|
+
editor: opts.editor
|
|
2054
2184
|
});
|
|
2055
2185
|
|
|
2056
2186
|
// src/compile/assetIcons.ts
|
|
2057
2187
|
import { createHash as createHash2 } from "node:crypto";
|
|
2058
|
-
import { existsSync as
|
|
2059
|
-
import { dirname as dirname7, join as
|
|
2188
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
|
|
2189
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
2060
2190
|
var DEFAULT_REGISTRY = process.env.LECODES_ICON_REGISTRY?.trim() || "https://icon-registry.jt3.ru/v1";
|
|
2061
2191
|
var ICON_ID = /^([a-z0-9]+(?:-[a-z0-9]+)*):([a-z0-9]+(?:-[a-z0-9]+)*)$/;
|
|
2062
2192
|
var isIdentStart = (c2) => c2 >= "A" && c2 <= "Z" || c2 >= "a" && c2 <= "z" || c2 === "_" || c2 === "$";
|
|
@@ -2308,14 +2438,14 @@ var readText = async (registry, rel) => {
|
|
|
2308
2438
|
return res.ok ? await res.text() : null;
|
|
2309
2439
|
}
|
|
2310
2440
|
try {
|
|
2311
|
-
return
|
|
2441
|
+
return readFileSync8(join12(registry, rel), "utf8");
|
|
2312
2442
|
} catch {
|
|
2313
2443
|
return null;
|
|
2314
2444
|
}
|
|
2315
2445
|
};
|
|
2316
2446
|
var readLock = (path) => {
|
|
2317
2447
|
try {
|
|
2318
|
-
const lock = JSON.parse(
|
|
2448
|
+
const lock = JSON.parse(readFileSync8(path, "utf8"));
|
|
2319
2449
|
if (lock && typeof lock === "object" && lock.icons && lock.packs)
|
|
2320
2450
|
return lock;
|
|
2321
2451
|
} catch {}
|
|
@@ -2352,7 +2482,7 @@ var renderNotices = (lock) => {
|
|
|
2352
2482
|
`);
|
|
2353
2483
|
};
|
|
2354
2484
|
var writeIfChanged = (path, text) => {
|
|
2355
|
-
const current =
|
|
2485
|
+
const current = existsSync10(path) ? safeRead(path) : null;
|
|
2356
2486
|
if (current === text)
|
|
2357
2487
|
return false;
|
|
2358
2488
|
writeFileSync7(path, text);
|
|
@@ -2360,20 +2490,20 @@ var writeIfChanged = (path, text) => {
|
|
|
2360
2490
|
};
|
|
2361
2491
|
var safeRead = (path) => {
|
|
2362
2492
|
try {
|
|
2363
|
-
return
|
|
2493
|
+
return readFileSync8(path, "utf8");
|
|
2364
2494
|
} catch {
|
|
2365
2495
|
return null;
|
|
2366
2496
|
}
|
|
2367
2497
|
};
|
|
2368
2498
|
var vendorIcons = async (designDir, ids, opts = {}) => {
|
|
2369
2499
|
const registry = (opts.registry ?? DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
2370
|
-
const lockPath =
|
|
2500
|
+
const lockPath = join12(designDir, "icons.lock.json");
|
|
2371
2501
|
const lock = readLock(lockPath) ?? { registry, icons: {}, packs: {} };
|
|
2372
2502
|
const svgById = new Map;
|
|
2373
2503
|
const missing = [];
|
|
2374
2504
|
for (const id of ids) {
|
|
2375
2505
|
const li = lock.icons[id];
|
|
2376
|
-
const cached = li && safeRead(
|
|
2506
|
+
const cached = li && safeRead(join12(designDir, li.path));
|
|
2377
2507
|
if (cached)
|
|
2378
2508
|
svgById.set(id, cached);
|
|
2379
2509
|
else
|
|
@@ -2394,7 +2524,7 @@ var vendorIcons = async (designDir, ids, opts = {}) => {
|
|
|
2394
2524
|
if (svg === null)
|
|
2395
2525
|
throw new Error(`assetIcon: icon "${id}" not found in the registry. Check the name at ${registryHome(registry)}.`);
|
|
2396
2526
|
const rel = `assets/icons/${pack}/${name}.svg`;
|
|
2397
|
-
const abs =
|
|
2527
|
+
const abs = join12(designDir, rel);
|
|
2398
2528
|
mkdirSync7(dirname7(abs), { recursive: true });
|
|
2399
2529
|
writeFileSync7(abs, svg);
|
|
2400
2530
|
lock.icons[id] = { pack, name, hash: sha12(svg), spdx: lic.spdx, path: rel };
|
|
@@ -2405,7 +2535,7 @@ var vendorIcons = async (designDir, ids, opts = {}) => {
|
|
|
2405
2535
|
writeIfChanged(lockPath, serializeLock(lock));
|
|
2406
2536
|
}
|
|
2407
2537
|
if (Object.keys(lock.icons).length)
|
|
2408
|
-
writeIfChanged(
|
|
2538
|
+
writeIfChanged(join12(designDir, "ICON-NOTICES.md"), renderNotices(lock));
|
|
2409
2539
|
return svgById;
|
|
2410
2540
|
};
|
|
2411
2541
|
var applyAssetIcons = async (entries, designDir, opts = {}) => {
|
|
@@ -2440,7 +2570,7 @@ var compileDesignScreen = async (opts) => {
|
|
|
2440
2570
|
} else if (type === "shader") {
|
|
2441
2571
|
entries.push({ path: file.path, type, shaders: [] });
|
|
2442
2572
|
} else {
|
|
2443
|
-
entries.push({ path: file.path, type, text:
|
|
2573
|
+
entries.push({ path: file.path, type, text: readFileSync9(file.absPath, "utf8") });
|
|
2444
2574
|
}
|
|
2445
2575
|
}
|
|
2446
2576
|
const invoke = state !== undefined ? `screen(${JSON.stringify(state)})` : "screen()";
|
|
@@ -2849,31 +2979,31 @@ var getContext = (args) => {
|
|
|
2849
2979
|
manifest,
|
|
2850
2980
|
name: manifest?.name ?? basename2(root),
|
|
2851
2981
|
dirName,
|
|
2852
|
-
designDir:
|
|
2982
|
+
designDir: join13(root, dirName),
|
|
2853
2983
|
designKey: "/" + dirName.replace(/\\/g, "/")
|
|
2854
2984
|
};
|
|
2855
2985
|
};
|
|
2856
2986
|
var listScreens = (designDir) => {
|
|
2857
|
-
const dir =
|
|
2858
|
-
if (!
|
|
2987
|
+
const dir = join13(designDir, "screens");
|
|
2988
|
+
if (!existsSync11(dir))
|
|
2859
2989
|
return [];
|
|
2860
|
-
return
|
|
2990
|
+
return readdirSync6(dir).filter((f) => f.endsWith(".ts") && !f.startsWith(".")).map((f) => f.slice(0, -3)).sort();
|
|
2861
2991
|
};
|
|
2862
2992
|
var readMetaSizes = (designDir) => {
|
|
2863
2993
|
try {
|
|
2864
|
-
const meta = JSON.parse(
|
|
2994
|
+
const meta = JSON.parse(readFileSync10(join13(designDir, "meta.json"), "utf8"));
|
|
2865
2995
|
return meta?.screens ?? {};
|
|
2866
2996
|
} catch {
|
|
2867
2997
|
return {};
|
|
2868
2998
|
}
|
|
2869
2999
|
};
|
|
2870
3000
|
var writeMcpConfig = (ctx, port) => {
|
|
2871
|
-
const path =
|
|
3001
|
+
const path = join13(ctx.root, ".mcp.json");
|
|
2872
3002
|
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
2873
3003
|
let config = {};
|
|
2874
|
-
if (
|
|
3004
|
+
if (existsSync11(path)) {
|
|
2875
3005
|
try {
|
|
2876
|
-
config = JSON.parse(
|
|
3006
|
+
config = JSON.parse(readFileSync10(path, "utf8"));
|
|
2877
3007
|
} catch {
|
|
2878
3008
|
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-design server by hand.");
|
|
2879
3009
|
return;
|
|
@@ -2896,15 +3026,15 @@ var init = async (ctx, args) => {
|
|
|
2896
3026
|
["spec.md", SPEC_MD],
|
|
2897
3027
|
["README.md", README_HINT],
|
|
2898
3028
|
["CLAUDE.md", CLAUDE_MD],
|
|
2899
|
-
[
|
|
2900
|
-
[
|
|
3029
|
+
[join13("shared", "tokens.ts"), TOKENS_TS],
|
|
3030
|
+
[join13("screens", "home.ts"), HOME_SCREEN_TS]
|
|
2901
3031
|
];
|
|
2902
|
-
mkdirSync8(
|
|
2903
|
-
mkdirSync8(
|
|
3032
|
+
mkdirSync8(join13(ctx.designDir, "screens"), { recursive: true });
|
|
3033
|
+
mkdirSync8(join13(ctx.designDir, "shared"), { recursive: true });
|
|
2904
3034
|
let created = 0;
|
|
2905
3035
|
for (const [rel, content] of files) {
|
|
2906
|
-
const abs =
|
|
2907
|
-
if (
|
|
3036
|
+
const abs = join13(ctx.designDir, rel);
|
|
3037
|
+
if (existsSync11(abs)) {
|
|
2908
3038
|
note(`kept ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
2909
3039
|
continue;
|
|
2910
3040
|
}
|
|
@@ -2912,9 +3042,9 @@ var init = async (ctx, args) => {
|
|
|
2912
3042
|
log(`${c.green("created")} ${ctx.dirName}/${rel.replace(/\\/g, "/")}`);
|
|
2913
3043
|
created++;
|
|
2914
3044
|
}
|
|
2915
|
-
const ignorePath =
|
|
3045
|
+
const ignorePath = join13(ctx.root, ".lecodesignore");
|
|
2916
3046
|
const ignoreLine = `${ctx.dirName}/.snapshots/`;
|
|
2917
|
-
const current =
|
|
3047
|
+
const current = existsSync11(ignorePath) ? readFileSync10(ignorePath, "utf8") : "";
|
|
2918
3048
|
if (!current.split(/\r?\n/).includes(ignoreLine)) {
|
|
2919
3049
|
appendFileSync(ignorePath, (current && !current.endsWith(`
|
|
2920
3050
|
`) ? `
|
|
@@ -2925,8 +3055,8 @@ var init = async (ctx, args) => {
|
|
|
2925
3055
|
writeMcpConfig(ctx, Number(flagStr(args, "port")) || DEFAULT_PORT);
|
|
2926
3056
|
const wroteTypes = materializeTypesLocal(ctx.designDir);
|
|
2927
3057
|
writeTsconfig(ctx.designDir);
|
|
2928
|
-
mkdirSync8(
|
|
2929
|
-
writeFileSync8(
|
|
3058
|
+
mkdirSync8(join13(ctx.designDir, ".lecodes", "types"), { recursive: true });
|
|
3059
|
+
writeFileSync8(join13(ctx.designDir, ".lecodes", "types", "design-macros.d.ts"), DESIGN_MACROS_DTS);
|
|
2930
3060
|
log(`${c.green("created")} ${ctx.dirName}/tsconfig.json${wroteTypes ? ` + ${ctx.dirName}/.lecodes/types/` : ""} (IDE types)`);
|
|
2931
3061
|
if (!wroteTypes)
|
|
2932
3062
|
warnErr("Bundled SDK types not found — tsconfig written, but globals may show unresolved until the CLI is rebuilt with vendored types.");
|
|
@@ -2975,7 +3105,7 @@ var snapshot = async (ctx, args) => {
|
|
|
2975
3105
|
killer.unref?.();
|
|
2976
3106
|
const png = flagBool(args, "png") || flagStr(args, "png") !== undefined;
|
|
2977
3107
|
const sizes = readMetaSizes(ctx.designDir);
|
|
2978
|
-
const outDir = flagStr(args, "out-dir") ??
|
|
3108
|
+
const outDir = flagStr(args, "out-dir") ?? join13(ctx.designDir, ".snapshots");
|
|
2979
3109
|
const single = targets.length === 1 && requested.length === 1 && !png && !allStates && !flagStr(args, "out-dir");
|
|
2980
3110
|
const onConsole = flagBool(args, "logs") ? (level, text) => logErr(`${c.dim(`[${level}]`)} ${text}`) : undefined;
|
|
2981
3111
|
if (!single)
|
|
@@ -2984,7 +3114,7 @@ var snapshot = async (ctx, args) => {
|
|
|
2984
3114
|
const [width, height] = sizes[id]?.size ?? [390, 844];
|
|
2985
3115
|
let plan = [{ state: undefined, bases: [id] }];
|
|
2986
3116
|
if (discover) {
|
|
2987
|
-
const { states, canonical } = discover(
|
|
3117
|
+
const { states, canonical } = discover(readFileSync10(join13(ctx.designDir, "screens", `${id}.ts`), "utf8"));
|
|
2988
3118
|
if (stateFlag !== undefined) {
|
|
2989
3119
|
if (!states.includes(stateFlag)) {
|
|
2990
3120
|
if (requested.length === 1)
|
|
@@ -3025,18 +3155,18 @@ var snapshot = async (ctx, args) => {
|
|
|
3025
3155
|
for (const w of pngResult.warnings)
|
|
3026
3156
|
warnErr(`${tag}: ${w}`);
|
|
3027
3157
|
for (const base of bases) {
|
|
3028
|
-
writeFileSync8(
|
|
3029
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
3158
|
+
writeFileSync8(join13(outDir, `${base}.json`), result.text);
|
|
3159
|
+
log(`${c.green("✓")} ${tag} → ${join13(outDir, `${base}.json`)}`);
|
|
3030
3160
|
if (pngResult) {
|
|
3031
|
-
writeFileSync8(
|
|
3032
|
-
log(`${c.green("✓")} ${tag} → ${
|
|
3161
|
+
writeFileSync8(join13(outDir, `${base}.png`), pngResult.png);
|
|
3162
|
+
log(`${c.green("✓")} ${tag} → ${join13(outDir, `${base}.png`)}`);
|
|
3033
3163
|
}
|
|
3034
3164
|
}
|
|
3035
3165
|
}
|
|
3036
3166
|
}
|
|
3037
3167
|
};
|
|
3038
3168
|
var serve = async (ctx, args) => {
|
|
3039
|
-
if (!
|
|
3169
|
+
if (!existsSync11(ctx.designDir)) {
|
|
3040
3170
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
3041
3171
|
}
|
|
3042
3172
|
let designServer;
|
|
@@ -3109,7 +3239,7 @@ var serve = async (ctx, args) => {
|
|
|
3109
3239
|
openBrowser(server.url);
|
|
3110
3240
|
};
|
|
3111
3241
|
var arrange = async (ctx, args) => {
|
|
3112
|
-
if (!
|
|
3242
|
+
if (!existsSync11(ctx.designDir)) {
|
|
3113
3243
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
3114
3244
|
}
|
|
3115
3245
|
let designServer;
|
|
@@ -3145,7 +3275,7 @@ var share = async (ctx, args) => {
|
|
|
3145
3275
|
note("Turn it off with: lecodes design share --off");
|
|
3146
3276
|
};
|
|
3147
3277
|
var check = async (ctx, args) => {
|
|
3148
|
-
if (!
|
|
3278
|
+
if (!existsSync11(ctx.designDir)) {
|
|
3149
3279
|
throw new CliError(`No ${ctx.dirName}/ folder here. Run "lecodes design init" to scaffold it.`);
|
|
3150
3280
|
}
|
|
3151
3281
|
let designServer;
|
|
@@ -3210,18 +3340,18 @@ var design = async (args) => {
|
|
|
3210
3340
|
};
|
|
3211
3341
|
|
|
3212
3342
|
// src/commands/dev.ts
|
|
3213
|
-
import { existsSync as
|
|
3343
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync12, watch, writeFileSync as writeFileSync9 } from "node:fs";
|
|
3214
3344
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3215
3345
|
import { networkInterfaces } from "node:os";
|
|
3216
|
-
import { basename as basename4, join as
|
|
3346
|
+
import { basename as basename4, join as join15 } from "node:path";
|
|
3217
3347
|
|
|
3218
3348
|
// src/dev/devServer.ts
|
|
3219
3349
|
import { createServer as createServer2 } from "node:http";
|
|
3220
|
-
import { createReadStream, existsSync as
|
|
3221
|
-
import { join as
|
|
3350
|
+
import { createReadStream, existsSync as existsSync12, statSync as statSync2 } from "node:fs";
|
|
3351
|
+
import { join as join14 } from "node:path";
|
|
3222
3352
|
|
|
3223
3353
|
// src/compile/collectLocal.ts
|
|
3224
|
-
import { readFileSync as
|
|
3354
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
3225
3355
|
import { basename as basename3 } from "node:path";
|
|
3226
3356
|
var collectLocalEntries = (root, manifest, assetBase) => {
|
|
3227
3357
|
const entries = [];
|
|
@@ -3235,7 +3365,7 @@ var collectLocalEntries = (root, manifest, assetBase) => {
|
|
|
3235
3365
|
warnings.push(`${file.path}: shaders compile on the server — push the project once for shaders to work in dev mode.`);
|
|
3236
3366
|
entries.push({ path: file.path, type, shaders: [] });
|
|
3237
3367
|
} else {
|
|
3238
|
-
entries.push({ path: file.path, type, text:
|
|
3368
|
+
entries.push({ path: file.path, type, text: readFileSync11(file.absPath, "utf8") });
|
|
3239
3369
|
}
|
|
3240
3370
|
}
|
|
3241
3371
|
return { entries, warnings };
|
|
@@ -3595,8 +3725,8 @@ var startDevServer = async (options) => {
|
|
|
3595
3725
|
} catch {
|
|
3596
3726
|
return sendText(res, 400, "text/plain", "Bad path");
|
|
3597
3727
|
}
|
|
3598
|
-
const abs =
|
|
3599
|
-
if (!
|
|
3728
|
+
const abs = join14(root, key.slice(1));
|
|
3729
|
+
if (!existsSync12(abs) || !statSync2(abs).isFile())
|
|
3600
3730
|
return sendText(res, 404, "text/plain", "Not found");
|
|
3601
3731
|
res.writeHead(200, { "content-type": contentType(key), "cache-control": "no-store" });
|
|
3602
3732
|
createReadStream(abs).pipe(res);
|
|
@@ -3679,14 +3809,14 @@ var pickLanAddress = () => {
|
|
|
3679
3809
|
return null;
|
|
3680
3810
|
};
|
|
3681
3811
|
var loadDevToken = (root) => {
|
|
3682
|
-
const path =
|
|
3812
|
+
const path = join15(root, LECODES_DIR, "dev.json");
|
|
3683
3813
|
try {
|
|
3684
|
-
const token2 = JSON.parse(
|
|
3814
|
+
const token2 = JSON.parse(readFileSync12(path, "utf8")).token;
|
|
3685
3815
|
if (token2 && /^[a-z0-9]{8,}$/.test(token2))
|
|
3686
3816
|
return token2;
|
|
3687
3817
|
} catch {}
|
|
3688
3818
|
const token = randomBytes2(6).toString("hex");
|
|
3689
|
-
mkdirSync9(
|
|
3819
|
+
mkdirSync9(join15(root, LECODES_DIR), { recursive: true });
|
|
3690
3820
|
writeFileSync9(path, JSON.stringify({ token }, null, 2) + `
|
|
3691
3821
|
`);
|
|
3692
3822
|
return token;
|
|
@@ -3799,14 +3929,14 @@ ${result.error}`);
|
|
|
3799
3929
|
await printQr(server.url);
|
|
3800
3930
|
log(`Scan the QR with the LeCodes app, or open: ${c.bold(server.url)}`);
|
|
3801
3931
|
note(`Older desktop builds (no websocket bridge): ${server.url}?t=poll`);
|
|
3802
|
-
if (
|
|
3932
|
+
if (existsSync13(join15(root, LECODES_DIR, "manifest.json")))
|
|
3803
3933
|
note(`Token stored in ${LECODES_DIR}/dev.json (delete it to rotate the URL).`);
|
|
3804
3934
|
log("");
|
|
3805
3935
|
log(`Watching ${root} — save a file to hot-reload. Ctrl+C to stop.`);
|
|
3806
3936
|
};
|
|
3807
3937
|
|
|
3808
3938
|
// src/serverDiff.ts
|
|
3809
|
-
import { readFileSync as
|
|
3939
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
3810
3940
|
import { basename as basename5 } from "node:path";
|
|
3811
3941
|
var isTextAsset = (t) => t === "text" || t === "shader" || t === "scene";
|
|
3812
3942
|
var computeServerDiff = (project, scan) => {
|
|
@@ -3825,7 +3955,7 @@ var computeServerDiff = (project, scan) => {
|
|
|
3825
3955
|
const local = localByPath.get(path);
|
|
3826
3956
|
if (local && !server) {
|
|
3827
3957
|
const text = isTextKind(classifyKind(basename5(path)));
|
|
3828
|
-
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ?
|
|
3958
|
+
entries.push({ path, status: "added", binary: !text, serverText: "", localText: text ? readFileSync13(local.absPath, "utf8") : undefined });
|
|
3829
3959
|
continue;
|
|
3830
3960
|
}
|
|
3831
3961
|
if (server && !local) {
|
|
@@ -3836,12 +3966,12 @@ var computeServerDiff = (project, scan) => {
|
|
|
3836
3966
|
if (server && local) {
|
|
3837
3967
|
if (isTextAsset(server.type)) {
|
|
3838
3968
|
const serverText = server.text ?? "";
|
|
3839
|
-
const localText =
|
|
3969
|
+
const localText = readFileSync13(local.absPath, "utf8");
|
|
3840
3970
|
if (serverText !== localText)
|
|
3841
3971
|
entries.push({ path, status: "modified", binary: false, serverText, localText });
|
|
3842
3972
|
} else {
|
|
3843
3973
|
const serverSize = server.file?.size;
|
|
3844
|
-
const localSize = Buffer.byteLength(
|
|
3974
|
+
const localSize = Buffer.byteLength(readFileSync13(local.absPath));
|
|
3845
3975
|
if (serverSize !== undefined && serverSize !== localSize) {
|
|
3846
3976
|
entries.push({ path, status: "modified", binary: true });
|
|
3847
3977
|
}
|
|
@@ -3994,8 +4124,8 @@ var diff = async (args) => {
|
|
|
3994
4124
|
var statusLabel = (e) => e.status === "added" ? c.green(" added ") : e.status === "removed" ? c.red(" removed ") : c.yellow(" modified ");
|
|
3995
4125
|
|
|
3996
4126
|
// src/commands/init.ts
|
|
3997
|
-
import { existsSync as
|
|
3998
|
-
import { basename as basename6, dirname as dirname8, join as
|
|
4127
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync10, writeFileSync as writeFileSync10 } from "node:fs";
|
|
4128
|
+
import { basename as basename6, dirname as dirname8, join as join16, resolve as resolve3 } from "node:path";
|
|
3999
4129
|
var STARTER2 = `// Welcome to LeCodes — the whole SDK is available as globals, no imports needed.
|
|
4000
4130
|
// Run it live on a device with \`lecodes dev\`, or bundle it with \`lecodes preview -o app.js\`.
|
|
4001
4131
|
|
|
@@ -4038,8 +4168,8 @@ var init2 = async (args) => {
|
|
|
4038
4168
|
mkdirSync10(root, { recursive: true });
|
|
4039
4169
|
let created = 0;
|
|
4040
4170
|
const write = (rel, content) => {
|
|
4041
|
-
const abs =
|
|
4042
|
-
if (
|
|
4171
|
+
const abs = join16(root, rel);
|
|
4172
|
+
if (existsSync14(abs)) {
|
|
4043
4173
|
note(`kept ${rel}`);
|
|
4044
4174
|
return;
|
|
4045
4175
|
}
|
|
@@ -4079,7 +4209,7 @@ import { writeFileSync as writeFileSync11 } from "node:fs";
|
|
|
4079
4209
|
import { basename as basename8, resolve as resolve4 } from "node:path";
|
|
4080
4210
|
|
|
4081
4211
|
// src/compile/collect.ts
|
|
4082
|
-
import { readFileSync as
|
|
4212
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
4083
4213
|
import { basename as basename7 } from "node:path";
|
|
4084
4214
|
var collectEntries = (root, manifest, project) => {
|
|
4085
4215
|
const pathById = buildPaths(project.assets);
|
|
@@ -4104,7 +4234,7 @@ var collectEntries = (root, manifest, project) => {
|
|
|
4104
4234
|
warnings.push(`No compiled shader for ${file.path} — push it to compile the shader.`);
|
|
4105
4235
|
entries.push({ path: file.path, type, shaders });
|
|
4106
4236
|
} else {
|
|
4107
|
-
entries.push({ path: file.path, type, text:
|
|
4237
|
+
entries.push({ path: file.path, type, text: readFileSync14(file.absPath, "utf8") });
|
|
4108
4238
|
}
|
|
4109
4239
|
}
|
|
4110
4240
|
return { entries, warnings };
|
|
@@ -4223,7 +4353,7 @@ var render = async (args) => {
|
|
|
4223
4353
|
format: "iife"
|
|
4224
4354
|
});
|
|
4225
4355
|
if (js.includes("_creator.createScene") || js.includes("_creator.createEntity")) {
|
|
4226
|
-
warnings.push("
|
|
4356
|
+
warnings.push("3D project: the scene graph is captured as `scene3d` in the JSON output; 3D isn't rasterized into PNGs.");
|
|
4227
4357
|
}
|
|
4228
4358
|
if (js.includes("_creator2d") && !useLocalAssets && !flagBool(args, "wait-network")) {
|
|
4229
4359
|
warnings.push("2D scene detected: sprite textures load over the network — add --wait-network to include them (else sprites render as flat placeholders).");
|
|
@@ -4292,8 +4422,8 @@ var render = async (args) => {
|
|
|
4292
4422
|
};
|
|
4293
4423
|
|
|
4294
4424
|
// src/commands/pull.ts
|
|
4295
|
-
import { existsSync as
|
|
4296
|
-
import { dirname as dirname9, join as
|
|
4425
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync11, rmSync, writeFileSync as writeFileSync13 } from "node:fs";
|
|
4426
|
+
import { dirname as dirname9, join as join17 } from "node:path";
|
|
4297
4427
|
var pull = async (args) => {
|
|
4298
4428
|
const root = findProjectRoot(process.cwd());
|
|
4299
4429
|
const manifest = readManifest(root);
|
|
@@ -4313,8 +4443,8 @@ var pull = async (args) => {
|
|
|
4313
4443
|
const remotePaths = new Set([...buildPaths(project.assets).entries()].filter(([id]) => project.assets.find((a) => a.id === id)?.type !== "folder").map(([, path]) => path));
|
|
4314
4444
|
for (const path of Object.keys(manifest.files)) {
|
|
4315
4445
|
if (!remotePaths.has(path)) {
|
|
4316
|
-
const abs =
|
|
4317
|
-
if (
|
|
4446
|
+
const abs = join17(root, ...path.replace(/^\//, "").split("/"));
|
|
4447
|
+
if (existsSync15(abs))
|
|
4318
4448
|
rmSync(abs);
|
|
4319
4449
|
}
|
|
4320
4450
|
}
|
|
@@ -4352,7 +4482,7 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
4352
4482
|
const files = { ...manifest.files };
|
|
4353
4483
|
for (const asset of targets) {
|
|
4354
4484
|
const key = pathById.get(asset.id);
|
|
4355
|
-
const abs =
|
|
4485
|
+
const abs = join17(root, ...key.replace(/^\//, "").split("/"));
|
|
4356
4486
|
mkdirSync11(dirname9(abs), { recursive: true });
|
|
4357
4487
|
let sha;
|
|
4358
4488
|
if (asset.type === "file") {
|
|
@@ -4376,7 +4506,7 @@ var pullPaths = async (root, manifest, project, paths, apiUrl, token) => {
|
|
|
4376
4506
|
|
|
4377
4507
|
// src/commands/push.ts
|
|
4378
4508
|
import { basename as basename9 } from "node:path";
|
|
4379
|
-
import { readFileSync as
|
|
4509
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
4380
4510
|
var push = async (args) => {
|
|
4381
4511
|
const root = findProjectRoot(process.cwd());
|
|
4382
4512
|
const manifest = readManifest(root);
|
|
@@ -4435,9 +4565,9 @@ var push = async (args) => {
|
|
|
4435
4565
|
if (source)
|
|
4436
4566
|
entry.assetId = source.id;
|
|
4437
4567
|
if (isTextKind(kind)) {
|
|
4438
|
-
entry.text =
|
|
4568
|
+
entry.text = readFileSync15(file.absPath, "utf8");
|
|
4439
4569
|
} else if (!source || source.sha !== file.sha) {
|
|
4440
|
-
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename9(file.path),
|
|
4570
|
+
const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename9(file.path), readFileSync15(file.absPath));
|
|
4441
4571
|
entry.blobFileId = blob.id;
|
|
4442
4572
|
uploaded++;
|
|
4443
4573
|
}
|
|
@@ -4489,6 +4619,154 @@ var reportPublish = (res) => {
|
|
|
4489
4619
|
warn(`Push landed, but publish failed: ${res.publishError ?? "unknown error"}`);
|
|
4490
4620
|
};
|
|
4491
4621
|
|
|
4622
|
+
// src/commands/scene.ts
|
|
4623
|
+
import { existsSync as existsSync17, readFileSync as readFileSync17, readdirSync as readdirSync7, statSync as statSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
4624
|
+
import { basename as basename10, dirname as dirname10, join as join19, resolve as resolve6 } from "node:path";
|
|
4625
|
+
|
|
4626
|
+
// src/compile/sceneCompile.ts
|
|
4627
|
+
import { existsSync as existsSync16, readFileSync as readFileSync16 } from "node:fs";
|
|
4628
|
+
import { join as join18 } from "node:path";
|
|
4629
|
+
var TEXT_EXT = /\.(ts|js|json|svg|txt|md)$/i;
|
|
4630
|
+
var rel = (key) => key.replace(/^\//, "");
|
|
4631
|
+
var resolveHarness = (loadSceneHarness2) => {
|
|
4632
|
+
const inRepo = join18(distRoot, "..", "projects", "src", "scene-editor");
|
|
4633
|
+
try {
|
|
4634
|
+
return loadSceneHarness2(existsSync16(inRepo) ? inRepo : undefined);
|
|
4635
|
+
} catch {
|
|
4636
|
+
const vendored = join18(distRoot, "runtime", "scene-harness.json");
|
|
4637
|
+
if (existsSync16(vendored))
|
|
4638
|
+
return JSON.parse(readFileSync16(vendored, "utf8"));
|
|
4639
|
+
throw new CliError("scene-editor harness sources not found — reinstall the CLI or set LECODES_SCENE_HARNESS_DIR.");
|
|
4640
|
+
}
|
|
4641
|
+
};
|
|
4642
|
+
var compileSceneEditor = async (opts) => {
|
|
4643
|
+
const { root, scenePath } = opts;
|
|
4644
|
+
const { loadSceneHarness: loadSceneHarness2, sceneEditorEntries: sceneEditorEntries2, SCENE_ENTRY_PATH: SCENE_ENTRY_PATH2 } = await Promise.resolve().then(() => (init_compile(), exports_compile));
|
|
4645
|
+
const entries = [];
|
|
4646
|
+
const scan = scanLocal(root, { ignore: loadIgnore(root), tracked: new Set(Object.keys(opts.manifest?.files ?? {})) });
|
|
4647
|
+
for (const file of scan.files) {
|
|
4648
|
+
const path = "./" + rel(file.path);
|
|
4649
|
+
if (TEXT_EXT.test(path)) {
|
|
4650
|
+
entries.push({ path, type: "text", text: readFileSync16(file.absPath, "utf8") });
|
|
4651
|
+
} else {
|
|
4652
|
+
entries.push({ path, type: "resource", fileSrc: "/project/" + rel(file.path) });
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
const scene = scenePath.startsWith("./") ? scenePath : "./" + rel(scenePath);
|
|
4656
|
+
if (!entries.some((e) => e.path === scene && e.type === "text")) {
|
|
4657
|
+
return { js: null, error: `Scene file not found: ${scene}` };
|
|
4658
|
+
}
|
|
4659
|
+
entries.push(...sceneEditorEntries2(scene, resolveHarness(loadSceneHarness2), entries.map((e) => e.path)));
|
|
4660
|
+
try {
|
|
4661
|
+
const js = await compileProject2({
|
|
4662
|
+
entries,
|
|
4663
|
+
name: opts.name,
|
|
4664
|
+
publicUrl: "",
|
|
4665
|
+
entryOverride: SCENE_ENTRY_PATH2,
|
|
4666
|
+
header: false,
|
|
4667
|
+
format: "iife",
|
|
4668
|
+
minify: false,
|
|
4669
|
+
editor: true
|
|
4670
|
+
});
|
|
4671
|
+
return { js };
|
|
4672
|
+
} catch (e) {
|
|
4673
|
+
return { js: null, error: e?.message?.replace(/mem:/g, "") ?? String(e) };
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
|
|
4677
|
+
// src/commands/scene.ts
|
|
4678
|
+
var DEFAULT_PORT3 = 4488;
|
|
4679
|
+
var getContext2 = (args) => {
|
|
4680
|
+
let root;
|
|
4681
|
+
let manifest;
|
|
4682
|
+
try {
|
|
4683
|
+
root = findProjectRoot(process.cwd());
|
|
4684
|
+
manifest = readManifest(root);
|
|
4685
|
+
} catch {
|
|
4686
|
+
root = process.cwd();
|
|
4687
|
+
manifest = null;
|
|
4688
|
+
const pathArg = args._[0];
|
|
4689
|
+
if (pathArg) {
|
|
4690
|
+
const abs = resolve6(process.cwd(), pathArg);
|
|
4691
|
+
if (existsSync17(abs) && statSync3(abs).isDirectory())
|
|
4692
|
+
root = abs;
|
|
4693
|
+
else if (abs.endsWith(".scene.ts") && existsSync17(abs))
|
|
4694
|
+
root = dirname10(abs);
|
|
4695
|
+
else
|
|
4696
|
+
throw new CliError(`No such folder or .scene.ts: ${pathArg}`);
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
return { root, manifest, name: manifest?.name ?? basename10(root) };
|
|
4700
|
+
};
|
|
4701
|
+
var writeMcpConfig2 = (root, port) => {
|
|
4702
|
+
const path = join19(root, ".mcp.json");
|
|
4703
|
+
const entry = { type: "http", url: `http://localhost:${port}/mcp` };
|
|
4704
|
+
let config = {};
|
|
4705
|
+
if (existsSync17(path)) {
|
|
4706
|
+
try {
|
|
4707
|
+
config = JSON.parse(readFileSync17(path, "utf8"));
|
|
4708
|
+
} catch {
|
|
4709
|
+
warnErr(".mcp.json exists but isn't valid JSON — left it untouched. Add the lecodes-scene server by hand.");
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
config.mcpServers ??= {};
|
|
4714
|
+
const existing = config.mcpServers["lecodes-scene"];
|
|
4715
|
+
if (existing?.url === entry.url) {
|
|
4716
|
+
note("kept .mcp.json (lecodes-scene MCP server)");
|
|
4717
|
+
return;
|
|
4718
|
+
}
|
|
4719
|
+
config.mcpServers["lecodes-scene"] = entry;
|
|
4720
|
+
writeFileSync14(path, JSON.stringify(config, null, 2) + `
|
|
4721
|
+
`);
|
|
4722
|
+
note(`${existing ? "updated" : "created"} .mcp.json (lecodes-scene MCP → ${entry.url})`);
|
|
4723
|
+
};
|
|
4724
|
+
var countScenes = (dir) => {
|
|
4725
|
+
let n = 0;
|
|
4726
|
+
for (const name of readdirSync7(dir)) {
|
|
4727
|
+
if (name === "node_modules" || name.startsWith("."))
|
|
4728
|
+
continue;
|
|
4729
|
+
const full = join19(dir, name);
|
|
4730
|
+
if (statSync3(full).isDirectory())
|
|
4731
|
+
n += countScenes(full);
|
|
4732
|
+
else if (name.endsWith(".scene.ts"))
|
|
4733
|
+
n++;
|
|
4734
|
+
}
|
|
4735
|
+
return n;
|
|
4736
|
+
};
|
|
4737
|
+
var scene = async (args) => {
|
|
4738
|
+
const ctx = getContext2(args);
|
|
4739
|
+
let sceneServer;
|
|
4740
|
+
try {
|
|
4741
|
+
sceneServer = await import("lecodes-3d-editor/server");
|
|
4742
|
+
} catch {
|
|
4743
|
+
throw new CliError("The scene editor isn't installed. Run: npm i -D lecodes-3d-editor");
|
|
4744
|
+
}
|
|
4745
|
+
const port = Number(flagStr(args, "port")) || DEFAULT_PORT3;
|
|
4746
|
+
if (countScenes(ctx.root) === 0) {
|
|
4747
|
+
warnErr(`No .scene.ts file under ${ctx.root} yet — add one and the editor will pick it up.`);
|
|
4748
|
+
}
|
|
4749
|
+
const server = await sceneServer.startSceneServer({
|
|
4750
|
+
root: ctx.root,
|
|
4751
|
+
port,
|
|
4752
|
+
log: note,
|
|
4753
|
+
compile: (scenePath) => compileSceneEditor({
|
|
4754
|
+
root: ctx.root,
|
|
4755
|
+
manifest: ctx.manifest,
|
|
4756
|
+
name: ctx.name,
|
|
4757
|
+
scenePath
|
|
4758
|
+
})
|
|
4759
|
+
});
|
|
4760
|
+
if (!flagBool(args, "no-mcp"))
|
|
4761
|
+
writeMcpConfig2(ctx.root, port);
|
|
4762
|
+
log(`${c.bold("LeCodes Scene Editor")} — ${ctx.name}`);
|
|
4763
|
+
log(`Editor: ${c.bold(server.url)}`);
|
|
4764
|
+
log(`MCP: ${c.bold(server.url + "/mcp")}`);
|
|
4765
|
+
log(`Watching: ${ctx.root} (scenes hot-reload on save; Ctrl+C to stop)`);
|
|
4766
|
+
if (!flagBool(args, "no-open"))
|
|
4767
|
+
openBrowser(server.url);
|
|
4768
|
+
};
|
|
4769
|
+
|
|
4492
4770
|
// src/commands/status.ts
|
|
4493
4771
|
var status = async (_args) => {
|
|
4494
4772
|
const root = findProjectRoot(process.cwd());
|
|
@@ -4519,21 +4797,21 @@ var status = async (_args) => {
|
|
|
4519
4797
|
};
|
|
4520
4798
|
|
|
4521
4799
|
// src/index.ts
|
|
4522
|
-
import { existsSync as
|
|
4523
|
-
import { delimiter as delimiter2, dirname as
|
|
4800
|
+
import { existsSync as existsSync19, realpathSync as realpathSync2 } from "node:fs";
|
|
4801
|
+
import { delimiter as delimiter2, dirname as dirname12, join as join21 } from "node:path";
|
|
4524
4802
|
|
|
4525
4803
|
// src/commands/install.ts
|
|
4526
4804
|
import { execFileSync } from "node:child_process";
|
|
4527
|
-
import { appendFileSync as appendFileSync2, chmodSync, cpSync, existsSync as
|
|
4805
|
+
import { appendFileSync as appendFileSync2, chmodSync, cpSync, existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync18, realpathSync, rmSync as rmSync2, symlinkSync } from "node:fs";
|
|
4528
4806
|
import { homedir as homedir2 } from "node:os";
|
|
4529
|
-
import { basename as
|
|
4807
|
+
import { basename as basename11, delimiter, dirname as dirname11, join as join20, resolve as resolve7, sep } from "node:path";
|
|
4530
4808
|
var isWin = process.platform === "win32";
|
|
4531
4809
|
var HOME = homedir2();
|
|
4532
4810
|
var safeRealpath = (p) => {
|
|
4533
4811
|
try {
|
|
4534
4812
|
return realpathSync(p);
|
|
4535
4813
|
} catch {
|
|
4536
|
-
return
|
|
4814
|
+
return resolve7(p);
|
|
4537
4815
|
}
|
|
4538
4816
|
};
|
|
4539
4817
|
var onPath = (dir) => {
|
|
@@ -4549,13 +4827,13 @@ var onPath = (dir) => {
|
|
|
4549
4827
|
});
|
|
4550
4828
|
};
|
|
4551
4829
|
var install = async (args) => {
|
|
4552
|
-
const bundle =
|
|
4553
|
-
const exeName =
|
|
4830
|
+
const bundle = dirname11(process.execPath);
|
|
4831
|
+
const exeName = basename11(process.execPath);
|
|
4554
4832
|
const chiselName = isWin ? "chisel.exe" : "chisel";
|
|
4555
|
-
if (!
|
|
4833
|
+
if (!existsSync18(join20(bundle, "runtime")) || !existsSync18(join20(bundle, chiselName))) {
|
|
4556
4834
|
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.");
|
|
4557
4835
|
}
|
|
4558
|
-
const companions = [exeName, chiselName, "runtime", "README.txt"].filter((f) =>
|
|
4836
|
+
const companions = [exeName, chiselName, "runtime", "README.txt"].filter((f) => existsSync18(join20(bundle, f)));
|
|
4559
4837
|
if (isWin)
|
|
4560
4838
|
return installWindows(args, bundle, companions);
|
|
4561
4839
|
return installUnix(args, bundle, companions);
|
|
@@ -4565,9 +4843,9 @@ var copyBundle = (bundle, target, companions) => {
|
|
|
4565
4843
|
if (safeRealpath(target) === safeRealpath(bundle))
|
|
4566
4844
|
return;
|
|
4567
4845
|
for (const f of companions) {
|
|
4568
|
-
const dest =
|
|
4846
|
+
const dest = join20(target, f);
|
|
4569
4847
|
rmSync2(dest, { recursive: true, force: true });
|
|
4570
|
-
cpSync(
|
|
4848
|
+
cpSync(join20(bundle, f), dest, { recursive: true });
|
|
4571
4849
|
}
|
|
4572
4850
|
};
|
|
4573
4851
|
var permError = (e, dir, system) => {
|
|
@@ -4579,41 +4857,41 @@ var permError = (e, dir, system) => {
|
|
|
4579
4857
|
};
|
|
4580
4858
|
var installUnix = (args, bundle, companions) => {
|
|
4581
4859
|
const system = flagBool(args, "system");
|
|
4582
|
-
const libDir =
|
|
4583
|
-
const binDir =
|
|
4584
|
-
const link =
|
|
4860
|
+
const libDir = resolve7(flagStr(args, "dir") ?? (system ? "/usr/local/lib/lecodes-cli" : join20(HOME, ".local", "lib", "lecodes-cli")));
|
|
4861
|
+
const binDir = resolve7(flagStr(args, "bin") ?? (system ? "/usr/local/bin" : join20(HOME, ".local", "bin")));
|
|
4862
|
+
const link = join20(binDir, "lecodes");
|
|
4585
4863
|
try {
|
|
4586
4864
|
copyBundle(bundle, libDir, companions);
|
|
4587
|
-
chmodSync(
|
|
4588
|
-
chmodSync(
|
|
4865
|
+
chmodSync(join20(libDir, "lecodes"), 493);
|
|
4866
|
+
chmodSync(join20(libDir, "chisel"), 493);
|
|
4589
4867
|
} catch (e) {
|
|
4590
4868
|
throw permError(e, libDir, system);
|
|
4591
4869
|
}
|
|
4592
4870
|
try {
|
|
4593
4871
|
mkdirSync12(binDir, { recursive: true });
|
|
4594
4872
|
rmSync2(link, { force: true });
|
|
4595
|
-
symlinkSync(
|
|
4873
|
+
symlinkSync(join20(libDir, "lecodes"), link);
|
|
4596
4874
|
} catch (e) {
|
|
4597
4875
|
throw permError(e, binDir, system);
|
|
4598
4876
|
}
|
|
4599
4877
|
success(`Installed to ${c.bold(libDir)}`);
|
|
4600
|
-
log(` ${c.dim(link + " → " +
|
|
4878
|
+
log(` ${c.dim(link + " → " + join20(libDir, "lecodes"))}`);
|
|
4601
4879
|
finishPathUnix(args, binDir);
|
|
4602
4880
|
};
|
|
4603
4881
|
var shellRc = () => {
|
|
4604
|
-
const shell =
|
|
4882
|
+
const shell = basename11(process.env.SHELL ?? "");
|
|
4605
4883
|
if (shell === "zsh")
|
|
4606
|
-
return { file:
|
|
4884
|
+
return { file: join20(HOME, ".zshrc"), name: "~/.zshrc" };
|
|
4607
4885
|
if (shell === "bash")
|
|
4608
|
-
return { file:
|
|
4609
|
-
return { file:
|
|
4886
|
+
return { file: join20(HOME, ".bashrc"), name: "~/.bashrc" };
|
|
4887
|
+
return { file: join20(HOME, ".profile"), name: "~/.profile" };
|
|
4610
4888
|
};
|
|
4611
4889
|
var PATH_MARKER = "# Added by `lecodes install`";
|
|
4612
4890
|
var appendPathToShellRc = (binDir) => {
|
|
4613
4891
|
const { file, name } = shellRc();
|
|
4614
4892
|
const line = `export PATH="${binDir}:$PATH"`;
|
|
4615
4893
|
try {
|
|
4616
|
-
const existing =
|
|
4894
|
+
const existing = existsSync18(file) ? readFileSync18(file, "utf8") : "";
|
|
4617
4895
|
if (existing.includes(line))
|
|
4618
4896
|
return name;
|
|
4619
4897
|
appendFileSync2(file, `${existing && !existing.endsWith(`
|
|
@@ -4651,7 +4929,7 @@ var finishPathUnix = (args, binDir) => {
|
|
|
4651
4929
|
log(` • or install system-wide (no PATH edit): ${c.bold("sudo lecodes install --system")}`);
|
|
4652
4930
|
};
|
|
4653
4931
|
var installWindows = (args, bundle, companions) => {
|
|
4654
|
-
const target =
|
|
4932
|
+
const target = resolve7(flagStr(args, "dir") ?? join20(process.env.LOCALAPPDATA ?? join20(HOME, "AppData", "Local"), "Programs", "lecodes-cli"));
|
|
4655
4933
|
try {
|
|
4656
4934
|
copyBundle(bundle, target, companions);
|
|
4657
4935
|
} catch (e) {
|
|
@@ -4688,9 +4966,9 @@ var addToUserPathWindows = (dir) => {
|
|
|
4688
4966
|
|
|
4689
4967
|
// src/index.ts
|
|
4690
4968
|
var STANDALONE = (() => {
|
|
4691
|
-
const dir =
|
|
4969
|
+
const dir = dirname12(process.execPath);
|
|
4692
4970
|
const chisel = process.platform === "win32" ? "chisel.exe" : "chisel";
|
|
4693
|
-
return
|
|
4971
|
+
return existsSync19(join21(dir, "runtime")) && existsSync19(join21(dir, chisel));
|
|
4694
4972
|
})();
|
|
4695
4973
|
var INSTALLED = STANDALONE && (() => {
|
|
4696
4974
|
const exe = process.platform === "win32" ? "lecodes.exe" : "lecodes";
|
|
@@ -4705,8 +4983,8 @@ var INSTALLED = STANDALONE && (() => {
|
|
|
4705
4983
|
if (!d)
|
|
4706
4984
|
return false;
|
|
4707
4985
|
try {
|
|
4708
|
-
const cand =
|
|
4709
|
-
return
|
|
4986
|
+
const cand = join21(d, exe);
|
|
4987
|
+
return existsSync19(cand) && realpathSync2(cand) === self;
|
|
4710
4988
|
} catch {
|
|
4711
4989
|
return false;
|
|
4712
4990
|
}
|
|
@@ -4784,9 +5062,15 @@ ${c.bold("Commands:")}
|
|
|
4784
5062
|
--png also rasterize PNGs (needs @napi-rs/canvas)
|
|
4785
5063
|
--out-dir <dir> output folder override
|
|
4786
5064
|
--logs forward the screens' console output (to stderr)
|
|
5065
|
+
scene [path] LeCodes Scene Editor — visually edit a .scene.ts (tree, inspector,
|
|
5066
|
+
gizmo) on local files (needs the optional 'lecodes-3d-editor' package;
|
|
5067
|
+
works inside a cloned project or standalone in any folder)
|
|
5068
|
+
--port <n> editor-server port (default 4488)
|
|
5069
|
+
--no-open don't open the browser
|
|
5070
|
+
--no-mcp don't register the MCP server in .mcp.json
|
|
4787
5071
|
|
|
4788
5072
|
Config lives in ~/.lecodes/config.json (override with LECODES_API / LECODES_TOKEN).`;
|
|
4789
|
-
var commands = { init: init2, login, create, clone, status, diff, pull, push, preview, render, design, dev };
|
|
5073
|
+
var commands = { init: init2, login, create, clone, status, diff, pull, push, preview, render, design, scene, dev };
|
|
4790
5074
|
if (STANDALONE)
|
|
4791
5075
|
commands.install = install;
|
|
4792
5076
|
var readVersion = () => package_default.version ?? "unknown";
|