@kohryan/moodui 0.0.8 → 0.0.9

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/dist/cli.js CHANGED
@@ -23,8 +23,525 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
25
  // src/cli.ts
26
- var import_promises = __toESM(require("fs/promises"));
27
- var import_node_path = __toESM(require("path"));
26
+ var import_promises3 = __toESM(require("fs/promises"));
27
+ var import_node_path2 = __toESM(require("path"));
28
+ var import_node_url2 = require("url");
29
+ var import_node_http = __toESM(require("http"));
30
+
31
+ // node_modules/open/index.js
32
+ var import_node_process6 = __toESM(require("process"), 1);
33
+ var import_node_buffer = require("buffer");
34
+ var import_node_path = __toESM(require("path"), 1);
35
+ var import_node_url = require("url");
36
+ var import_node_util5 = require("util");
37
+ var import_node_child_process5 = __toESM(require("child_process"), 1);
38
+ var import_promises2 = __toESM(require("fs/promises"), 1);
39
+
40
+ // node_modules/wsl-utils/index.js
41
+ var import_node_process2 = __toESM(require("process"), 1);
42
+ var import_promises = __toESM(require("fs/promises"), 1);
43
+
44
+ // node_modules/is-wsl/index.js
45
+ var import_node_process = __toESM(require("process"), 1);
46
+ var import_node_os = __toESM(require("os"), 1);
47
+ var import_node_fs3 = __toESM(require("fs"), 1);
48
+
49
+ // node_modules/is-inside-container/index.js
50
+ var import_node_fs2 = __toESM(require("fs"), 1);
51
+
52
+ // node_modules/is-docker/index.js
53
+ var import_node_fs = __toESM(require("fs"), 1);
54
+ var isDockerCached;
55
+ function hasDockerEnv() {
56
+ try {
57
+ import_node_fs.default.statSync("/.dockerenv");
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+ function hasDockerCGroup() {
64
+ try {
65
+ return import_node_fs.default.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+ function isDocker() {
71
+ if (isDockerCached === void 0) {
72
+ isDockerCached = hasDockerEnv() || hasDockerCGroup();
73
+ }
74
+ return isDockerCached;
75
+ }
76
+
77
+ // node_modules/is-inside-container/index.js
78
+ var cachedResult;
79
+ var hasContainerEnv = () => {
80
+ try {
81
+ import_node_fs2.default.statSync("/run/.containerenv");
82
+ return true;
83
+ } catch {
84
+ return false;
85
+ }
86
+ };
87
+ function isInsideContainer() {
88
+ if (cachedResult === void 0) {
89
+ cachedResult = hasContainerEnv() || isDocker();
90
+ }
91
+ return cachedResult;
92
+ }
93
+
94
+ // node_modules/is-wsl/index.js
95
+ var isWsl = () => {
96
+ if (import_node_process.default.platform !== "linux") {
97
+ return false;
98
+ }
99
+ if (import_node_os.default.release().toLowerCase().includes("microsoft")) {
100
+ if (isInsideContainer()) {
101
+ return false;
102
+ }
103
+ return true;
104
+ }
105
+ try {
106
+ if (import_node_fs3.default.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
107
+ return !isInsideContainer();
108
+ }
109
+ } catch {
110
+ }
111
+ if (import_node_fs3.default.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || import_node_fs3.default.existsSync("/run/WSL")) {
112
+ return !isInsideContainer();
113
+ }
114
+ return false;
115
+ };
116
+ var is_wsl_default = import_node_process.default.env.__IS_WSL_TEST__ ? isWsl : isWsl();
117
+
118
+ // node_modules/wsl-utils/index.js
119
+ var wslDrivesMountPoint = /* @__PURE__ */ (() => {
120
+ const defaultMountPoint = "/mnt/";
121
+ let mountPoint;
122
+ return async function() {
123
+ if (mountPoint) {
124
+ return mountPoint;
125
+ }
126
+ const configFilePath = "/etc/wsl.conf";
127
+ let isConfigFileExists = false;
128
+ try {
129
+ await import_promises.default.access(configFilePath, import_promises.constants.F_OK);
130
+ isConfigFileExists = true;
131
+ } catch {
132
+ }
133
+ if (!isConfigFileExists) {
134
+ return defaultMountPoint;
135
+ }
136
+ const configContent = await import_promises.default.readFile(configFilePath, { encoding: "utf8" });
137
+ const configMountPoint = /(?<!#.*)root\s*=\s*(?<mountPoint>.*)/g.exec(configContent);
138
+ if (!configMountPoint) {
139
+ return defaultMountPoint;
140
+ }
141
+ mountPoint = configMountPoint.groups.mountPoint.trim();
142
+ mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
143
+ return mountPoint;
144
+ };
145
+ })();
146
+ var powerShellPathFromWsl = async () => {
147
+ const mountPoint = await wslDrivesMountPoint();
148
+ return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
149
+ };
150
+ var powerShellPath = async () => {
151
+ if (is_wsl_default) {
152
+ return powerShellPathFromWsl();
153
+ }
154
+ return `${import_node_process2.default.env.SYSTEMROOT || import_node_process2.default.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
155
+ };
156
+
157
+ // node_modules/define-lazy-prop/index.js
158
+ function defineLazyProperty(object, propertyName, valueGetter) {
159
+ const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
160
+ Object.defineProperty(object, propertyName, {
161
+ configurable: true,
162
+ enumerable: true,
163
+ get() {
164
+ const result = valueGetter();
165
+ define(result);
166
+ return result;
167
+ },
168
+ set(value) {
169
+ define(value);
170
+ }
171
+ });
172
+ return object;
173
+ }
174
+
175
+ // node_modules/default-browser/index.js
176
+ var import_node_util4 = require("util");
177
+ var import_node_process5 = __toESM(require("process"), 1);
178
+ var import_node_child_process4 = require("child_process");
179
+
180
+ // node_modules/default-browser-id/index.js
181
+ var import_node_util = require("util");
182
+ var import_node_process3 = __toESM(require("process"), 1);
183
+ var import_node_child_process = require("child_process");
184
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
185
+ async function defaultBrowserId() {
186
+ if (import_node_process3.default.platform !== "darwin") {
187
+ throw new Error("macOS only");
188
+ }
189
+ const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
190
+ const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
191
+ const browserId = match?.groups.id ?? "com.apple.Safari";
192
+ if (browserId === "com.apple.safari") {
193
+ return "com.apple.Safari";
194
+ }
195
+ return browserId;
196
+ }
197
+
198
+ // node_modules/run-applescript/index.js
199
+ var import_node_process4 = __toESM(require("process"), 1);
200
+ var import_node_util2 = require("util");
201
+ var import_node_child_process2 = require("child_process");
202
+ var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
203
+ async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
204
+ if (import_node_process4.default.platform !== "darwin") {
205
+ throw new Error("macOS only");
206
+ }
207
+ const outputArguments = humanReadableOutput ? [] : ["-ss"];
208
+ const execOptions = {};
209
+ if (signal) {
210
+ execOptions.signal = signal;
211
+ }
212
+ const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
213
+ return stdout.trim();
214
+ }
215
+
216
+ // node_modules/bundle-name/index.js
217
+ async function bundleName(bundleId) {
218
+ return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
219
+ tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
220
+ }
221
+
222
+ // node_modules/default-browser/windows.js
223
+ var import_node_util3 = require("util");
224
+ var import_node_child_process3 = require("child_process");
225
+ var execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
226
+ var windowsBrowserProgIds = {
227
+ MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
228
+ // The missing `L` is correct.
229
+ MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
230
+ MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
231
+ AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
232
+ ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
233
+ ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
234
+ ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
235
+ ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
236
+ BraveHTML: { name: "Brave", id: "com.brave.Browser" },
237
+ BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
238
+ BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
239
+ BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
240
+ FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
241
+ OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
242
+ VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
243
+ "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
244
+ };
245
+ var _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
246
+ var UnknownBrowserError = class extends Error {
247
+ };
248
+ async function defaultBrowser(_execFileAsync = execFileAsync3) {
249
+ const { stdout } = await _execFileAsync("reg", [
250
+ "QUERY",
251
+ " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
252
+ "/v",
253
+ "ProgId"
254
+ ]);
255
+ const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
256
+ if (!match) {
257
+ throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
258
+ }
259
+ const { id } = match.groups;
260
+ const dotIndex = id.lastIndexOf(".");
261
+ const hyphenIndex = id.lastIndexOf("-");
262
+ const baseIdByDot = dotIndex === -1 ? void 0 : id.slice(0, dotIndex);
263
+ const baseIdByHyphen = hyphenIndex === -1 ? void 0 : id.slice(0, hyphenIndex);
264
+ return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? { name: id, id };
265
+ }
266
+
267
+ // node_modules/default-browser/index.js
268
+ var execFileAsync4 = (0, import_node_util4.promisify)(import_node_child_process4.execFile);
269
+ var titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
270
+ async function defaultBrowser2() {
271
+ if (import_node_process5.default.platform === "darwin") {
272
+ const id = await defaultBrowserId();
273
+ const name = await bundleName(id);
274
+ return { name, id };
275
+ }
276
+ if (import_node_process5.default.platform === "linux") {
277
+ const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
278
+ const id = stdout.trim();
279
+ const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
280
+ return { name, id };
281
+ }
282
+ if (import_node_process5.default.platform === "win32") {
283
+ return defaultBrowser();
284
+ }
285
+ throw new Error("Only macOS, Linux, and Windows are supported");
286
+ }
287
+
288
+ // node_modules/open/index.js
289
+ var import_meta = {};
290
+ var execFile5 = (0, import_node_util5.promisify)(import_node_child_process5.default.execFile);
291
+ var __dirname = import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
292
+ var localXdgOpenPath = import_node_path.default.join(__dirname, "xdg-open");
293
+ var { platform, arch } = import_node_process6.default;
294
+ async function getWindowsDefaultBrowserFromWsl() {
295
+ const powershellPath = await powerShellPath();
296
+ const rawCommand = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
297
+ const encodedCommand = import_node_buffer.Buffer.from(rawCommand, "utf16le").toString("base64");
298
+ const { stdout } = await execFile5(
299
+ powershellPath,
300
+ [
301
+ "-NoProfile",
302
+ "-NonInteractive",
303
+ "-ExecutionPolicy",
304
+ "Bypass",
305
+ "-EncodedCommand",
306
+ encodedCommand
307
+ ],
308
+ { encoding: "utf8" }
309
+ );
310
+ const progId = stdout.trim();
311
+ const browserMap = {
312
+ ChromeHTML: "com.google.chrome",
313
+ BraveHTML: "com.brave.Browser",
314
+ MSEdgeHTM: "com.microsoft.edge",
315
+ FirefoxURL: "org.mozilla.firefox"
316
+ };
317
+ return browserMap[progId] ? { id: browserMap[progId] } : {};
318
+ }
319
+ var pTryEach = async (array, mapper) => {
320
+ let latestError;
321
+ for (const item of array) {
322
+ try {
323
+ return await mapper(item);
324
+ } catch (error) {
325
+ latestError = error;
326
+ }
327
+ }
328
+ throw latestError;
329
+ };
330
+ var baseOpen = async (options) => {
331
+ options = {
332
+ wait: false,
333
+ background: false,
334
+ newInstance: false,
335
+ allowNonzeroExitCode: false,
336
+ ...options
337
+ };
338
+ if (Array.isArray(options.app)) {
339
+ return pTryEach(options.app, (singleApp) => baseOpen({
340
+ ...options,
341
+ app: singleApp
342
+ }));
343
+ }
344
+ let { name: app, arguments: appArguments = [] } = options.app ?? {};
345
+ appArguments = [...appArguments];
346
+ if (Array.isArray(app)) {
347
+ return pTryEach(app, (appName) => baseOpen({
348
+ ...options,
349
+ app: {
350
+ name: appName,
351
+ arguments: appArguments
352
+ }
353
+ }));
354
+ }
355
+ if (app === "browser" || app === "browserPrivate") {
356
+ const ids = {
357
+ "com.google.chrome": "chrome",
358
+ "google-chrome.desktop": "chrome",
359
+ "com.brave.Browser": "brave",
360
+ "org.mozilla.firefox": "firefox",
361
+ "firefox.desktop": "firefox",
362
+ "com.microsoft.msedge": "edge",
363
+ "com.microsoft.edge": "edge",
364
+ "com.microsoft.edgemac": "edge",
365
+ "microsoft-edge.desktop": "edge"
366
+ };
367
+ const flags = {
368
+ chrome: "--incognito",
369
+ brave: "--incognito",
370
+ firefox: "--private-window",
371
+ edge: "--inPrivate"
372
+ };
373
+ const browser = is_wsl_default ? await getWindowsDefaultBrowserFromWsl() : await defaultBrowser2();
374
+ if (browser.id in ids) {
375
+ const browserName = ids[browser.id];
376
+ if (app === "browserPrivate") {
377
+ appArguments.push(flags[browserName]);
378
+ }
379
+ return baseOpen({
380
+ ...options,
381
+ app: {
382
+ name: apps[browserName],
383
+ arguments: appArguments
384
+ }
385
+ });
386
+ }
387
+ throw new Error(`${browser.name} is not supported as a default browser`);
388
+ }
389
+ let command;
390
+ const cliArguments = [];
391
+ const childProcessOptions = {};
392
+ if (platform === "darwin") {
393
+ command = "open";
394
+ if (options.wait) {
395
+ cliArguments.push("--wait-apps");
396
+ }
397
+ if (options.background) {
398
+ cliArguments.push("--background");
399
+ }
400
+ if (options.newInstance) {
401
+ cliArguments.push("--new");
402
+ }
403
+ if (app) {
404
+ cliArguments.push("-a", app);
405
+ }
406
+ } else if (platform === "win32" || is_wsl_default && !isInsideContainer() && !app) {
407
+ command = await powerShellPath();
408
+ cliArguments.push(
409
+ "-NoProfile",
410
+ "-NonInteractive",
411
+ "-ExecutionPolicy",
412
+ "Bypass",
413
+ "-EncodedCommand"
414
+ );
415
+ if (!is_wsl_default) {
416
+ childProcessOptions.windowsVerbatimArguments = true;
417
+ }
418
+ const encodedArguments = ["Start"];
419
+ if (options.wait) {
420
+ encodedArguments.push("-Wait");
421
+ }
422
+ if (app) {
423
+ encodedArguments.push(`"\`"${app}\`""`);
424
+ if (options.target) {
425
+ appArguments.push(options.target);
426
+ }
427
+ } else if (options.target) {
428
+ encodedArguments.push(`"${options.target}"`);
429
+ }
430
+ if (appArguments.length > 0) {
431
+ appArguments = appArguments.map((argument) => `"\`"${argument}\`""`);
432
+ encodedArguments.push("-ArgumentList", appArguments.join(","));
433
+ }
434
+ options.target = import_node_buffer.Buffer.from(encodedArguments.join(" "), "utf16le").toString("base64");
435
+ } else {
436
+ if (app) {
437
+ command = app;
438
+ } else {
439
+ const isBundled = !__dirname || __dirname === "/";
440
+ let exeLocalXdgOpen = false;
441
+ try {
442
+ await import_promises2.default.access(localXdgOpenPath, import_promises2.constants.X_OK);
443
+ exeLocalXdgOpen = true;
444
+ } catch {
445
+ }
446
+ const useSystemXdgOpen = import_node_process6.default.versions.electron ?? (platform === "android" || isBundled || !exeLocalXdgOpen);
447
+ command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
448
+ }
449
+ if (appArguments.length > 0) {
450
+ cliArguments.push(...appArguments);
451
+ }
452
+ if (!options.wait) {
453
+ childProcessOptions.stdio = "ignore";
454
+ childProcessOptions.detached = true;
455
+ }
456
+ }
457
+ if (platform === "darwin" && appArguments.length > 0) {
458
+ cliArguments.push("--args", ...appArguments);
459
+ }
460
+ if (options.target) {
461
+ cliArguments.push(options.target);
462
+ }
463
+ const subprocess = import_node_child_process5.default.spawn(command, cliArguments, childProcessOptions);
464
+ if (options.wait) {
465
+ return new Promise((resolve, reject) => {
466
+ subprocess.once("error", reject);
467
+ subprocess.once("close", (exitCode) => {
468
+ if (!options.allowNonzeroExitCode && exitCode > 0) {
469
+ reject(new Error(`Exited with code ${exitCode}`));
470
+ return;
471
+ }
472
+ resolve(subprocess);
473
+ });
474
+ });
475
+ }
476
+ subprocess.unref();
477
+ return subprocess;
478
+ };
479
+ var open = (target, options) => {
480
+ if (typeof target !== "string") {
481
+ throw new TypeError("Expected a `target`");
482
+ }
483
+ return baseOpen({
484
+ ...options,
485
+ target
486
+ });
487
+ };
488
+ function detectArchBinary(binary) {
489
+ if (typeof binary === "string" || Array.isArray(binary)) {
490
+ return binary;
491
+ }
492
+ const { [arch]: archBinary } = binary;
493
+ if (!archBinary) {
494
+ throw new Error(`${arch} is not supported`);
495
+ }
496
+ return archBinary;
497
+ }
498
+ function detectPlatformBinary({ [platform]: platformBinary }, { wsl }) {
499
+ if (wsl && is_wsl_default) {
500
+ return detectArchBinary(wsl);
501
+ }
502
+ if (!platformBinary) {
503
+ throw new Error(`${platform} is not supported`);
504
+ }
505
+ return detectArchBinary(platformBinary);
506
+ }
507
+ var apps = {};
508
+ defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
509
+ darwin: "google chrome",
510
+ win32: "chrome",
511
+ linux: ["google-chrome", "google-chrome-stable", "chromium"]
512
+ }, {
513
+ wsl: {
514
+ ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
515
+ x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
516
+ }
517
+ }));
518
+ defineLazyProperty(apps, "brave", () => detectPlatformBinary({
519
+ darwin: "brave browser",
520
+ win32: "brave",
521
+ linux: ["brave-browser", "brave"]
522
+ }, {
523
+ wsl: {
524
+ ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
525
+ x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
526
+ }
527
+ }));
528
+ defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
529
+ darwin: "firefox",
530
+ win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
531
+ linux: "firefox"
532
+ }, {
533
+ wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
534
+ }));
535
+ defineLazyProperty(apps, "edge", () => detectPlatformBinary({
536
+ darwin: "microsoft edge",
537
+ win32: "msedge",
538
+ linux: ["microsoft-edge", "microsoft-edge-dev"]
539
+ }, {
540
+ wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
541
+ }));
542
+ defineLazyProperty(apps, "browser", () => "browser");
543
+ defineLazyProperty(apps, "browserPrivate", () => "browserPrivate");
544
+ var open_default = open;
28
545
 
29
546
  // src/validate.ts
30
547
  function validateMoodUISpec(input) {
@@ -48,21 +565,21 @@ function assertMoodUISpec(input) {
48
565
  }
49
566
  return result.value;
50
567
  }
51
- function validateNode(input, path2) {
568
+ function validateNode(input, path3) {
52
569
  const errors = [];
53
- if (!isObject(input)) return { ok: false, errors: [`${path2} must be an object`] };
570
+ if (!isObject(input)) return { ok: false, errors: [`${path3} must be an object`] };
54
571
  const type = input.type;
55
- if (!isString(type)) return { ok: false, errors: [`${path2}.type must be a string`] };
572
+ if (!isString(type)) return { ok: false, errors: [`${path3}.type must be a string`] };
56
573
  switch (type) {
57
574
  case "box": {
58
575
  const children = input.children;
59
576
  if (children != null) {
60
577
  if (!Array.isArray(children)) {
61
- errors.push(`${path2}.children must be an array`);
578
+ errors.push(`${path3}.children must be an array`);
62
579
  } else {
63
580
  for (let i = 0; i < children.length; i += 1) {
64
581
  const child = children[i];
65
- const childResult = validateNode(child, `${path2}.children[${i}]`);
582
+ const childResult = validateNode(child, `${path3}.children[${i}]`);
66
583
  if (!childResult.ok) errors.push(...childResult.errors);
67
584
  }
68
585
  }
@@ -71,34 +588,34 @@ function validateNode(input, path2) {
71
588
  }
72
589
  case "text": {
73
590
  const props = input.props;
74
- if (!isObject(props)) errors.push(`${path2}.props must be an object`);
75
- else if (!isString(props.value)) errors.push(`${path2}.props.value must be a string`);
591
+ if (!isObject(props)) errors.push(`${path3}.props must be an object`);
592
+ else if (!isString(props.value)) errors.push(`${path3}.props.value must be a string`);
76
593
  break;
77
594
  }
78
595
  case "button": {
79
596
  const props = input.props;
80
- if (!isObject(props)) errors.push(`${path2}.props must be an object`);
81
- else if (!isString(props.label)) errors.push(`${path2}.props.label must be a string`);
597
+ if (!isObject(props)) errors.push(`${path3}.props must be an object`);
598
+ else if (!isString(props.label)) errors.push(`${path3}.props.label must be a string`);
82
599
  break;
83
600
  }
84
601
  case "input": {
85
602
  const props = input.props;
86
- if (props != null && !isObject(props)) errors.push(`${path2}.props must be an object if provided`);
603
+ if (props != null && !isObject(props)) errors.push(`${path3}.props must be an object if provided`);
87
604
  break;
88
605
  }
89
606
  case "image": {
90
607
  const props = input.props;
91
- if (!isObject(props)) errors.push(`${path2}.props must be an object`);
92
- else if (!isString(props.src)) errors.push(`${path2}.props.src must be a string`);
608
+ if (!isObject(props)) errors.push(`${path3}.props must be an object`);
609
+ else if (!isString(props.src)) errors.push(`${path3}.props.src must be a string`);
93
610
  break;
94
611
  }
95
612
  case "spacer": {
96
613
  const props = input.props;
97
- if (props != null && !isObject(props)) errors.push(`${path2}.props must be an object if provided`);
614
+ if (props != null && !isObject(props)) errors.push(`${path3}.props must be an object if provided`);
98
615
  break;
99
616
  }
100
617
  default:
101
- errors.push(`${path2}.type must be one of: box | text | button | input | image | spacer`);
618
+ errors.push(`${path3}.type must be one of: box | text | button | input | image | spacer`);
102
619
  }
103
620
  if (errors.length > 0) return { ok: false, errors };
104
621
  return { ok: true, value: input };
@@ -280,11 +797,11 @@ function renderNode(node, ctx) {
280
797
  function renderElement(tag, node, ctx, renderChildren, extraProps) {
281
798
  const children = renderChildren();
282
799
  const propParts = buildProps(tag, node, extraProps);
283
- const open = `<${tag}${propParts.length ? " " + propParts.join(" ") : ""}>`;
800
+ const open2 = `<${tag}${propParts.length ? " " + propParts.join(" ") : ""}>`;
284
801
  const close = `</${tag}>`;
285
- if (children.length === 0) return indent(ctx.indent) + open + close;
802
+ if (children.length === 0) return indent(ctx.indent) + open2 + close;
286
803
  return [
287
- indent(ctx.indent) + open,
804
+ indent(ctx.indent) + open2,
288
805
  ...children,
289
806
  indent(ctx.indent) + close
290
807
  ].join("\n");
@@ -583,6 +1100,8 @@ async function generateReactFromPrompt(options) {
583
1100
  }
584
1101
 
585
1102
  // src/cli.ts
1103
+ var import_meta2 = {};
1104
+ var __dirname2 = import_node_path2.default.dirname((0, import_node_url2.fileURLToPath)(import_meta2.url));
586
1105
  async function main() {
587
1106
  const argv = process.argv.slice(2);
588
1107
  const command = argv[0] ?? "help";
@@ -590,6 +1109,10 @@ async function main() {
590
1109
  printHelp();
591
1110
  return;
592
1111
  }
1112
+ if (command === "ui") {
1113
+ await runUI();
1114
+ return;
1115
+ }
593
1116
  if (command !== "generate") {
594
1117
  console.error(`Unknown command: ${command}`);
595
1118
  printHelp();
@@ -652,17 +1175,65 @@ async function main() {
652
1175
  temperature,
653
1176
  maxAttempts
654
1177
  });
655
- const resolvedOut = import_node_path.default.resolve(process.cwd(), outFile);
656
- await import_promises.default.mkdir(import_node_path.default.dirname(resolvedOut), { recursive: true });
657
- await import_promises.default.writeFile(resolvedOut, result.code, "utf8");
1178
+ const resolvedOut = import_node_path2.default.resolve(process.cwd(), outFile);
1179
+ await import_promises3.default.mkdir(import_node_path2.default.dirname(resolvedOut), { recursive: true });
1180
+ await import_promises3.default.writeFile(resolvedOut, result.code, "utf8");
658
1181
  console.log(`OK: wrote ${resolvedOut}`);
659
1182
  }
1183
+ async function runUI() {
1184
+ let uiDir = import_node_path2.default.join(__dirname2, "ui");
1185
+ try {
1186
+ await import_promises3.default.access(uiDir);
1187
+ } catch {
1188
+ uiDir = import_node_path2.default.join(__dirname2, "..", "dist", "ui");
1189
+ }
1190
+ const PORT = 3e3;
1191
+ console.log(`Starting MoodUI UI...`);
1192
+ const server = import_node_http.default.createServer(async (req, res) => {
1193
+ let filePath = import_node_path2.default.join(uiDir, req.url === "/" ? "index.html" : req.url);
1194
+ const extname = String(import_node_path2.default.extname(filePath)).toLowerCase();
1195
+ const mimeTypes = {
1196
+ ".html": "text/html",
1197
+ ".js": "text/javascript",
1198
+ ".css": "text/css",
1199
+ ".json": "application/json",
1200
+ ".png": "image/png",
1201
+ ".jpg": "image/jpg",
1202
+ ".gif": "image/gif",
1203
+ ".svg": "image/svg+xml",
1204
+ ".ico": "image/x-icon"
1205
+ };
1206
+ const contentType = mimeTypes[extname] || "application/octet-stream";
1207
+ try {
1208
+ const content = await import_promises3.default.readFile(filePath);
1209
+ res.writeHead(200, { "Content-Type": contentType });
1210
+ res.end(content, "utf-8");
1211
+ } catch (err) {
1212
+ res.writeHead(404);
1213
+ res.end("Not Found", "utf-8");
1214
+ }
1215
+ });
1216
+ server.listen(PORT, () => {
1217
+ const url = `http://localhost:${PORT}`;
1218
+ console.log(`MoodUI UI running at ${url}`);
1219
+ open_default(url);
1220
+ });
1221
+ process.on("SIGINT", () => {
1222
+ server.close();
1223
+ process.exit(0);
1224
+ });
1225
+ }
660
1226
  function printHelp() {
661
1227
  console.log(
662
1228
  [
663
- "moodui generate [options] <prompt...>",
1229
+ "moodui <command>",
664
1230
  "",
665
- "Options:",
1231
+ "Commands:",
1232
+ " generate [options] <prompt...> Generate component via CLI",
1233
+ " ui Launch Web UI for generating components",
1234
+ " help, --help, -h Show this help message",
1235
+ "",
1236
+ "Generate Options:",
666
1237
  " --provider gemini|ollama|openai-compatible default: gemini",
667
1238
  " --model <name> required (default for gemini: gemini-3-flash-preview)",
668
1239
  " --out <path> path lengkap ke file output (termasuk direktori), default: src/generated/MoodScreen.tsx",
@@ -678,6 +1249,9 @@ function printHelp() {
678
1249
  " OPENAI_COMPATIBLE_API_KEY",
679
1250
  "",
680
1251
  "Examples:",
1252
+ " # Launch Web UI",
1253
+ " npx @kohryan/moodui ui",
1254
+ "",
681
1255
  " # Generate ke src/generated/MoodScreen.tsx (default)",
682
1256
  ' GEMINI_API_KEY=... npx @kohryan/moodui generate --model gemini-3-flash-preview "Buat UI login sederhana"',
683
1257
  "",