lecodes-cli 2.3.1 → 2.4.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.
@@ -100,14 +100,21 @@ export interface AppConfig {
100
100
  /** 3D buffer cap in px, e.g. [1920, 1080]: on a bigger window/monitor the 3D renders at most
101
101
  * this size (aspect kept) — the per-machine answer to "fullscreen on a 4K display is slow". */
102
102
  maxRenderSize?: [number, number],
103
- /** Frame-rate CAP for the host's render loop: `"display"` (default — follow the screen's refresh
104
- * rate, 120 on a ProMotion Mac) or a number, meaning "at most this many fps" (the screen's
105
- * maximum still wins when it is lower — vsync is the real ceiling, so this can never be a
106
- * target above it). For an app that wants to spend less: a UI-heavy app on a laptop, a game
107
- * whose feel is tuned for 60. `desktop run` forwards it as CREATOR_FRAME_RATE (a set env wins);
108
- * a built .app's host reads it from the staged app.json. Honoured by the macOS host
109
- * (2026-09-09); the Windows/Linux host is vsync-locked and ignores it for now. */
103
+ /** Frame-rate CAP the host starts with: a number = "at most this many fps", `0` or `"display"`
104
+ * (the default) = no cap — the display's own rate, or, with vsync off, as fast as the host
105
+ * renders. The app changes it at runtime with `device.setFrameRate()`; this is the value in
106
+ * force before the bundle runs. For an app that wants to spend less: a UI-heavy app on a
107
+ * laptop, a game whose feel is tuned for 60. `desktop run` / `dev --desktop` forward it as
108
+ * CREATOR_FRAME_RATE (a set env wins); a built folder's host reads it from the staged app.json.
109
+ * Headless render/test ignore it (their frames are app-time steps). */
110
110
  frameRate?: number | "display",
111
+ /** Vertical sync the host starts with (default true). `false` presents without waiting for the
112
+ * display's refresh: the lowest input latency, tearing possible — and with no `frameRate` cap
113
+ * the loop renders as fast as it can, so set one unless you are measuring. The app switches it
114
+ * at runtime with `device.setVsync()`. Forwarded as CREATOR_VSYNC (a set env wins), read from
115
+ * the staged app.json by a built folder's host. Windows (GL: the swap interval; Vulkan: the
116
+ * immediate present mode); the macOS and Linux hosts ignore it for now. */
117
+ vsync?: boolean,
111
118
  /** The mouse button acts as a finger: a left click-drag scrolls + flings like touch (the wheel
112
119
  * always scrolls, clicks are unaffected; a button inside a scrollable then gets the touch-style
113
120
  * press feedback delay). For testing the touch experience without a touchscreen, or an app
@@ -93,8 +93,9 @@ const run = defineCommand({
93
93
  // from app.json only. Both are desktop-level (the machine/app), not window-level.
94
94
  renderScale: resolveRenderScale(flags["render-scale"], appCfg?.desktop?.renderScale),
95
95
  maxRenderSize: appCfg?.desktop?.maxRenderSize,
96
- // Frame-rate cap (app.json only — like renderScale it is a property of the app, not of a run).
96
+ // Frame-rate cap + vsync (app.json only — like renderScale they are properties of the app, not of a run).
97
97
  frameRate: appCfg?.desktop?.frameRate,
98
+ vsync: appCfg?.desktop?.vsync,
98
99
  mouseEmulateTouch: appCfg?.desktop?.mouseEmulateTouch,
99
100
  virtualKeyboard: appCfg?.desktop?.virtualKeyboard,
100
101
  // --fps: the host's own corner counter. A flag and not app.json on purpose — it is something you
@@ -300,6 +300,7 @@ export default defineCommand({
300
300
  scale: resolveScale(undefined, win?.scale),
301
301
  // The frame-rate cap is forwarded too: it changes how the game FEELS, and dev should match production.
302
302
  frameRate: appCfg?.desktop?.frameRate,
303
+ vsync: appCfg?.desktop?.vsync,
303
304
  fps: flags.fps,
304
305
  }).then(
305
306
  () => note("Desktop window closed — the dev server keeps running (Ctrl+C to stop)."),
@@ -348,9 +348,13 @@ export type DesktopWindowOpts = {
348
348
  * Filament's timer query, the CPU split, visible / total renderables, the view's switches — and the
349
349
  * fps overlay's second row. docs/profiling.md in the core repo. */
350
350
  perf?: boolean
351
- /** app.json desktop.frameRate — a CAP on the host's render loop, "display" or a number; forwarded
352
- * verbatim as CREATOR_FRAME_RATE (a set env wins). macOS host 2026-09-09; Windows ignores it. */
351
+ /** app.json desktop.frameRate — the CAP the host's loop starts with, "display" / 0 (none) or a
352
+ * number; forwarded verbatim as CREATOR_FRAME_RATE (a set env wins). The app moves it with
353
+ * device.setFrameRate(). */
353
354
  frameRate?: number | "display"
355
+ /** app.json desktop.vsync — whether the host starts with vertical sync (default true); forwarded
356
+ * as CREATOR_VSYNC=1/0 (a set env wins). The app moves it with device.setVsync(). */
357
+ vsync?: boolean
354
358
  /** Multiplayer launch facts for the host (`--server --port N --max-clients N` / `--connect ip:port`);
355
359
  * the SDK reads them through `Net.launch` and decides (docs/multiplayer-plan.md). */
356
360
  net?: { role: "server" | "client"; address?: string; port?: number; maxClients?: number }
@@ -388,6 +392,9 @@ const windowEnv = (opts: DesktopWindowOpts): Record<string, string> => {
388
392
  if (opts.frameRate !== undefined && process.env.CREATOR_FRAME_RATE === undefined) {
389
393
  env.CREATOR_FRAME_RATE = String(opts.frameRate)
390
394
  }
395
+ if (opts.vsync !== undefined && process.env.CREATOR_VSYNC === undefined) {
396
+ env.CREATOR_VSYNC = opts.vsync ? "1" : "0"
397
+ }
391
398
  if (opts.mouseEmulateTouch !== undefined && process.env.CREATOR_MOUSE_TOUCH === undefined) {
392
399
  env.CREATOR_MOUSE_TOUCH = opts.mouseEmulateTouch ? "1" : "0"
393
400
  }