unity-hub-cli 0.13.2 → 0.15.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.
- package/dist/index.js +572 -72
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.tsx
|
|
4
|
-
import
|
|
4
|
+
import process3 from "process";
|
|
5
5
|
import { render } from "ink";
|
|
6
6
|
|
|
7
7
|
// src/application/usecases.ts
|
|
@@ -105,6 +105,108 @@ var TerminateProjectUseCase = class {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
};
|
|
108
|
+
var LaunchWithEditorUseCase = class {
|
|
109
|
+
constructor(launchProjectUseCase, externalEditorPathReader, externalEditorLauncher) {
|
|
110
|
+
this.launchProjectUseCase = launchProjectUseCase;
|
|
111
|
+
this.externalEditorPathReader = externalEditorPathReader;
|
|
112
|
+
this.externalEditorLauncher = externalEditorLauncher;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Launches the Unity project and attempts to open the external editor.
|
|
116
|
+
* @param project - The Unity project to launch.
|
|
117
|
+
* @returns The result of the launch operation.
|
|
118
|
+
*/
|
|
119
|
+
async execute(project) {
|
|
120
|
+
const editorResult = await this.externalEditorPathReader.read();
|
|
121
|
+
let editorLaunched = false;
|
|
122
|
+
let editorMessage = "";
|
|
123
|
+
if (editorResult.status === "found") {
|
|
124
|
+
editorLaunched = await this.tryLaunchEditor(editorResult, project.path);
|
|
125
|
+
editorMessage = editorLaunched ? ` + ${editorResult.name}` : " (Editor launch failed)";
|
|
126
|
+
} else {
|
|
127
|
+
editorMessage = this.buildEditorStatusMessage(editorResult);
|
|
128
|
+
}
|
|
129
|
+
await this.launchProjectUseCase.execute(project);
|
|
130
|
+
return {
|
|
131
|
+
unityLaunched: true,
|
|
132
|
+
editorLaunched,
|
|
133
|
+
message: `Launched: ${project.title}${editorMessage}`
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Attempts to launch the external editor.
|
|
138
|
+
* @param editorResult - The found external editor result.
|
|
139
|
+
* @param projectPath - The path to the project root.
|
|
140
|
+
* @returns Whether the editor was successfully launched.
|
|
141
|
+
*/
|
|
142
|
+
async tryLaunchEditor(editorResult, projectPath) {
|
|
143
|
+
try {
|
|
144
|
+
await this.externalEditorLauncher.launch(editorResult.path, projectPath);
|
|
145
|
+
return true;
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Builds a status message for non-found editor results.
|
|
152
|
+
* @param editorResult - The external editor result.
|
|
153
|
+
* @returns The status message.
|
|
154
|
+
*/
|
|
155
|
+
buildEditorStatusMessage(editorResult) {
|
|
156
|
+
if (editorResult.status === "not_configured") {
|
|
157
|
+
return " (Editor not configured)";
|
|
158
|
+
}
|
|
159
|
+
if (editorResult.status === "not_found") {
|
|
160
|
+
return ` (Editor not found: ${editorResult.configuredPath})`;
|
|
161
|
+
}
|
|
162
|
+
return "";
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var LaunchEditorOnlyUseCase = class {
|
|
166
|
+
constructor(externalEditorPathReader, externalEditorLauncher) {
|
|
167
|
+
this.externalEditorPathReader = externalEditorPathReader;
|
|
168
|
+
this.externalEditorLauncher = externalEditorLauncher;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Launches only the external editor for the specified project.
|
|
172
|
+
* @param project - The Unity project to open in the editor.
|
|
173
|
+
* @returns The result of the launch operation.
|
|
174
|
+
*/
|
|
175
|
+
async execute(project) {
|
|
176
|
+
const editorResult = await this.externalEditorPathReader.read();
|
|
177
|
+
if (editorResult.status === "not_configured") {
|
|
178
|
+
return {
|
|
179
|
+
editorLaunched: false,
|
|
180
|
+
message: "Editor not configured in Unity preferences"
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (editorResult.status === "not_found") {
|
|
184
|
+
return {
|
|
185
|
+
editorLaunched: false,
|
|
186
|
+
message: `Editor not found: ${editorResult.configuredPath}`
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const launched = await this.tryLaunchEditor(editorResult, project.path);
|
|
190
|
+
return {
|
|
191
|
+
editorLaunched: launched,
|
|
192
|
+
message: launched ? `Launched: ${editorResult.name}` : `Failed to launch ${editorResult.name}`
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Attempts to launch the external editor.
|
|
197
|
+
* @param editorResult - The found external editor result.
|
|
198
|
+
* @param projectPath - The path to the project root.
|
|
199
|
+
* @returns Whether the editor was successfully launched.
|
|
200
|
+
*/
|
|
201
|
+
async tryLaunchEditor(editorResult, projectPath) {
|
|
202
|
+
try {
|
|
203
|
+
await this.externalEditorLauncher.launch(editorResult.path, projectPath);
|
|
204
|
+
return true;
|
|
205
|
+
} catch {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
108
210
|
|
|
109
211
|
// src/infrastructure/editor.ts
|
|
110
212
|
import { constants } from "fs";
|
|
@@ -158,9 +260,139 @@ var WinEditorPathResolver = class {
|
|
|
158
260
|
}
|
|
159
261
|
};
|
|
160
262
|
|
|
263
|
+
// src/infrastructure/externalEditor.ts
|
|
264
|
+
import { execFile } from "child_process";
|
|
265
|
+
import { constants as constants3, existsSync } from "fs";
|
|
266
|
+
import { access as access3 } from "fs/promises";
|
|
267
|
+
import { basename, join as join3 } from "path";
|
|
268
|
+
import { promisify } from "util";
|
|
269
|
+
var execFileAsync = promisify(execFile);
|
|
270
|
+
var PLIST_DOMAIN = "com.unity3d.UnityEditor5.x";
|
|
271
|
+
var PLIST_KEY = "kScriptsDefaultApp";
|
|
272
|
+
var MacExternalEditorPathReader = class {
|
|
273
|
+
/**
|
|
274
|
+
* Reads the external editor configuration from Unity preferences.
|
|
275
|
+
* @returns The external editor result with status and path information.
|
|
276
|
+
*/
|
|
277
|
+
async read() {
|
|
278
|
+
let configuredPath;
|
|
279
|
+
try {
|
|
280
|
+
const result = await execFileAsync("defaults", ["read", PLIST_DOMAIN, PLIST_KEY]);
|
|
281
|
+
configuredPath = result.stdout.trim();
|
|
282
|
+
} catch {
|
|
283
|
+
return { status: "not_configured" };
|
|
284
|
+
}
|
|
285
|
+
if (!configuredPath) {
|
|
286
|
+
return { status: "not_configured" };
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
await access3(configuredPath, constants3.F_OK);
|
|
290
|
+
} catch {
|
|
291
|
+
return { status: "not_found", configuredPath };
|
|
292
|
+
}
|
|
293
|
+
const name = basename(configuredPath, ".app");
|
|
294
|
+
return { status: "found", path: configuredPath, name };
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
var MacExternalEditorLauncher = class {
|
|
298
|
+
/**
|
|
299
|
+
* Launches the external editor with the specified project root.
|
|
300
|
+
* If a .sln file exists with the project name, it will be opened directly.
|
|
301
|
+
* This allows Rider to open the solution without showing a selection dialog.
|
|
302
|
+
* @param editorPath - The path to the editor application.
|
|
303
|
+
* @param projectRoot - The project root directory to open.
|
|
304
|
+
*/
|
|
305
|
+
async launch(editorPath, projectRoot) {
|
|
306
|
+
const projectName = basename(projectRoot);
|
|
307
|
+
const slnFilePath = join3(projectRoot, `${projectName}.sln`);
|
|
308
|
+
const targetPath = existsSync(slnFilePath) ? slnFilePath : projectRoot;
|
|
309
|
+
await execFileAsync("open", ["-a", editorPath, targetPath]);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// src/infrastructure/externalEditor.win.ts
|
|
314
|
+
import { execFile as execFile2, spawn } from "child_process";
|
|
315
|
+
import { constants as constants4, existsSync as existsSync2 } from "fs";
|
|
316
|
+
import { access as access4 } from "fs/promises";
|
|
317
|
+
import { basename as basename2, join as join4 } from "path";
|
|
318
|
+
import { promisify as promisify2 } from "util";
|
|
319
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
320
|
+
var REGISTRY_PATH = "HKEY_CURRENT_USER\\Software\\Unity Technologies\\Unity Editor 5.x";
|
|
321
|
+
var parseRegistryOutput = (stdout) => {
|
|
322
|
+
const lines = stdout.split("\n");
|
|
323
|
+
for (const line of lines) {
|
|
324
|
+
const match = line.match(/kScriptsDefaultApp[^\s]*\s+REG_SZ\s+(.+)/i);
|
|
325
|
+
if (match?.[1]) {
|
|
326
|
+
return match[1].trim();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return void 0;
|
|
330
|
+
};
|
|
331
|
+
var WinExternalEditorPathReader = class {
|
|
332
|
+
/**
|
|
333
|
+
* Reads the external editor configuration from Unity preferences.
|
|
334
|
+
* @returns The external editor result with status and path information.
|
|
335
|
+
*/
|
|
336
|
+
async read() {
|
|
337
|
+
let configuredPath;
|
|
338
|
+
try {
|
|
339
|
+
const result = await execFileAsync2("reg", [
|
|
340
|
+
"query",
|
|
341
|
+
REGISTRY_PATH,
|
|
342
|
+
"/v",
|
|
343
|
+
"kScriptsDefaultApp"
|
|
344
|
+
]);
|
|
345
|
+
configuredPath = parseRegistryOutput(result.stdout);
|
|
346
|
+
} catch {
|
|
347
|
+
return { status: "not_configured" };
|
|
348
|
+
}
|
|
349
|
+
if (!configuredPath) {
|
|
350
|
+
return { status: "not_configured" };
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
await access4(configuredPath, constants4.F_OK);
|
|
354
|
+
} catch {
|
|
355
|
+
return { status: "not_found", configuredPath };
|
|
356
|
+
}
|
|
357
|
+
const name = basename2(configuredPath, ".exe");
|
|
358
|
+
return { status: "found", path: configuredPath, name };
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
var WinExternalEditorLauncher = class {
|
|
362
|
+
/**
|
|
363
|
+
* Launches the external editor with the specified project root.
|
|
364
|
+
* If a .sln file exists with the project name, it will be opened directly.
|
|
365
|
+
* This allows Rider to open the solution without showing a selection dialog.
|
|
366
|
+
* @param editorPath - The path to the editor executable.
|
|
367
|
+
* @param projectRoot - The project root directory to open.
|
|
368
|
+
*/
|
|
369
|
+
async launch(editorPath, projectRoot) {
|
|
370
|
+
const projectName = basename2(projectRoot);
|
|
371
|
+
const slnFilePath = join4(projectRoot, `${projectName}.sln`);
|
|
372
|
+
const targetPath = existsSync2(slnFilePath) ? slnFilePath : projectRoot;
|
|
373
|
+
await new Promise((resolve4, reject) => {
|
|
374
|
+
const child = spawn(editorPath, [targetPath], {
|
|
375
|
+
detached: true,
|
|
376
|
+
stdio: "ignore"
|
|
377
|
+
});
|
|
378
|
+
const handleError = (error) => {
|
|
379
|
+
child.off("spawn", handleSpawn);
|
|
380
|
+
reject(error);
|
|
381
|
+
};
|
|
382
|
+
const handleSpawn = () => {
|
|
383
|
+
child.off("error", handleError);
|
|
384
|
+
child.unref();
|
|
385
|
+
resolve4();
|
|
386
|
+
};
|
|
387
|
+
child.once("error", handleError);
|
|
388
|
+
child.once("spawn", handleSpawn);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
161
393
|
// src/infrastructure/git.ts
|
|
162
394
|
import { readFile, stat } from "fs/promises";
|
|
163
|
-
import { dirname, join as
|
|
395
|
+
import { dirname, join as join5, resolve } from "path";
|
|
164
396
|
var HEAD_FILE = "HEAD";
|
|
165
397
|
var GIT_DIR = ".git";
|
|
166
398
|
var MAX_ASCENT = 50;
|
|
@@ -183,7 +415,7 @@ var isFile = async (path) => {
|
|
|
183
415
|
var findGitDir = async (start) => {
|
|
184
416
|
let current = resolve(start);
|
|
185
417
|
for (let depth = 0; depth < MAX_ASCENT; depth += 1) {
|
|
186
|
-
const candidate =
|
|
418
|
+
const candidate = join5(current, GIT_DIR);
|
|
187
419
|
if (await isDirectory(candidate)) {
|
|
188
420
|
return candidate;
|
|
189
421
|
}
|
|
@@ -227,7 +459,7 @@ var GitRepositoryInfoReader = class {
|
|
|
227
459
|
return void 0;
|
|
228
460
|
}
|
|
229
461
|
try {
|
|
230
|
-
const headPath =
|
|
462
|
+
const headPath = join5(gitDir, HEAD_FILE);
|
|
231
463
|
const content = await readFile(headPath, "utf8");
|
|
232
464
|
const branch = parseHead(content);
|
|
233
465
|
const root = dirname(gitDir);
|
|
@@ -239,12 +471,12 @@ var GitRepositoryInfoReader = class {
|
|
|
239
471
|
};
|
|
240
472
|
|
|
241
473
|
// src/infrastructure/process.ts
|
|
242
|
-
import { spawn } from "child_process";
|
|
474
|
+
import { spawn as spawn2 } from "child_process";
|
|
243
475
|
var NodeProcessLauncher = class {
|
|
244
476
|
async launch(command, args, options) {
|
|
245
477
|
const detached = options?.detached ?? false;
|
|
246
478
|
await new Promise((resolve4, reject) => {
|
|
247
|
-
const child =
|
|
479
|
+
const child = spawn2(command, args, {
|
|
248
480
|
detached,
|
|
249
481
|
stdio: "ignore"
|
|
250
482
|
});
|
|
@@ -263,9 +495,104 @@ var NodeProcessLauncher = class {
|
|
|
263
495
|
}
|
|
264
496
|
};
|
|
265
497
|
|
|
498
|
+
// src/infrastructure/terminalTheme.ts
|
|
499
|
+
import { createInterface } from "readline";
|
|
500
|
+
var parseOsc11Response = (response) => {
|
|
501
|
+
const rgbMatch = response.match(/rgb:([0-9a-fA-F]+)\/([0-9a-fA-F]+)\/([0-9a-fA-F]+)/);
|
|
502
|
+
if (!rgbMatch) {
|
|
503
|
+
return void 0;
|
|
504
|
+
}
|
|
505
|
+
const [, rHex, gHex, bHex] = rgbMatch;
|
|
506
|
+
if (!rHex || !gHex || !bHex) {
|
|
507
|
+
return void 0;
|
|
508
|
+
}
|
|
509
|
+
const normalizeColorValue = (hex) => {
|
|
510
|
+
const value = parseInt(hex, 16);
|
|
511
|
+
if (hex.length === 4) {
|
|
512
|
+
return Math.floor(value / 256);
|
|
513
|
+
}
|
|
514
|
+
return value;
|
|
515
|
+
};
|
|
516
|
+
return {
|
|
517
|
+
r: normalizeColorValue(rHex),
|
|
518
|
+
g: normalizeColorValue(gHex),
|
|
519
|
+
b: normalizeColorValue(bHex)
|
|
520
|
+
};
|
|
521
|
+
};
|
|
522
|
+
var calculateRelativeLuminance = (color) => {
|
|
523
|
+
const toLinear = (value) => {
|
|
524
|
+
const normalized = value / 255;
|
|
525
|
+
if (normalized <= 0.03928) {
|
|
526
|
+
return normalized / 12.92;
|
|
527
|
+
}
|
|
528
|
+
return Math.pow((normalized + 0.055) / 1.055, 2.4);
|
|
529
|
+
};
|
|
530
|
+
const rLinear = toLinear(color.r);
|
|
531
|
+
const gLinear = toLinear(color.g);
|
|
532
|
+
const bLinear = toLinear(color.b);
|
|
533
|
+
return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear;
|
|
534
|
+
};
|
|
535
|
+
var determineThemeFromLuminance = (luminance) => {
|
|
536
|
+
const darkThreshold = 0.5;
|
|
537
|
+
return luminance < darkThreshold ? "dark" : "light";
|
|
538
|
+
};
|
|
539
|
+
var queryTerminalBackgroundColor = async (timeoutMs) => {
|
|
540
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
541
|
+
return void 0;
|
|
542
|
+
}
|
|
543
|
+
return new Promise((resolve4) => {
|
|
544
|
+
let responseBuffer = "";
|
|
545
|
+
let resolved = false;
|
|
546
|
+
if (process.stdin.isTTY) {
|
|
547
|
+
process.stdin.setRawMode(true);
|
|
548
|
+
}
|
|
549
|
+
const rl = createInterface({
|
|
550
|
+
input: process.stdin,
|
|
551
|
+
escapeCodeTimeout: timeoutMs
|
|
552
|
+
});
|
|
553
|
+
let timeoutId;
|
|
554
|
+
const onData = (chunk) => {
|
|
555
|
+
responseBuffer += chunk.toString();
|
|
556
|
+
if (responseBuffer.includes("\x07") || responseBuffer.includes("\x1B\\")) {
|
|
557
|
+
if (!resolved) {
|
|
558
|
+
resolved = true;
|
|
559
|
+
const color = parseOsc11Response(responseBuffer);
|
|
560
|
+
cleanup();
|
|
561
|
+
resolve4(color);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
const cleanup = () => {
|
|
566
|
+
clearTimeout(timeoutId);
|
|
567
|
+
rl.close();
|
|
568
|
+
process.stdin.off("data", onData);
|
|
569
|
+
if (process.stdin.isTTY && process.stdin.isRaw) {
|
|
570
|
+
process.stdin.setRawMode(false);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
timeoutId = setTimeout(() => {
|
|
574
|
+
if (!resolved) {
|
|
575
|
+
resolved = true;
|
|
576
|
+
cleanup();
|
|
577
|
+
resolve4(void 0);
|
|
578
|
+
}
|
|
579
|
+
}, timeoutMs);
|
|
580
|
+
process.stdin.on("data", onData);
|
|
581
|
+
process.stdout.write("\x1B]11;?\x07");
|
|
582
|
+
});
|
|
583
|
+
};
|
|
584
|
+
var detectTerminalTheme = async (timeoutMs = 100) => {
|
|
585
|
+
const backgroundColor = await queryTerminalBackgroundColor(timeoutMs);
|
|
586
|
+
if (!backgroundColor) {
|
|
587
|
+
return "dark";
|
|
588
|
+
}
|
|
589
|
+
const luminance = calculateRelativeLuminance(backgroundColor);
|
|
590
|
+
return determineThemeFromLuminance(luminance);
|
|
591
|
+
};
|
|
592
|
+
|
|
266
593
|
// src/infrastructure/unityhub.ts
|
|
267
594
|
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
268
|
-
import { basename } from "path";
|
|
595
|
+
import { basename as basename3 } from "path";
|
|
269
596
|
var HUB_PROJECTS_PATH = `${process.env.HOME ?? ""}/Library/Application Support/UnityHub/projects-v1.json`;
|
|
270
597
|
var schemaVersion = "v1";
|
|
271
598
|
var toUnityProject = (entry) => {
|
|
@@ -280,7 +607,7 @@ var toUnityProject = (entry) => {
|
|
|
280
607
|
const lastModified = typeof entry.lastModified === "number" ? new Date(entry.lastModified) : void 0;
|
|
281
608
|
return {
|
|
282
609
|
id: safePath,
|
|
283
|
-
title: entry.title?.trim() ||
|
|
610
|
+
title: entry.title?.trim() || basename3(safePath),
|
|
284
611
|
path: safePath,
|
|
285
612
|
version: { value: version },
|
|
286
613
|
lastModified,
|
|
@@ -395,9 +722,9 @@ var MacUnityHubProjectsReader = class {
|
|
|
395
722
|
|
|
396
723
|
// src/infrastructure/unityhub.win.ts
|
|
397
724
|
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
398
|
-
import { basename as
|
|
399
|
-
var HUB_DIR =
|
|
400
|
-
var HUB_PROJECTS_PATH2 =
|
|
725
|
+
import { basename as basename4, join as join6 } from "path";
|
|
726
|
+
var HUB_DIR = join6(process.env.APPDATA ?? "", "UnityHub");
|
|
727
|
+
var HUB_PROJECTS_PATH2 = join6(HUB_DIR, "projects-v1.json");
|
|
401
728
|
var schemaVersion2 = "v1";
|
|
402
729
|
var toUnityProject2 = (entry) => {
|
|
403
730
|
const safePath = entry.path;
|
|
@@ -411,7 +738,7 @@ var toUnityProject2 = (entry) => {
|
|
|
411
738
|
const lastModified = typeof entry.lastModified === "number" ? new Date(entry.lastModified) : void 0;
|
|
412
739
|
return {
|
|
413
740
|
id: safePath,
|
|
414
|
-
title: entry.title?.trim() ||
|
|
741
|
+
title: entry.title?.trim() || basename4(safePath),
|
|
415
742
|
path: safePath,
|
|
416
743
|
version: { value: version },
|
|
417
744
|
lastModified,
|
|
@@ -499,7 +826,7 @@ var WinUnityHubProjectsReader = class {
|
|
|
499
826
|
await writeFile2(HUB_PROJECTS_PATH2, JSON.stringify(json, void 0, 2), "utf8");
|
|
500
827
|
}
|
|
501
828
|
async readCliArgs(projectPath) {
|
|
502
|
-
const infoPath =
|
|
829
|
+
const infoPath = join6(HUB_DIR, "projectsInfo.json");
|
|
503
830
|
let content;
|
|
504
831
|
try {
|
|
505
832
|
content = await readFile3(infoPath, "utf8");
|
|
@@ -525,18 +852,18 @@ var WinUnityHubProjectsReader = class {
|
|
|
525
852
|
};
|
|
526
853
|
|
|
527
854
|
// src/infrastructure/unityLock.ts
|
|
528
|
-
import { execFile } from "child_process";
|
|
529
|
-
import { constants as
|
|
530
|
-
import { access as
|
|
531
|
-
import { join as
|
|
532
|
-
import { promisify } from "util";
|
|
533
|
-
var
|
|
855
|
+
import { execFile as execFile3 } from "child_process";
|
|
856
|
+
import { constants as constants5 } from "fs";
|
|
857
|
+
import { access as access5, rm } from "fs/promises";
|
|
858
|
+
import { join as join7 } from "path";
|
|
859
|
+
import { promisify as promisify3 } from "util";
|
|
860
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
534
861
|
var buildBringToFrontScript = (pid) => {
|
|
535
862
|
return `tell application "System Events" to set frontmost of (first process whose unix id is ${pid}) to true`;
|
|
536
863
|
};
|
|
537
864
|
var pathExists = async (target) => {
|
|
538
865
|
try {
|
|
539
|
-
await
|
|
866
|
+
await access5(target, constants5.F_OK);
|
|
540
867
|
return true;
|
|
541
868
|
} catch {
|
|
542
869
|
return false;
|
|
@@ -556,7 +883,7 @@ var UnityLockChecker = class {
|
|
|
556
883
|
await this.bringUnityToFront(activeProcess.pid);
|
|
557
884
|
return "skip";
|
|
558
885
|
}
|
|
559
|
-
const lockfilePath =
|
|
886
|
+
const lockfilePath = join7(projectPath, "Temp", "UnityLockfile");
|
|
560
887
|
const hasLockfile = await pathExists(lockfilePath);
|
|
561
888
|
if (!hasLockfile) {
|
|
562
889
|
return "allow";
|
|
@@ -585,7 +912,7 @@ var UnityLockChecker = class {
|
|
|
585
912
|
}
|
|
586
913
|
try {
|
|
587
914
|
const script = buildBringToFrontScript(pid);
|
|
588
|
-
await
|
|
915
|
+
await execFileAsync3("osascript", ["-e", script]);
|
|
589
916
|
} catch (error) {
|
|
590
917
|
const message = error instanceof Error ? error.message : String(error);
|
|
591
918
|
console.error(`Failed to bring Unity to front: ${message}`);
|
|
@@ -594,16 +921,16 @@ var UnityLockChecker = class {
|
|
|
594
921
|
};
|
|
595
922
|
var UnityLockStatusReader = class {
|
|
596
923
|
async isLocked(projectPath) {
|
|
597
|
-
const lockfilePath =
|
|
924
|
+
const lockfilePath = join7(projectPath, "Temp", "UnityLockfile");
|
|
598
925
|
return await pathExists(lockfilePath);
|
|
599
926
|
}
|
|
600
927
|
};
|
|
601
928
|
|
|
602
929
|
// src/infrastructure/unityProcess.ts
|
|
603
|
-
import { execFile as
|
|
930
|
+
import { execFile as execFile4 } from "child_process";
|
|
604
931
|
import { resolve as resolve2 } from "path";
|
|
605
|
-
import { promisify as
|
|
606
|
-
var
|
|
932
|
+
import { promisify as promisify4 } from "util";
|
|
933
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
607
934
|
var UNITY_EXECUTABLE_PATTERN = /Unity\.app\/Contents\/MacOS\/Unity/i;
|
|
608
935
|
var PROJECT_PATH_PATTERN = /-(?:projectPath|projectpath)(?:=|\s+)("[^"]+"|'[^']+'|[^\s"']+)/i;
|
|
609
936
|
var PROCESS_LIST_ARGS = ["-axo", "pid=,command=", "-ww"];
|
|
@@ -686,7 +1013,7 @@ var MacUnityProcessReader = class {
|
|
|
686
1013
|
async listUnityProcesses() {
|
|
687
1014
|
let stdout;
|
|
688
1015
|
try {
|
|
689
|
-
const result = await
|
|
1016
|
+
const result = await execFileAsync4(PROCESS_LIST_COMMAND, PROCESS_LIST_ARGS);
|
|
690
1017
|
stdout = result.stdout;
|
|
691
1018
|
} catch (error) {
|
|
692
1019
|
throw new Error(`Failed to retrieve Unity process list: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -716,7 +1043,7 @@ var MacUnityProcessReader = class {
|
|
|
716
1043
|
pid: pidValue,
|
|
717
1044
|
projectPath: normalizePath(projectArgument)
|
|
718
1045
|
};
|
|
719
|
-
}).filter((
|
|
1046
|
+
}).filter((process4) => Boolean(process4));
|
|
720
1047
|
}
|
|
721
1048
|
};
|
|
722
1049
|
var MacUnityProcessTerminator = class {
|
|
@@ -731,7 +1058,7 @@ var MacUnityProcessTerminator = class {
|
|
|
731
1058
|
' keystroke "q" using {command down}',
|
|
732
1059
|
"end tell"
|
|
733
1060
|
].join("\n");
|
|
734
|
-
await
|
|
1061
|
+
await execFileAsync4("osascript", ["-e", script]);
|
|
735
1062
|
const deadlineGraceful = Date.now() + GRACEFUL_QUIT_TIMEOUT_MILLIS;
|
|
736
1063
|
while (Date.now() < deadlineGraceful) {
|
|
737
1064
|
await delay(GRACEFUL_QUIT_POLL_INTERVAL_MILLIS);
|
|
@@ -778,10 +1105,10 @@ var MacUnityProcessTerminator = class {
|
|
|
778
1105
|
};
|
|
779
1106
|
|
|
780
1107
|
// src/infrastructure/unityProcess.win.ts
|
|
781
|
-
import { execFile as
|
|
1108
|
+
import { execFile as execFile5 } from "child_process";
|
|
782
1109
|
import { resolve as resolve3 } from "path";
|
|
783
|
-
import { promisify as
|
|
784
|
-
var
|
|
1110
|
+
import { promisify as promisify5 } from "util";
|
|
1111
|
+
var execFileAsync5 = promisify5(execFile5);
|
|
785
1112
|
var PROJECT_PATH_PATTERN2 = /-(?:projectPath|projectpath)(?:=|\s+)("[^"]+"|'[^']+'|[^\s"']+)/i;
|
|
786
1113
|
var TERMINATE_TIMEOUT_MILLIS2 = 5e3;
|
|
787
1114
|
var TERMINATE_POLL_INTERVAL_MILLIS2 = 200;
|
|
@@ -872,7 +1199,7 @@ var WinUnityProcessReader = class {
|
|
|
872
1199
|
].join(" ");
|
|
873
1200
|
let stdout;
|
|
874
1201
|
try {
|
|
875
|
-
const result = await
|
|
1202
|
+
const result = await execFileAsync5(
|
|
876
1203
|
"powershell.exe",
|
|
877
1204
|
[
|
|
878
1205
|
"-NoProfile",
|
|
@@ -909,7 +1236,7 @@ var WinUnityProcessReader = class {
|
|
|
909
1236
|
var WinUnityProcessTerminator = class {
|
|
910
1237
|
async terminate(unityProcess) {
|
|
911
1238
|
try {
|
|
912
|
-
await
|
|
1239
|
+
await execFileAsync5("powershell.exe", [
|
|
913
1240
|
"-NoProfile",
|
|
914
1241
|
"-NonInteractive",
|
|
915
1242
|
"-ExecutionPolicy",
|
|
@@ -931,7 +1258,7 @@ var WinUnityProcessTerminator = class {
|
|
|
931
1258
|
}
|
|
932
1259
|
}
|
|
933
1260
|
try {
|
|
934
|
-
await
|
|
1261
|
+
await execFileAsync5("powershell.exe", [
|
|
935
1262
|
"-NoProfile",
|
|
936
1263
|
"-NonInteractive",
|
|
937
1264
|
"-ExecutionPolicy",
|
|
@@ -953,11 +1280,11 @@ var WinUnityProcessTerminator = class {
|
|
|
953
1280
|
|
|
954
1281
|
// src/infrastructure/unityTemp.ts
|
|
955
1282
|
import { rm as rm2 } from "fs/promises";
|
|
956
|
-
import { join as
|
|
1283
|
+
import { join as join8 } from "path";
|
|
957
1284
|
var TEMP_DIRECTORY_NAME = "Temp";
|
|
958
1285
|
var UnityTempDirectoryCleaner = class {
|
|
959
1286
|
async clean(projectPath) {
|
|
960
|
-
const tempDirectoryPath =
|
|
1287
|
+
const tempDirectoryPath = join8(projectPath, TEMP_DIRECTORY_NAME);
|
|
961
1288
|
try {
|
|
962
1289
|
await rm2(tempDirectoryPath, {
|
|
963
1290
|
recursive: true,
|
|
@@ -969,7 +1296,8 @@ var UnityTempDirectoryCleaner = class {
|
|
|
969
1296
|
};
|
|
970
1297
|
|
|
971
1298
|
// src/presentation/App.tsx
|
|
972
|
-
import { basename as
|
|
1299
|
+
import { basename as basename6 } from "path";
|
|
1300
|
+
import process2 from "process";
|
|
973
1301
|
import clipboard from "clipboardy";
|
|
974
1302
|
import { Box as Box6, Text as Text4, useApp, useInput, useStdout as useStdout2 } from "ink";
|
|
975
1303
|
import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useState as useState4 } from "react";
|
|
@@ -1013,10 +1341,60 @@ var LayoutManager = ({
|
|
|
1013
1341
|
};
|
|
1014
1342
|
|
|
1015
1343
|
// src/presentation/components/ProjectList.tsx
|
|
1016
|
-
import { basename as
|
|
1344
|
+
import { basename as basename5 } from "path";
|
|
1017
1345
|
import { Box as Box3 } from "ink";
|
|
1018
1346
|
import { useMemo } from "react";
|
|
1019
1347
|
|
|
1348
|
+
// src/presentation/theme.ts
|
|
1349
|
+
import { createContext, createElement, useContext } from "react";
|
|
1350
|
+
var darkPalette = {
|
|
1351
|
+
projectName: "#abd8e7",
|
|
1352
|
+
// Light cyan
|
|
1353
|
+
branch: "#e3839c",
|
|
1354
|
+
// Pink
|
|
1355
|
+
path: "#719bd8",
|
|
1356
|
+
// Blue
|
|
1357
|
+
border: "green",
|
|
1358
|
+
// Green
|
|
1359
|
+
status: "yellow",
|
|
1360
|
+
// Yellow
|
|
1361
|
+
focus: "green"
|
|
1362
|
+
// Green
|
|
1363
|
+
};
|
|
1364
|
+
var lightPalette = {
|
|
1365
|
+
projectName: "#0044aa",
|
|
1366
|
+
// Deep blue
|
|
1367
|
+
branch: "#991144",
|
|
1368
|
+
// Deep magenta
|
|
1369
|
+
path: "#1a4570",
|
|
1370
|
+
// Deep blue
|
|
1371
|
+
border: "#006400",
|
|
1372
|
+
// Dark green
|
|
1373
|
+
status: "#cc6600",
|
|
1374
|
+
// Dark orange (more visible)
|
|
1375
|
+
focus: "#006400"
|
|
1376
|
+
// Dark green
|
|
1377
|
+
};
|
|
1378
|
+
var getColorPalette = (theme) => {
|
|
1379
|
+
return theme === "dark" ? darkPalette : lightPalette;
|
|
1380
|
+
};
|
|
1381
|
+
var defaultThemeContext = {
|
|
1382
|
+
theme: "dark",
|
|
1383
|
+
colors: darkPalette
|
|
1384
|
+
};
|
|
1385
|
+
var ThemeContext = createContext(defaultThemeContext);
|
|
1386
|
+
var useThemeColors = () => {
|
|
1387
|
+
const context = useContext(ThemeContext);
|
|
1388
|
+
return context.colors;
|
|
1389
|
+
};
|
|
1390
|
+
var ThemeProvider = ({ theme, children }) => {
|
|
1391
|
+
const value = {
|
|
1392
|
+
theme,
|
|
1393
|
+
colors: getColorPalette(theme)
|
|
1394
|
+
};
|
|
1395
|
+
return createElement(ThemeContext.Provider, { value }, children);
|
|
1396
|
+
};
|
|
1397
|
+
|
|
1020
1398
|
// src/presentation/utils/path.ts
|
|
1021
1399
|
var homeDirectory = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
1022
1400
|
var normalizedHomeDirectory = homeDirectory.replace(/\\/g, "/");
|
|
@@ -1070,13 +1448,14 @@ var ProjectRow = ({
|
|
|
1070
1448
|
showSpacer
|
|
1071
1449
|
}) => {
|
|
1072
1450
|
const { stdout } = useStdout();
|
|
1451
|
+
const colors = useThemeColors();
|
|
1073
1452
|
const computedCenterWidth = typeof stdout?.columns === "number" ? Math.max(0, stdout.columns - 6) : void 0;
|
|
1074
1453
|
const centerWidth = typeof computedCenterWidth === "number" ? Math.max(0, computedCenterWidth - (isSelected ? 1 : 0)) : void 0;
|
|
1075
1454
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", children: [
|
|
1076
1455
|
/* @__PURE__ */ jsxs2(Box2, { width: 1, flexDirection: "column", alignItems: "center", marginLeft: 0, children: [
|
|
1077
|
-
/* @__PURE__ */ jsx2(Text, { color: isSelected ?
|
|
1078
|
-
showBranch ? /* @__PURE__ */ jsx2(Text, { color: isSelected ?
|
|
1079
|
-
showPath ? /* @__PURE__ */ jsx2(Text, { color: isSelected ?
|
|
1456
|
+
/* @__PURE__ */ jsx2(Text, { color: isSelected ? colors.focus : void 0, children: selectionBar }),
|
|
1457
|
+
showBranch ? /* @__PURE__ */ jsx2(Text, { color: isSelected ? colors.focus : void 0, children: selectionBar }) : null,
|
|
1458
|
+
showPath ? /* @__PURE__ */ jsx2(Text, { color: isSelected ? colors.focus : void 0, children: selectionBar }) : null
|
|
1080
1459
|
] }),
|
|
1081
1460
|
/* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginLeft: isSelected ? 2 : 1, width: centerWidth, children: [
|
|
1082
1461
|
/* @__PURE__ */ jsxs2(Text, { wrap: "truncate", children: [
|
|
@@ -1088,8 +1467,8 @@ var ProjectRow = ({
|
|
|
1088
1467
|
updatedText ? /* @__PURE__ */ jsx2(Text, { children: ` ${updatedText}` }) : null,
|
|
1089
1468
|
statusLabel && statusColor ? /* @__PURE__ */ jsx2(Text, { color: statusColor, children: ` ${statusLabel}` }) : null
|
|
1090
1469
|
] }),
|
|
1091
|
-
showBranch ? /* @__PURE__ */ jsx2(Text, { color:
|
|
1092
|
-
showPath ? /* @__PURE__ */ jsx2(Text, { color:
|
|
1470
|
+
showBranch ? /* @__PURE__ */ jsx2(Text, { color: colors.branch, wrap: "truncate", children: branchLine }) : null,
|
|
1471
|
+
showPath ? /* @__PURE__ */ jsx2(Text, { color: colors.path, wrap: "truncate", children: pathLine }) : null,
|
|
1093
1472
|
showSpacer ? /* @__PURE__ */ jsx2(Text, { children: " " }) : null
|
|
1094
1473
|
] }),
|
|
1095
1474
|
/* @__PURE__ */ jsxs2(Box2, { marginLeft: 1, width: 1, flexDirection: "column", alignItems: "center", children: [
|
|
@@ -1103,8 +1482,6 @@ var ProjectRow = ({
|
|
|
1103
1482
|
|
|
1104
1483
|
// src/presentation/components/ProjectList.tsx
|
|
1105
1484
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1106
|
-
var PROJECT_COLOR = "#abd8e7";
|
|
1107
|
-
var LOCK_COLOR = "yellow";
|
|
1108
1485
|
var STATUS_LABELS = {
|
|
1109
1486
|
idle: "",
|
|
1110
1487
|
running: "[running]",
|
|
@@ -1114,7 +1491,7 @@ var extractRootFolder = (repository) => {
|
|
|
1114
1491
|
if (!repository?.root) {
|
|
1115
1492
|
return void 0;
|
|
1116
1493
|
}
|
|
1117
|
-
const base =
|
|
1494
|
+
const base = basename5(repository.root);
|
|
1118
1495
|
return base || void 0;
|
|
1119
1496
|
};
|
|
1120
1497
|
var formatProjectName = (projectTitle, repository, useGitRootName) => {
|
|
@@ -1199,6 +1576,7 @@ var ProjectList = ({
|
|
|
1199
1576
|
launchedProjects,
|
|
1200
1577
|
totalProjects
|
|
1201
1578
|
}) => {
|
|
1579
|
+
const colors = useThemeColors();
|
|
1202
1580
|
const scrollbarChars = useMemo(() => {
|
|
1203
1581
|
const totalLines = totalProjects * linesPerProject;
|
|
1204
1582
|
const windowProjects = visibleProjects.length;
|
|
@@ -1252,14 +1630,14 @@ var ProjectList = ({
|
|
|
1252
1630
|
const pathScrollbar = showPath ? scrollbarChars[baseScrollbarIndex + 1 + (showBranch ? 1 : 0)] ?? " " : " ";
|
|
1253
1631
|
const spacerScrollbar = scrollbarChars[baseScrollbarIndex + linesPerProject - 1] ?? " ";
|
|
1254
1632
|
const statusLabel = STATUS_LABELS[displayStatus];
|
|
1255
|
-
const statusColor = displayStatus === "running" ?
|
|
1633
|
+
const statusColor = displayStatus === "running" ? colors.status : void 0;
|
|
1256
1634
|
return /* @__PURE__ */ jsx3(
|
|
1257
1635
|
ProjectRow,
|
|
1258
1636
|
{
|
|
1259
1637
|
isSelected,
|
|
1260
1638
|
selectionBar,
|
|
1261
1639
|
projectName,
|
|
1262
|
-
projectColor:
|
|
1640
|
+
projectColor: colors.projectName,
|
|
1263
1641
|
versionLabel,
|
|
1264
1642
|
updatedText,
|
|
1265
1643
|
statusLabel,
|
|
@@ -1313,10 +1691,11 @@ var SortPanel = ({ sortPreferences, focusedIndex, width }) => {
|
|
|
1313
1691
|
const primaryLine = lineForPrimary(sortPreferences.primary);
|
|
1314
1692
|
const directionLine = lineForDirection(sortPreferences);
|
|
1315
1693
|
const favoritesLine = lineForFavorites(sortPreferences.favoritesFirst);
|
|
1694
|
+
const colors = useThemeColors();
|
|
1316
1695
|
const Item = ({ label, selected }) => {
|
|
1317
1696
|
const prefix = selected ? "> " : " ";
|
|
1318
1697
|
return /* @__PURE__ */ jsxs3(Text2, { children: [
|
|
1319
|
-
selected ? /* @__PURE__ */ jsx4(Text2, { color:
|
|
1698
|
+
selected ? /* @__PURE__ */ jsx4(Text2, { color: colors.focus, children: prefix }) : prefix,
|
|
1320
1699
|
label
|
|
1321
1700
|
] });
|
|
1322
1701
|
};
|
|
@@ -1325,7 +1704,7 @@ var SortPanel = ({ sortPreferences, focusedIndex, width }) => {
|
|
|
1325
1704
|
{
|
|
1326
1705
|
flexDirection: "column",
|
|
1327
1706
|
borderStyle: "round",
|
|
1328
|
-
borderColor:
|
|
1707
|
+
borderColor: colors.border,
|
|
1329
1708
|
paddingX: 1,
|
|
1330
1709
|
width,
|
|
1331
1710
|
children: [
|
|
@@ -1345,10 +1724,11 @@ var lineForPath = (on) => `Show path: ${on ? "ON" : "OFF"}`;
|
|
|
1345
1724
|
var VisibilityPanel = ({ visibility, focusedIndex, width }) => {
|
|
1346
1725
|
const branchLine = lineForBranch(visibility.showBranch);
|
|
1347
1726
|
const pathLine = lineForPath(visibility.showPath);
|
|
1727
|
+
const colors = useThemeColors();
|
|
1348
1728
|
const Item = ({ label, selected }) => {
|
|
1349
1729
|
const prefix = selected ? "> " : " ";
|
|
1350
1730
|
return /* @__PURE__ */ jsxs4(Text3, { children: [
|
|
1351
|
-
selected ? /* @__PURE__ */ jsx5(Text3, { color:
|
|
1731
|
+
selected ? /* @__PURE__ */ jsx5(Text3, { color: colors.focus, children: prefix }) : prefix,
|
|
1352
1732
|
label
|
|
1353
1733
|
] });
|
|
1354
1734
|
};
|
|
@@ -1357,7 +1737,7 @@ var VisibilityPanel = ({ visibility, focusedIndex, width }) => {
|
|
|
1357
1737
|
{
|
|
1358
1738
|
flexDirection: "column",
|
|
1359
1739
|
borderStyle: "round",
|
|
1360
|
-
borderColor:
|
|
1740
|
+
borderColor: colors.border,
|
|
1361
1741
|
paddingX: 1,
|
|
1362
1742
|
width,
|
|
1363
1743
|
children: [
|
|
@@ -1544,11 +1924,12 @@ var extractRootFolder2 = (repository) => {
|
|
|
1544
1924
|
if (!repository?.root) {
|
|
1545
1925
|
return void 0;
|
|
1546
1926
|
}
|
|
1547
|
-
const base =
|
|
1927
|
+
const base = basename6(repository.root);
|
|
1548
1928
|
return base || void 0;
|
|
1549
1929
|
};
|
|
1550
1930
|
var minimumVisibleProjectCount = 4;
|
|
1551
|
-
var
|
|
1931
|
+
var editorOnlyKey = process2.platform === "darwin" ? "\u2325o" : "Alt+o";
|
|
1932
|
+
var defaultHintMessage = `j/k Select \xB7 [o]pen [O]+Editor [${editorOnlyKey}]Editor [q]uit [r]efresh [c]opy [s]ort [v]isibility \xB7 ^C Exit`;
|
|
1552
1933
|
var getCopyTargetPath = (view) => {
|
|
1553
1934
|
const root = view.repository?.root;
|
|
1554
1935
|
return root && root.length > 0 ? root : view.project.path;
|
|
@@ -1556,12 +1937,15 @@ var getCopyTargetPath = (view) => {
|
|
|
1556
1937
|
var App = ({
|
|
1557
1938
|
projects,
|
|
1558
1939
|
onLaunch,
|
|
1940
|
+
onLaunchWithEditor,
|
|
1941
|
+
onLaunchEditorOnly,
|
|
1559
1942
|
onTerminate,
|
|
1560
1943
|
onRefresh,
|
|
1561
1944
|
useGitRootName = true
|
|
1562
1945
|
}) => {
|
|
1563
1946
|
const { exit } = useApp();
|
|
1564
1947
|
const { stdout } = useStdout2();
|
|
1948
|
+
const colors = useThemeColors();
|
|
1565
1949
|
const [projectViews, setProjectViews] = useState4(projects);
|
|
1566
1950
|
const [isSortMenuOpen, setIsSortMenuOpen] = useState4(false);
|
|
1567
1951
|
const [isVisibilityMenuOpen, setIsVisibilityMenuOpen] = useState4(false);
|
|
@@ -1651,9 +2035,9 @@ var App = ({
|
|
|
1651
2035
|
const handleSigint = () => {
|
|
1652
2036
|
exit();
|
|
1653
2037
|
};
|
|
1654
|
-
|
|
2038
|
+
process2.on("SIGINT", handleSigint);
|
|
1655
2039
|
return () => {
|
|
1656
|
-
|
|
2040
|
+
process2.off("SIGINT", handleSigint);
|
|
1657
2041
|
};
|
|
1658
2042
|
}, [exit]);
|
|
1659
2043
|
const limit = Math.max(1, visibleCount);
|
|
@@ -1800,6 +2184,100 @@ var App = ({
|
|
|
1800
2184
|
}, 3e3);
|
|
1801
2185
|
}
|
|
1802
2186
|
}, [index, onLaunch, sortedProjects]);
|
|
2187
|
+
const launchSelectedWithEditor = useCallback(async () => {
|
|
2188
|
+
if (!onLaunchWithEditor) {
|
|
2189
|
+
setHint("Launch with editor not available");
|
|
2190
|
+
setTimeout(() => {
|
|
2191
|
+
setHint(defaultHintMessage);
|
|
2192
|
+
}, 2e3);
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
const projectView = sortedProjects[index];
|
|
2196
|
+
if (!projectView) {
|
|
2197
|
+
setHint("No project to launch");
|
|
2198
|
+
setTimeout(() => {
|
|
2199
|
+
setHint(defaultHintMessage);
|
|
2200
|
+
}, 2e3);
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
2203
|
+
const { project } = projectView;
|
|
2204
|
+
try {
|
|
2205
|
+
const cdTarget = getCopyTargetPath(projectView);
|
|
2206
|
+
const command = buildCdCommand(cdTarget);
|
|
2207
|
+
clipboard.writeSync(command);
|
|
2208
|
+
} catch (error) {
|
|
2209
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2210
|
+
setHint(`Failed to copy: ${message}`);
|
|
2211
|
+
setTimeout(() => {
|
|
2212
|
+
setHint(defaultHintMessage);
|
|
2213
|
+
}, 3e3);
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2216
|
+
try {
|
|
2217
|
+
const result = await onLaunchWithEditor(project);
|
|
2218
|
+
setLaunchedProjects((previous) => {
|
|
2219
|
+
const next = new Set(previous);
|
|
2220
|
+
next.add(project.id);
|
|
2221
|
+
return next;
|
|
2222
|
+
});
|
|
2223
|
+
setReleasedProjects((previous) => {
|
|
2224
|
+
if (!previous.has(project.id)) {
|
|
2225
|
+
return previous;
|
|
2226
|
+
}
|
|
2227
|
+
const next = new Set(previous);
|
|
2228
|
+
next.delete(project.id);
|
|
2229
|
+
return next;
|
|
2230
|
+
});
|
|
2231
|
+
setHint(result.message);
|
|
2232
|
+
setTimeout(() => {
|
|
2233
|
+
setHint(defaultHintMessage);
|
|
2234
|
+
}, 3e3);
|
|
2235
|
+
} catch (error) {
|
|
2236
|
+
if (error instanceof LaunchCancelledError) {
|
|
2237
|
+
setHint("Launch cancelled");
|
|
2238
|
+
setTimeout(() => {
|
|
2239
|
+
setHint(defaultHintMessage);
|
|
2240
|
+
}, 3e3);
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2244
|
+
setHint(`Failed to launch: ${message}`);
|
|
2245
|
+
setTimeout(() => {
|
|
2246
|
+
setHint(defaultHintMessage);
|
|
2247
|
+
}, 3e3);
|
|
2248
|
+
}
|
|
2249
|
+
}, [index, onLaunchWithEditor, sortedProjects]);
|
|
2250
|
+
const launchEditorOnly = useCallback(async () => {
|
|
2251
|
+
if (!onLaunchEditorOnly) {
|
|
2252
|
+
setHint("Launch editor only not available");
|
|
2253
|
+
setTimeout(() => {
|
|
2254
|
+
setHint(defaultHintMessage);
|
|
2255
|
+
}, 2e3);
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
const projectView = sortedProjects[index];
|
|
2259
|
+
if (!projectView) {
|
|
2260
|
+
setHint("No project to open");
|
|
2261
|
+
setTimeout(() => {
|
|
2262
|
+
setHint(defaultHintMessage);
|
|
2263
|
+
}, 2e3);
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
const { project } = projectView;
|
|
2267
|
+
try {
|
|
2268
|
+
const result = await onLaunchEditorOnly(project);
|
|
2269
|
+
setHint(result.message);
|
|
2270
|
+
setTimeout(() => {
|
|
2271
|
+
setHint(defaultHintMessage);
|
|
2272
|
+
}, 3e3);
|
|
2273
|
+
} catch (error) {
|
|
2274
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2275
|
+
setHint(`Failed to launch editor: ${message}`);
|
|
2276
|
+
setTimeout(() => {
|
|
2277
|
+
setHint(defaultHintMessage);
|
|
2278
|
+
}, 3e3);
|
|
2279
|
+
}
|
|
2280
|
+
}, [index, onLaunchEditorOnly, sortedProjects]);
|
|
1803
2281
|
const terminateSelected = useCallback(async () => {
|
|
1804
2282
|
const projectView = sortedProjects[index];
|
|
1805
2283
|
if (!projectView) {
|
|
@@ -2003,10 +2481,18 @@ var App = ({
|
|
|
2003
2481
|
void terminateSelected();
|
|
2004
2482
|
return;
|
|
2005
2483
|
}
|
|
2484
|
+
if (input === "\xF8" || input === "o" && key.meta) {
|
|
2485
|
+
void launchEditorOnly();
|
|
2486
|
+
return;
|
|
2487
|
+
}
|
|
2006
2488
|
if (input === "o") {
|
|
2007
2489
|
void launchSelected();
|
|
2008
2490
|
return;
|
|
2009
2491
|
}
|
|
2492
|
+
if (input === "O") {
|
|
2493
|
+
void launchSelectedWithEditor();
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2010
2496
|
if (input === "r") {
|
|
2011
2497
|
void refreshProjects();
|
|
2012
2498
|
return;
|
|
@@ -2040,7 +2526,7 @@ var App = ({
|
|
|
2040
2526
|
{
|
|
2041
2527
|
flexDirection: "column",
|
|
2042
2528
|
borderStyle: "round",
|
|
2043
|
-
borderColor:
|
|
2529
|
+
borderColor: colors.border,
|
|
2044
2530
|
width: typeof stdout?.columns === "number" ? stdout.columns : void 0,
|
|
2045
2531
|
children: sortedProjects.length === 0 ? /* @__PURE__ */ jsx6(Text4, { children: "No Unity Hub projects were found." }) : /* @__PURE__ */ jsx6(
|
|
2046
2532
|
ProjectList,
|
|
@@ -2091,7 +2577,7 @@ var App = ({
|
|
|
2091
2577
|
// src/index.tsx
|
|
2092
2578
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2093
2579
|
var bootstrap = async () => {
|
|
2094
|
-
const isWindows =
|
|
2580
|
+
const isWindows = process3.platform === "win32";
|
|
2095
2581
|
const unityHubReader = isWindows ? new WinUnityHubProjectsReader() : new MacUnityHubProjectsReader();
|
|
2096
2582
|
const gitRepositoryInfoReader = new GitRepositoryInfoReader();
|
|
2097
2583
|
const lockStatusReader = new UnityLockStatusReader();
|
|
@@ -2120,48 +2606,62 @@ var bootstrap = async () => {
|
|
|
2120
2606
|
unityProcessTerminator,
|
|
2121
2607
|
unityTempDirectoryCleaner
|
|
2122
2608
|
);
|
|
2123
|
-
const
|
|
2609
|
+
const externalEditorPathReader = isWindows ? new WinExternalEditorPathReader() : new MacExternalEditorPathReader();
|
|
2610
|
+
const externalEditorLauncher = isWindows ? new WinExternalEditorLauncher() : new MacExternalEditorLauncher();
|
|
2611
|
+
const launchWithEditorUseCase = new LaunchWithEditorUseCase(
|
|
2612
|
+
launchProjectUseCase,
|
|
2613
|
+
externalEditorPathReader,
|
|
2614
|
+
externalEditorLauncher
|
|
2615
|
+
);
|
|
2616
|
+
const launchEditorOnlyUseCase = new LaunchEditorOnlyUseCase(
|
|
2617
|
+
externalEditorPathReader,
|
|
2618
|
+
externalEditorLauncher
|
|
2619
|
+
);
|
|
2620
|
+
const useGitRootName = !process3.argv.includes("--no-git-root-name");
|
|
2124
2621
|
try {
|
|
2125
2622
|
const rawModeSupported = Boolean(
|
|
2126
|
-
|
|
2623
|
+
process3.stdin.isTTY && typeof process3.stdin.setRawMode === "function"
|
|
2127
2624
|
);
|
|
2128
2625
|
if (!rawModeSupported) {
|
|
2129
2626
|
const message = [
|
|
2130
|
-
"
|
|
2131
|
-
"PowerShell / cmd.exe
|
|
2132
|
-
"MinTTY
|
|
2627
|
+
"Interactive input (Raw mode) is not available in this terminal.",
|
|
2628
|
+
"Please run in PowerShell / cmd.exe, or use Git Bash in a ConPTY-based terminal (Windows Terminal, VS Code/Cursor integrated terminal).",
|
|
2629
|
+
"For MinTTY Git Bash, use one of the following:",
|
|
2133
2630
|
" - winpty cmd.exe /c npx unity-hub-cli",
|
|
2134
2631
|
" - winpty powershell.exe -NoProfile -Command npx unity-hub-cli",
|
|
2135
|
-
"
|
|
2136
|
-
"
|
|
2632
|
+
"(If already built) npm run build && winpty node dist/index.js",
|
|
2633
|
+
"Details: https://github.com/vadimdemedes/ink/#israwmodesupported"
|
|
2137
2634
|
].join("\n");
|
|
2138
2635
|
console.error(message);
|
|
2139
|
-
|
|
2636
|
+
process3.exitCode = 1;
|
|
2140
2637
|
return;
|
|
2141
2638
|
}
|
|
2639
|
+
const theme = await detectTerminalTheme();
|
|
2142
2640
|
const projects = await listProjectsUseCase.execute();
|
|
2143
2641
|
const { waitUntilExit } = render(
|
|
2144
|
-
/* @__PURE__ */ jsx7(
|
|
2642
|
+
/* @__PURE__ */ jsx7(ThemeProvider, { theme, children: /* @__PURE__ */ jsx7(
|
|
2145
2643
|
App,
|
|
2146
2644
|
{
|
|
2147
2645
|
projects,
|
|
2148
2646
|
onLaunch: (project) => launchProjectUseCase.execute(project),
|
|
2647
|
+
onLaunchWithEditor: (project) => launchWithEditorUseCase.execute(project),
|
|
2648
|
+
onLaunchEditorOnly: (project) => launchEditorOnlyUseCase.execute(project),
|
|
2149
2649
|
onTerminate: (project) => terminateProjectUseCase.execute(project),
|
|
2150
2650
|
onRefresh: () => listProjectsUseCase.execute(),
|
|
2151
2651
|
useGitRootName
|
|
2152
2652
|
}
|
|
2153
|
-
)
|
|
2653
|
+
) })
|
|
2154
2654
|
);
|
|
2155
2655
|
await waitUntilExit();
|
|
2156
|
-
|
|
2656
|
+
process3.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
2157
2657
|
} catch (error) {
|
|
2158
2658
|
const message = error instanceof Error ? error.message : String(error);
|
|
2159
2659
|
console.error(message);
|
|
2160
|
-
|
|
2660
|
+
process3.exitCode = 1;
|
|
2161
2661
|
}
|
|
2162
2662
|
};
|
|
2163
2663
|
void bootstrap().catch((error) => {
|
|
2164
2664
|
const message = error instanceof Error ? error.message : String(error);
|
|
2165
2665
|
console.error(message);
|
|
2166
|
-
|
|
2666
|
+
process3.exitCode = 1;
|
|
2167
2667
|
});
|