lecodes-cli 0.20.2 → 1.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 (105) hide show
  1. package/README.md +56 -57
  2. package/dist/index.js +5918 -5020
  3. package/package.json +7 -5
  4. package/runtime/materials/decal-relief.mat +12 -1
  5. package/runtime/materials/decal.mat +17 -3
  6. package/runtime/materials/lightmap-baked-lite.mat +6 -1
  7. package/runtime/materials/lightmap-baked.mat +6 -1
  8. package/runtime/materials/lightmap.mat +3 -0
  9. package/runtime/scene-harness.json +1 -1
  10. package/runtime/sdk-types.json +1 -1
  11. package/runtime/web/assets/createViewerLite-DHqTFhWB.js +866 -0
  12. package/runtime/web/assets/{index-BMt7AnC5.js → index-Q06c6oHx.js} +1 -1
  13. package/runtime/web/embed.js +1 -1
  14. package/src/api.ts +1 -302
  15. package/src/cli/args.ts +37 -0
  16. package/src/cli/command.ts +113 -0
  17. package/src/cli/errors.ts +13 -0
  18. package/src/cli/help.ts +133 -0
  19. package/src/cli/index.ts +7 -0
  20. package/src/cli/output.ts +102 -0
  21. package/src/cli/run.ts +106 -0
  22. package/src/commands/{appAndroid.ts → app/android.ts} +451 -42
  23. package/src/commands/{appDesktop.ts → app/desktop.ts} +63 -32
  24. package/src/commands/{appDesktopMac.ts → app/desktopMac.ts} +24 -18
  25. package/src/commands/{appIcon.ts → app/icon.ts} +2 -2
  26. package/src/commands/{app.ts → app/index.ts} +397 -289
  27. package/src/commands/{appShared.ts → app/shared.ts} +30 -39
  28. package/src/commands/{appTemplatesAndroid.ts → app/templates/android.ts} +62 -28
  29. package/src/commands/{appTemplatesGradlew.ts → app/templates/gradlew.ts} +1 -1
  30. package/src/commands/{appTemplates.ts → app/templates/ios.ts} +1 -1
  31. package/src/commands/assets.ts +23 -21
  32. package/src/commands/clone.ts +48 -51
  33. package/src/commands/compile.ts +62 -88
  34. package/src/commands/create.ts +46 -47
  35. package/src/commands/design/comments.ts +361 -0
  36. package/src/commands/design/context.ts +101 -0
  37. package/src/commands/design/index.ts +391 -0
  38. package/src/commands/design/snapshot.ts +134 -0
  39. package/src/commands/{designTemplates.ts → design/templates.ts} +4 -3
  40. package/src/commands/desktop.ts +90 -82
  41. package/src/commands/dev.ts +201 -176
  42. package/src/commands/diff.ts +39 -51
  43. package/src/commands/index.ts +60 -0
  44. package/src/commands/{init.ts → init/index.ts} +199 -191
  45. package/src/commands/install.ts +80 -67
  46. package/src/commands/lightmap.ts +203 -171
  47. package/src/commands/link.ts +87 -94
  48. package/src/commands/login.ts +25 -23
  49. package/src/commands/navmesh.ts +168 -138
  50. package/src/commands/pn.ts +201 -244
  51. package/src/commands/pull.ts +49 -56
  52. package/src/commands/push.ts +48 -54
  53. package/src/commands/render.ts +158 -99
  54. package/src/commands/scene.ts +58 -55
  55. package/src/commands/{shaders.ts → shaders/index.ts} +61 -42
  56. package/src/commands/{shadersNew.ts → shaders/new.ts} +2 -2
  57. package/src/commands/shared.ts +79 -0
  58. package/src/commands/status.ts +22 -26
  59. package/src/commands/test.ts +398 -371
  60. package/src/commands/thumbs.ts +185 -175
  61. package/src/commands/update/index.ts +226 -0
  62. package/src/commands/{types.ts → update/types.ts} +31 -22
  63. package/src/compile/collect.ts +5 -5
  64. package/src/compile/collectLocal.ts +3 -3
  65. package/src/compile/designCompile.ts +3 -3
  66. package/src/compile/headlessBundle.ts +133 -129
  67. package/src/compile/projectCompile.ts +2 -2
  68. package/src/compile/sceneCompile.ts +10 -8
  69. package/src/compile/screenEntry.ts +123 -127
  70. package/src/compile/shaders.ts +3 -3
  71. package/src/dev/androidDev.ts +1 -1
  72. package/src/dev/devServer.ts +3 -3
  73. package/src/dev/webRunner.ts +1 -1
  74. package/src/{cmgenTool.ts → hosts/cmgenTool.ts} +1 -1
  75. package/src/{desktopRenderer.ts → hosts/desktopRenderer.ts} +6 -5
  76. package/src/{desktopScript.ts → hosts/desktopScript.ts} +1 -1
  77. package/src/{distRoot.ts → hosts/distRoot.ts} +9 -5
  78. package/src/{matcTool.ts → hosts/matcTool.ts} +1 -1
  79. package/src/{peerInstall.ts → hosts/peerInstall.ts} +2 -2
  80. package/src/{releases.ts → hosts/releases.ts} +1 -1
  81. package/src/index.ts +32 -480
  82. package/src/platform/api.ts +302 -0
  83. package/src/{browserAuth.ts → platform/browserAuth.ts} +1 -1
  84. package/src/{config.ts → platform/config.ts} +1 -1
  85. package/src/{serverDiff.ts → platform/serverDiff.ts} +3 -3
  86. package/src/{projectEnv.ts → project/env.ts} +33 -2
  87. package/src/{localFiles.ts → project/localFiles.ts} +1 -1
  88. package/src/{manifest.ts → project/manifest.ts} +1 -1
  89. package/src/{project.ts → project/materialize.ts} +2 -2
  90. package/src/project/paths.ts +20 -0
  91. package/src/{textDiff.ts → project/textDiff.ts} +1 -1
  92. package/src/{types.ts → project/types.ts} +0 -0
  93. package/runtime/web/assets/createViewerLite-Ct_PZdof.js +0 -852
  94. package/src/commands/design.ts +0 -846
  95. package/src/commands/update.ts +0 -211
  96. package/src/util.ts +0 -146
  97. /package/src/commands/{projectTemplates.ts → init/templates.ts} +0 -0
  98. /package/src/{lecodes-3d-editor.d.ts → declarations/lecodes-3d-editor.d.ts} +0 -0
  99. /package/src/{lecodes-assets.d.ts → declarations/lecodes-assets.d.ts} +0 -0
  100. /package/src/{lecodes-design.d.ts → declarations/lecodes-design.d.ts} +0 -0
  101. /package/src/{lecodes-renderer.d.ts → declarations/lecodes-renderer.d.ts} +0 -0
  102. /package/src/{qrcode-terminal.d.ts → declarations/qrcode-terminal.d.ts} +0 -0
  103. /package/src/{peers.ts → hosts/peers.ts} +0 -0
  104. /package/src/{designMeta.ts → project/designMeta.ts} +0 -0
  105. /package/src/{ignore.ts → project/ignore.ts} +0 -0
@@ -1,26 +1,19 @@
1
1
  import { basename } from "node:path"
2
- import { CliError, c, flagBool, flagStr, log, note, type Args } from "../util"
3
- import { findProjectRoot } from "../manifest"
4
- import { buildBundle } from "./compile"
5
- import { assertRendererSupportsBundle, desktopPluginLibraries, readAppConfigAt, resolveDesktopRenderer } from "./appShared"
6
- import { buildDesktop } from "./appDesktop"
2
+ import { CliError, bool, c, defineCommand, log, note, num, str } from "../cli"
7
3
  import {
8
- DESKTOP_RENDERER_VERSION,
9
- fetchDesktopManifest,
10
- findMacHostExe,
11
- installedDesktopVersions,
12
- runDesktopApp,
13
- selectedDesktopVersion,
14
- semverNewer,
15
- updateDesktop,
16
- } from "../desktopRenderer"
4
+ DESKTOP_RENDERER_VERSION, fetchDesktopManifest, findMacHostExe, installedDesktopVersions, runDesktopApp,
5
+ selectedDesktopVersion, semverNewer, updateDesktop,
6
+ } from "../hosts/desktopRenderer"
7
+ import { desktopBuild } from "./app/desktop"
8
+ import { desktopPluginLibraries, readAppConfigAt, resolveDesktopRenderer } from "./app/shared"
9
+ import { buildBundle } from "./compile"
10
+ import { bundleFlags, bundleOptions, localProject } from "./shared"
17
11
 
18
12
  /*
19
- * `lecodes desktop` — everything about the desktop target in one place.
13
+ * `lecodes desktop` — the desktop target, the host `lecodes render --desktop` uses too.
20
14
  * (no args) status: the version renders use, what's cached, what's published
21
15
  * run compile the current project and open it in a real desktop window
22
- * build stage a portable app folder (the same command as `lecodes app build desktop`,
23
- * which stays for symmetry with the ios/android shells)
16
+ * build stage a portable app folder (the same as `lecodes app build desktop`)
24
17
  * update move the host to the newest published release (downloads it if needed)
25
18
  * The binary cache lives in ~/.lecodes/renderer/; LECODES_DESKTOP_EXE overrides everything.
26
19
  */
@@ -32,9 +25,7 @@ export const resolveScale = (flag: string | undefined, cfg: number | "fit" | "sy
32
25
  if (raw === undefined || raw === "system") return undefined
33
26
  if (raw === "fit") return "fit"
34
27
  const n = Number(raw)
35
- if (!Number.isFinite(n) || n < 0.5 || n > 4) {
36
- throw new CliError(`scale must be a number 0.5..4, "fit" or "system" (got "${raw}")`)
37
- }
28
+ if (!Number.isFinite(n) || n < 0.5 || n > 4) throw new CliError(`scale must be a number 0.5..4, "fit" or "system" (got "${raw}")`)
38
29
  return n
39
30
  }
40
31
 
@@ -46,55 +37,71 @@ export const resolveRenderScale = (flag: string | undefined, cfg: number | undef
46
37
  return n
47
38
  }
48
39
 
49
- const run = async (args: Args) => {
50
- // Window defaults come from app.json's desktop block (flags win); standalone projects
51
- // without an app.json keep the host's built-in 1280×720.
52
- let root: string
53
- try { root = findProjectRoot(process.cwd()) } catch { root = process.cwd() }
54
- const appCfg = readAppConfigAt(root)
55
- const win = appCfg?.desktop?.window
56
- // ONE renderer decision per run: it picks the host binary AND which .filamat gets staged beside
57
- // the bundle. Reading the config twice is how a Vulkan exe ends up next to an OpenGL shader.
58
- const renderer = resolveDesktopRenderer(root, args)
59
- const { js, resources } = await buildBundle(args, undefined, { localAssets: true, desktopRenderer: renderer })
60
- assertRendererSupportsBundle(renderer, js)
61
- // Native plugins: the project's plugins/ dir + declared plugin libraries, scanned by the host.
62
- const { dirs: pluginDirs, files: pluginFiles } = desktopPluginLibraries(root)
63
- if (pluginFiles.length > 0) note(`Native plugins: ${pluginFiles.map((f) => basename(f)).join(", ")}`)
64
- note(`Launching LeCodes Desktop${renderer === "vulkan" ? " (Vulkan)" : ""}… (close the window to return)`)
65
- await runDesktopApp(js, resources, {
66
- renderer,
67
- pluginDirs,
68
- width: Number(flagStr(args, "width")) || win?.width,
69
- height: Number(flagStr(args, "height")) || win?.height,
70
- minWidth: win?.minWidth,
71
- minHeight: win?.minHeight,
72
- resizable: win?.resizable,
73
- title: flagStr(args, "title") ?? win?.title ?? appCfg?.name,
74
- storageScope: appCfg?.name,
75
- // --fullscreen forces it on; --windowed forces it off (over app.json); else the config decides.
76
- fullscreen: flagBool(args, "fullscreen") ? true : flagBool(args, "windowed") ? false : win?.fullscreen,
77
- scale: resolveScale(flagStr(args, "scale"), win?.scale),
78
- // 3D render resolution: --render-scale wins over app.json desktop.renderScale; the cap comes
79
- // from app.json only. Both are desktop-level (the machine/app), not window-level.
80
- renderScale: resolveRenderScale(flagStr(args, "render-scale"), appCfg?.desktop?.renderScale),
81
- maxRenderSize: appCfg?.desktop?.maxRenderSize,
82
- mouseEmulateTouch: appCfg?.desktop?.mouseEmulateTouch,
83
- virtualKeyboard: appCfg?.desktop?.virtualKeyboard,
84
- // --fps: the host's own corner counter. A flag and not app.json on purpose — it is something you
85
- // switch on while looking at a frame, not a property of the app.
86
- fps: flagBool(args, "fps"),
87
- // Multiplayer (docs/multiplayer-plan.md): --server [--port N] [--max-clients N] starts the host as a
88
- // dedicated server; --connect ip:port joins one. Both only set Net.launch — the app calls Net.listen/connect.
89
- net: flagBool(args, "server")
90
- ? { role: "server", port: Number(flagStr(args, "port")) || undefined, maxClients: Number(flagStr(args, "max-clients")) || undefined }
91
- : flagStr(args, "connect")
92
- ? { role: "client", address: flagStr(args, "connect") }
93
- : undefined,
94
- })
95
- }
40
+ const run = defineCommand({
41
+ name: "run",
42
+ summary: "Compile the current project and open it in a desktop window",
43
+ description: "Window size / min size / title default from app.json's desktop block; asset() resources load from the working tree. Multiplayer: --server starts the host as a dedicated server, --connect joins one — both only set Net.launch (the app decides).",
44
+ flags: {
45
+ width: num("window width (logical px)"),
46
+ height: num("window height (logical px)"),
47
+ title: str("window title", { value: "<s>" }),
48
+ fullscreen: bool("force fullscreen (over app.json)"),
49
+ windowed: bool("force windowed (over app.json)"),
50
+ scale: str('display scale: 0.5..4, "fit" (size a 1080p-designed app to the monitor), or "system"', { value: "<n|fit>" }),
51
+ "render-scale": str("3D render resolution 0.25..1 (over app.json desktop.renderScale)", { value: "<n>" }),
52
+ renderer: str("host + shader variant: gl | vulkan (default: app.json desktop.renderer, else gl)", { value: "<gl|vulkan>" }),
53
+ fps: bool("draw the host's frame counter in the corner (composited fps + the 3D fps when they diverge)"),
54
+ server: bool("launch as a multiplayer server"),
55
+ port: num("(--server) listen port"),
56
+ "max-clients": num("(--server) client cap"),
57
+ connect: str("join a server as a client", { value: "<ip:port>" }),
58
+ ...bundleFlags,
59
+ },
60
+ examples: ["lecodes desktop run", "lecodes desktop run --scale fit --fps", "lecodes desktop run --server --port 7777"],
61
+ run: async ({ flags, raw }) => {
62
+ // Window defaults come from app.json's desktop block (flags win); standalone projects
63
+ // without an app.json keep the host's built-in 1280×720.
64
+ const { root } = localProject()
65
+ const appCfg = readAppConfigAt(root)
66
+ const win = appCfg?.desktop?.window
67
+ // ONE renderer decision per run: it picks the host binary AND which .filamat gets staged beside
68
+ // the bundle. Reading the config twice is how a Vulkan exe ends up next to an OpenGL shader.
69
+ const renderer = resolveDesktopRenderer(root, flags.renderer)
70
+ const { js, resources } = await buildBundle(bundleOptions(flags), { localAssets: true, desktopRenderer: renderer })
71
+ // Native plugins: the project's plugins/ dir + declared plugin libraries, scanned by the host.
72
+ const { dirs: pluginDirs, files: pluginFiles } = desktopPluginLibraries(root)
73
+ if (pluginFiles.length > 0) note(`Native plugins: ${pluginFiles.map((f) => basename(f)).join(", ")}`)
74
+ note(`Launching LeCodes Desktop${renderer === "vulkan" ? " (Vulkan)" : ""}… (close the window to return)`)
75
+ await runDesktopApp(js, resources, {
76
+ renderer,
77
+ pluginDirs,
78
+ width: flags.width || win?.width,
79
+ height: flags.height || win?.height,
80
+ minWidth: win?.minWidth,
81
+ minHeight: win?.minHeight,
82
+ resizable: win?.resizable,
83
+ title: flags.title ?? win?.title ?? appCfg?.name,
84
+ storageScope: appCfg?.name,
85
+ // --fullscreen forces it on; --windowed forces it off (over app.json); else the config decides.
86
+ fullscreen: flags.fullscreen ? true : flags.windowed ? false : win?.fullscreen,
87
+ scale: resolveScale(flags.scale, win?.scale),
88
+ // 3D render resolution: --render-scale wins over app.json desktop.renderScale; the cap comes
89
+ // from app.json only. Both are desktop-level (the machine/app), not window-level.
90
+ renderScale: resolveRenderScale(flags["render-scale"], appCfg?.desktop?.renderScale),
91
+ maxRenderSize: appCfg?.desktop?.maxRenderSize,
92
+ mouseEmulateTouch: appCfg?.desktop?.mouseEmulateTouch,
93
+ virtualKeyboard: appCfg?.desktop?.virtualKeyboard,
94
+ // --fps: the host's own corner counter. A flag and not app.json on purpose — it is something you
95
+ // switch on while looking at a frame, not a property of the app.
96
+ fps: flags.fps,
97
+ net: flags.server
98
+ ? { role: "server", port: flags.port, maxClients: flags["max-clients"] }
99
+ : flags.connect ? { role: "client", address: flags.connect } : undefined,
100
+ })
101
+ },
102
+ })
96
103
 
97
- const status = async () => {
104
+ const status = async (): Promise<void> => {
98
105
  const selected = selectedDesktopVersion()
99
106
  const installed = installedDesktopVersions()
100
107
  log(`renders with ${selected}${installed.includes(selected) ? "" : c.dim(" (not downloaded yet — fetched on first render)")}`)
@@ -103,28 +110,29 @@ const status = async () => {
103
110
  if (process.env.LECODES_DESKTOP_EXE) log(`override LECODES_DESKTOP_EXE=${process.env.LECODES_DESKTOP_EXE}`)
104
111
  if (process.platform === "darwin") {
105
112
  // The cache is what runs/builds use; a hand-installed app only steps in when the manifest is unreachable.
106
- const installed = findMacHostExe()
107
- log(`installed ${installed ?? c.dim("none")} ${c.dim("(offline fallback only)")}`)
113
+ const mac = findMacHostExe()
114
+ log(`installed ${mac ?? c.dim("none")} ${c.dim("(offline fallback only)")}`)
108
115
  }
109
116
  try {
110
117
  const { latest } = await fetchDesktopManifest()
111
118
  const hint = semverNewer(selected, latest) > 0
112
119
  ? c.yellow("(newer — run `lecodes desktop update`)")
113
- : latest === selected
114
- ? c.dim("(up to date)")
115
- : c.dim("(older than the CLI's minimum — this desktop release isn't published yet)")
120
+ : latest === selected ? c.dim("(up to date)") : c.dim("(older than the CLI's minimum — this desktop release isn't published yet)")
116
121
  log(`published ${latest} ${hint}`)
117
122
  } catch {
118
123
  log(`published ${c.dim("unknown (couldn't reach the release manifest)")}`)
119
124
  }
120
125
  }
121
126
 
122
- export const desktop = async (args: Args) => {
123
- const sub = args._[0]
124
- if (sub === undefined) return status()
125
- if (sub === "run") return run(args)
126
- // `buildDesktop` reads only flags, never positionals, so it takes both spellings unchanged.
127
- if (sub === "build") return buildDesktop(args)
128
- if (sub === "update") return updateDesktop()
129
- throw new CliError(`Unknown subcommand "desktop ${sub}". Use \`lecodes desktop\` (status), \`lecodes desktop run\`, \`lecodes desktop build\` or \`lecodes desktop update\`.`)
130
- }
127
+ export default defineCommand({
128
+ name: "desktop",
129
+ summary: "The desktop host: status, run the project in a window, build a portable app, update",
130
+ description: "Bare `lecodes desktop` prints the version status: the host renders use, what is cached in ~/.lecodes/renderer, the newest published one. LECODES_DESKTOP_EXE points every desktop command at a local host build instead.",
131
+ commands: [
132
+ run,
133
+ // `buildDesktop` is shared with `lecodes app build desktop`; its flags live there.
134
+ desktopBuild,
135
+ defineCommand({ name: "update", summary: "Move the host to the newest published release", run: () => updateDesktop() }),
136
+ ],
137
+ run: () => status(),
138
+ })