lecodes-cli 0.20.2 → 2.0.0

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.
Files changed (154) hide show
  1. package/README.md +56 -57
  2. package/dist/index.js +11052 -7592
  3. package/package.json +14 -8
  4. package/runtime/materials/decal-relief.mat +12 -1
  5. package/runtime/materials/decal.mat +17 -3
  6. package/runtime/materials/lightmap.mat +117 -138
  7. package/runtime/materials/particles-quad.mat +6 -2
  8. package/runtime/materials/terrain-lightmap.mat +123 -0
  9. package/runtime/materials/terrain.mat +36 -55
  10. package/runtime/sdk-types.json +1 -1
  11. package/runtime/web/VERSION.json +5 -0
  12. package/runtime/web/assets/camera-5XOM8eHl.js +1 -0
  13. package/runtime/web/assets/creator-2d-D9WKYM6h.js +1 -0
  14. package/runtime/web/assets/creator-2d-ukvEmNEp.wasm +0 -0
  15. package/runtime/web/assets/creator-full-BBXzQlhq.js +1 -0
  16. package/runtime/web/assets/creator-full-D2dSDdDj.wasm +0 -0
  17. package/runtime/web/assets/creator-full.js-BYswGsqR.symbols +8124 -0
  18. package/runtime/web/assets/creator-ui-DIc92ion.wasm +0 -0
  19. package/runtime/web/assets/creator-ui-_1Z_AX2M.js +1 -0
  20. package/runtime/web/assets/geolocation-BjTKN9_P.js +1 -0
  21. package/runtime/web/assets/inspect-C7x78PNs.js +2 -0
  22. package/runtime/web/assets/map-CyBxOSV0.js +1 -0
  23. package/runtime/web/assets/map-XzoOFt0E.js +2 -0
  24. package/runtime/web/assets/mapImpl-bxvQ2h1Z.js +823 -0
  25. package/runtime/web/assets/maplibre-gl-worker-B7LXXUkR.js +8 -0
  26. package/runtime/web/assets/materials-uri4FvPE.bin +0 -0
  27. package/runtime/web/assets/materials-world-CQWt8TAm.bin +0 -0
  28. package/runtime/web/assets/qr-scanner-CK_cYoJR.js +1 -0
  29. package/runtime/web/assets/uberarchive-DLH-kBhM.bin +0 -0
  30. package/runtime/web/index.html +36 -0
  31. package/runtime/web/player.js +21 -0
  32. package/runtime/web-page/index.html +41 -0
  33. package/runtime/web-page/page.css +1 -0
  34. package/runtime/web-page/page.js +31 -0
  35. package/src/api.ts +1 -302
  36. package/src/cli/args.ts +37 -0
  37. package/src/cli/command.ts +113 -0
  38. package/src/cli/errors.ts +13 -0
  39. package/src/cli/help.ts +158 -0
  40. package/src/cli/index.ts +7 -0
  41. package/src/cli/output.ts +102 -0
  42. package/src/cli/run.ts +106 -0
  43. package/src/commands/{appAndroid.ts → app/android.ts} +454 -43
  44. package/src/commands/{appDesktop.ts → app/desktop.ts} +64 -33
  45. package/src/commands/{appDesktopMac.ts → app/desktopMac.ts} +34 -27
  46. package/src/commands/{appIcon.ts → app/icon.ts} +11 -8
  47. package/src/commands/{app.ts → app/index.ts} +400 -290
  48. package/src/commands/{appShared.ts → app/shared.ts} +43 -44
  49. package/src/commands/{appTemplatesAndroid.ts → app/templates/android.ts} +72 -39
  50. package/src/commands/{appTemplatesGradlew.ts → app/templates/gradlew.ts} +1 -1
  51. package/src/commands/{appTemplates.ts → app/templates/ios.ts} +2 -2
  52. package/src/commands/assets.ts +23 -21
  53. package/src/commands/clone.ts +48 -51
  54. package/src/commands/compile.ts +76 -89
  55. package/src/commands/create.ts +46 -47
  56. package/src/commands/design/comments.ts +361 -0
  57. package/src/commands/design/context.ts +101 -0
  58. package/src/commands/design/index.ts +391 -0
  59. package/src/commands/design/snapshot.ts +134 -0
  60. package/src/commands/{designTemplates.ts → design/templates.ts} +4 -3
  61. package/src/commands/desktop.ts +94 -82
  62. package/src/commands/dev.ts +260 -182
  63. package/src/commands/diff.ts +39 -51
  64. package/src/commands/index.ts +64 -0
  65. package/src/commands/{init.ts → init/index.ts} +195 -191
  66. package/src/commands/{projectTemplates.ts → init/templates.ts} +64 -21
  67. package/src/commands/install.ts +80 -67
  68. package/src/commands/lightmap.ts +543 -252
  69. package/src/commands/link.ts +87 -94
  70. package/src/commands/login.ts +25 -23
  71. package/src/commands/navmesh.ts +169 -139
  72. package/src/commands/plugin.ts +46 -0
  73. package/src/commands/pn.ts +201 -244
  74. package/src/commands/pull.ts +49 -56
  75. package/src/commands/push.ts +48 -54
  76. package/src/commands/render.ts +176 -111
  77. package/src/commands/scene.ts +63 -55
  78. package/src/commands/serve.ts +58 -0
  79. package/src/commands/{shaders.ts → shaders/index.ts} +61 -42
  80. package/src/commands/{shadersNew.ts → shaders/new.ts} +3 -3
  81. package/src/commands/shared.ts +79 -0
  82. package/src/commands/status.ts +22 -26
  83. package/src/commands/test.ts +94 -63
  84. package/src/commands/thumbs.ts +185 -175
  85. package/src/commands/update/index.ts +226 -0
  86. package/src/commands/{types.ts → update/types.ts} +31 -22
  87. package/src/compile/collect.ts +5 -5
  88. package/src/compile/collectLocal.ts +3 -3
  89. package/src/compile/designCompile.ts +4 -4
  90. package/src/compile/headlessBundle.ts +16 -12
  91. package/src/compile/nativeStack.ts +1 -1
  92. package/src/compile/projectCompile.ts +12 -4
  93. package/src/compile/sceneCompile.ts +14 -26
  94. package/src/compile/screenEntry.ts +123 -127
  95. package/src/compile/shaders.ts +3 -3
  96. package/src/{lecodes-3d-editor.d.ts → declarations/lecodes-3d-editor.d.ts} +4 -0
  97. package/src/{lecodes-assets.d.ts → declarations/lecodes-assets.d.ts} +1 -1
  98. package/src/{lecodes-renderer.d.ts → declarations/lecodes-headless.d.ts} +3 -3
  99. package/src/dev/androidDev.ts +1 -1
  100. package/src/dev/clientTemplates.ts +18 -14
  101. package/src/dev/devServer.ts +78 -41
  102. package/src/dev/iosDev.ts +180 -0
  103. package/src/dev/webRunner.ts +83 -15
  104. package/src/{cmgenTool.ts → hosts/cmgenTool.ts} +2 -2
  105. package/src/{desktopRenderer.ts → hosts/desktopRenderer.ts} +104 -32
  106. package/src/{desktopScript.ts → hosts/desktopScript.ts} +151 -36
  107. package/src/{distRoot.ts → hosts/distRoot.ts} +9 -5
  108. package/src/{matcTool.ts → hosts/matcTool.ts} +2 -2
  109. package/src/{peerInstall.ts → hosts/peerInstall.ts} +7 -7
  110. package/src/{peers.ts → hosts/peers.ts} +2 -2
  111. package/src/{releases.ts → hosts/releases.ts} +1 -1
  112. package/src/index.ts +35 -480
  113. package/src/platform/api.ts +302 -0
  114. package/src/{browserAuth.ts → platform/browserAuth.ts} +1 -1
  115. package/src/{config.ts → platform/config.ts} +1 -1
  116. package/src/{serverDiff.ts → platform/serverDiff.ts} +3 -3
  117. package/src/plugin/contract.ts +438 -0
  118. package/src/plugin/emitKotlin.ts +357 -0
  119. package/src/plugin/emitSdk.ts +259 -0
  120. package/src/plugin/emitSwift.ts +336 -0
  121. package/src/plugin/emitWeb.ts +383 -0
  122. package/src/plugin/generate.ts +157 -0
  123. package/src/plugin/lines.ts +28 -0
  124. package/src/plugin/lower.ts +231 -0
  125. package/src/plugin/manifest.ts +192 -0
  126. package/src/{projectEnv.ts → project/env.ts} +33 -2
  127. package/src/{ignore.ts → project/ignore.ts} +164 -163
  128. package/src/{localFiles.ts → project/localFiles.ts} +1 -1
  129. package/src/{manifest.ts → project/manifest.ts} +1 -1
  130. package/src/{project.ts → project/materialize.ts} +2 -2
  131. package/src/project/paths.ts +20 -0
  132. package/src/{textDiff.ts → project/textDiff.ts} +1 -1
  133. package/src/{types.ts → project/types.ts} +0 -0
  134. package/runtime/materials/lightmap-baked-lite.mat +0 -175
  135. package/runtime/materials/lightmap-baked.mat +0 -176
  136. package/runtime/scene-harness.json +0 -1
  137. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +0 -1
  138. package/runtime/web/assets/basis-C64VHDVD.js +0 -1
  139. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  140. package/runtime/web/assets/createViewerLite-Ct_PZdof.js +0 -852
  141. package/runtime/web/assets/draco-BiISTFcR.js +0 -118
  142. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  143. package/runtime/web/assets/index-BMt7AnC5.js +0 -2
  144. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +0 -810
  145. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +0 -8
  146. package/runtime/web/assets/worker-Caf-yYEI.js +0 -2
  147. package/runtime/web/embed.html +0 -32
  148. package/runtime/web/embed.js +0 -171
  149. package/src/commands/design.ts +0 -846
  150. package/src/commands/update.ts +0 -211
  151. package/src/util.ts +0 -146
  152. /package/src/{lecodes-design.d.ts → declarations/lecodes-design.d.ts} +0 -0
  153. /package/src/{qrcode-terminal.d.ts → declarations/qrcode-terminal.d.ts} +0 -0
  154. /package/src/{designMeta.ts → project/designMeta.ts} +0 -0
@@ -1,12 +1,12 @@
1
- import { basename } from "node:path"
2
1
  import { readFileSync } from "node:fs"
3
- import { getCommits, getProject, publishProject, sync, uploadBlob, type Project, type SyncFileInput, type SyncFolderInput } from "../api"
4
- import { loadConfig, requireApiUrl, requireToken } from "../config"
5
- import { findProjectRoot, readManifest, writeManifest, type FileKind, type Manifest, type ManifestFile } from "../manifest"
6
- import { classifyKind, diffLocal, hasChanges, isTextKind, scanProject } from "../localFiles"
7
- import { computeServerDiff } from "../serverDiff"
8
- import { buildPaths } from "../project"
9
- import { CliError, c, confirm, flagBool, flagStr, info, log, prompt, success, warn, type Args } from "../util"
2
+ import { basename } from "node:path"
3
+ import { CliError, bool, c, confirm, defineCommand, info, log, prompt, str, success, warn } from "../cli"
4
+ import { getCommits, getProject, publishProject, sync, uploadBlob, type Project, type SyncFileInput, type SyncFolderInput } from "../platform/api"
5
+ import { computeServerDiff } from "../platform/serverDiff"
6
+ import { classifyKind, diffLocal, hasChanges, isTextKind, scanProject } from "../project/localFiles"
7
+ import { writeManifest, type FileKind, type Manifest, type ManifestFile } from "../project/manifest"
8
+ import { buildPaths } from "../project/materialize"
9
+ import { linkedProject } from "./shared"
10
10
 
11
11
  /*
12
12
  * `lecodes push -m "message"` — diff the working tree against the manifest, upload changed binaries,
@@ -21,33 +21,42 @@ import { CliError, c, confirm, flagBool, flagStr, info, log, prompt, success, wa
21
21
  * arrives before the fix is a lie the reviewer has no way to detect. That ordering is why nothing
22
22
  * else in the toolchain sends a comment upward.
23
23
  */
24
- export const push = async (args: Args) => {
25
- const root = findProjectRoot(process.cwd())
26
- const manifest = readManifest(root)
27
- const config = loadConfig()
28
- const apiUrl = requireApiUrl(config)
29
- const token = requireToken(config)
30
-
31
- const publish = flagBool(args, "publish")
24
+
25
+ export const pushFlags = {
26
+ message: str("checkpoint message (asked for when omitted)", { alias: "m", value: "<text>" }),
27
+ publish: bool("also deploy the project live (compile) with the push"),
28
+ force: bool("push even if the server moved on (shows + confirms which server files get overwritten)"),
29
+ yes: bool("skip the --force overwrite confirmation", { alias: "y" }),
30
+ }
31
+
32
+ export type PushOptions = { message?: string, publish?: boolean, force?: boolean, yes?: boolean }
33
+
34
+ export default defineCommand({
35
+ name: "push",
36
+ summary: "Push local changes as one new checkpoint",
37
+ flags: pushFlags,
38
+ examples: ['lecodes push -m "Tweak the menu"', "lecodes push --publish"],
39
+ run: ({ flags }) => runPush(flags),
40
+ })
41
+
42
+ /** The push itself — `lecodes link --push` runs it too. */
43
+ export const runPush = async (opts: PushOptions): Promise<void> => {
44
+ const { root, manifest, apiUrl, token } = linkedProject()
45
+ const publish = !!opts.publish
32
46
  const scan = scanProject(root, manifest)
33
47
  const diff = diffLocal(manifest, scan)
34
48
  if (!hasChanges(diff)) {
35
- if (publish) {
36
- await publishProject(apiUrl, token, manifest.uuid)
37
- success("No file changes — published the current project state.")
38
- } else {
39
- info(c.dim("Working tree clean — nothing to push."))
40
- }
49
+ if (publish) { await publishProject(apiUrl, token, manifest.uuid); success("No file changes — published the current project state.") }
50
+ else info(c.dim("Working tree clean — nothing to push."))
41
51
  // Still flush: a queued answer must not be stuck behind "no files changed", which is exactly the
42
52
  // state you're in after resolving a comment about something you already pushed.
43
- await flushComments(args)
53
+ await flushComments()
44
54
  return
45
55
  }
46
56
 
47
- const force = flagBool(args, "force")
48
57
  // Concurrency guard: a push rewrites the server's WHOLE tree (set-tree), so anything that changed
49
58
  // on the server since we cloned/pulled would be clobbered. Two kinds of drift to catch:
50
- if (!force && manifest.baseHeadId !== null) {
59
+ if (!opts.force && manifest.baseHeadId !== null) {
51
60
  const commits = await getCommits(apiUrl, token, manifest.uuid)
52
61
  // (1) A new checkpoint was frozen on the server — that advances the head id.
53
62
  if (commits.headId !== manifest.baseHeadId) {
@@ -58,28 +67,24 @@ export const push = async (args: Args) => {
58
67
  // manifest (e.g. a resource was added in the editor) a blind push would silently DELETE it. Bail.
59
68
  if (commits.dirty) {
60
69
  const drift = serverPathDrift(await getProject(apiUrl, token, manifest.uuid), manifest)
61
- if (drift) {
62
- throw new CliError(`The project has uncommitted changes in the web editor your checkout doesn't have (${drift}). Run \`lecodes pull\` to take them, or \`lecodes push --force\` to overwrite the server.`)
63
- }
70
+ if (drift) throw new CliError(`The project has uncommitted changes in the web editor your checkout doesn't have (${drift}). Run \`lecodes pull\` to take them, or \`lecodes push --force\` to overwrite the server.`)
64
71
  }
65
72
  }
66
73
 
67
74
  // With --force we may clobber server-side edits. Show exactly what changes on the server and
68
75
  // confirm (interactive only; skip with --yes or when piped).
69
- if (force) {
76
+ if (opts.force) {
70
77
  const project = await getProject(apiUrl, token, manifest.uuid)
71
78
  const serverDiff = computeServerDiff(project, scan)
72
79
  if (serverDiff.length) {
73
80
  info(c.yellow(`--force will overwrite the server. ${serverDiff.length} file${serverDiff.length === 1 ? "" : "s"} change:`))
74
81
  const label: Record<string, string> = { added: c.green(" create "), removed: c.red(" delete "), modified: c.yellow(" update ") }
75
82
  for (const e of serverDiff) log(label[e.status] + e.path + (e.binary ? c.dim(" (binary)") : ""))
76
- if (process.stdin.isTTY && !flagBool(args, "y", "yes")) {
77
- if (!(await confirm("Overwrite the server with your local copy?", false))) { info(c.dim("Aborted.")); return }
78
- }
83
+ if (process.stdin.isTTY && !opts.yes && !(await confirm("Overwrite the server with your local copy?", false))) { info(c.dim("Aborted.")); return }
79
84
  }
80
85
  }
81
86
 
82
- const message = (flagStr(args, "m", "message") ?? await prompt("Checkpoint message")) || "Update from lecodes CLI"
87
+ const message = (opts.message ?? await prompt("Checkpoint message")) || "Update from lecodes CLI"
83
88
 
84
89
  // The identity source for a path: the same path in the manifest, else its move origin.
85
90
  const sourceFor = (path: string): ManifestFile | undefined =>
@@ -92,10 +97,8 @@ export const push = async (args: Args) => {
92
97
  const kind: FileKind = source?.type ?? classifyKind(basename(file.path))
93
98
  const entry: SyncFileInput = { path: file.path, type: kind }
94
99
  if (source) entry.assetId = source.id
95
-
96
- if (isTextKind(kind)) {
97
- entry.text = readFileSync(file.absPath, "utf8")
98
- } else if (!source || source.sha !== file.sha) {
100
+ if (isTextKind(kind)) entry.text = readFileSync(file.absPath, "utf8")
101
+ else if (!source || source.sha !== file.sha) {
99
102
  // New or changed binary — upload it and reference the fresh blob.
100
103
  const blob = await uploadBlob(apiUrl, token, manifest.uuid, basename(file.path), readFileSync(file.absPath))
101
104
  entry.blobFileId = blob.id
@@ -104,7 +107,6 @@ export const push = async (args: Args) => {
104
107
  // Else: unchanged binary — assetId alone tells the server to keep the existing blob.
105
108
  files.push(entry)
106
109
  }
107
-
108
110
  const folders: SyncFolderInput[] = scan.dirs.map(path => ({ path }))
109
111
 
110
112
  if (uploaded > 0) info(c.dim(`Uploaded ${uploaded} binary file${uploaded === 1 ? "" : "s"}.`))
@@ -118,9 +120,7 @@ export const push = async (args: Args) => {
118
120
  // Rebuild the manifest from the authoritative server tree + local hashes.
119
121
  const shaByPath = new Map(scan.files.map(f => [f.path, f.sha]))
120
122
  const newFiles: Record<string, ManifestFile> = {}
121
- for (const node of res.nodes) {
122
- newFiles[node.path] = { id: node.id, type: node.type as FileKind, sha: shaByPath.get(node.path) ?? "" }
123
- }
123
+ for (const node of res.nodes) newFiles[node.path] = { id: node.id, type: node.type as FileKind, sha: shaByPath.get(node.path) ?? "" }
124
124
  writeManifest(root, { ...manifest, baseHeadId: res.headId, files: newFiles })
125
125
 
126
126
  const counts = [
@@ -131,19 +131,17 @@ export const push = async (args: Args) => {
131
131
  ].filter(Boolean).join(", ")
132
132
  success(`Pushed checkpoint${res.seq ? ` #${res.seq}` : ""}${counts ? ` (${counts})` : ""}: ${message}`)
133
133
  if (publish) reportPublish(res)
134
- await flushComments(args)
134
+ await flushComments()
135
135
  }
136
136
 
137
137
  /** Send the design board's queued comment writes, now that the design they describe is up. Silent
138
138
  * when there's no design folder, no queue, or no login — this is a rider on the push, not a step
139
139
  * the user asked for. */
140
- const flushComments = async (args: Args) => {
140
+ const flushComments = async (): Promise<void> => {
141
141
  const { flushDesignComments } = await import("./design")
142
- const result = await flushDesignComments(args)
142
+ const result = await flushDesignComments()
143
143
  if (!result) return
144
- if (result.threads) {
145
- success(`Sent ${result.sent} comment update${result.sent === 1 ? "" : "s"} across ${result.threads} thread${result.threads === 1 ? "" : "s"}.`)
146
- }
144
+ if (result.threads) success(`Sent ${result.sent} comment update${result.sent === 1 ? "" : "s"} across ${result.threads} thread${result.threads === 1 ? "" : "s"}.`)
147
145
  for (const failure of result.failures) warn(`Comment not sent — ${failure} (still queued; the next push retries it).`)
148
146
  }
149
147
 
@@ -157,17 +155,13 @@ const serverPathDrift = (project: Project, manifest: Manifest): string | null =>
157
155
  const manifestPaths = new Set(Object.keys(manifest.files))
158
156
  const added = [...serverPaths].filter(p => !manifestPaths.has(p))
159
157
  const removed = [...manifestPaths].filter(p => !serverPaths.has(p))
160
- const describe = (label: string, paths: string[]) =>
161
- `${paths.length} ${label} on the server: ${paths.slice(0, 3).join(", ")}${paths.length > 3 ? ", …" : ""}`
162
- const parts = [
163
- added.length ? describe("added", added) : "",
164
- removed.length ? describe("removed", removed) : "",
165
- ].filter(Boolean)
158
+ const describe = (label: string, paths: string[]) => `${paths.length} ${label} on the server: ${paths.slice(0, 3).join(", ")}${paths.length > 3 ? ", …" : ""}`
159
+ const parts = [added.length ? describe("added", added) : "", removed.length ? describe("removed", removed) : ""].filter(Boolean)
166
160
  return parts.length ? parts.join("; ") : null
167
161
  }
168
162
 
169
163
  /** Report the outcome of a --publish deploy bundled with the push (best-effort on the server). */
170
- const reportPublish = (res: { published?: boolean, publishError?: string }) => {
164
+ const reportPublish = (res: { published?: boolean, publishError?: string }): void => {
171
165
  if (res.published) success("Published — the project is now live.")
172
166
  else warn(`Push landed, but publish failed: ${res.publishError ?? "unknown error"}`)
173
167
  }