unity-hub-cli 0.14.0 → 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 +402 -51
- 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
|
});
|
|
@@ -360,7 +592,7 @@ var detectTerminalTheme = async (timeoutMs = 100) => {
|
|
|
360
592
|
|
|
361
593
|
// src/infrastructure/unityhub.ts
|
|
362
594
|
import { readFile as readFile2, writeFile } from "fs/promises";
|
|
363
|
-
import { basename } from "path";
|
|
595
|
+
import { basename as basename3 } from "path";
|
|
364
596
|
var HUB_PROJECTS_PATH = `${process.env.HOME ?? ""}/Library/Application Support/UnityHub/projects-v1.json`;
|
|
365
597
|
var schemaVersion = "v1";
|
|
366
598
|
var toUnityProject = (entry) => {
|
|
@@ -375,7 +607,7 @@ var toUnityProject = (entry) => {
|
|
|
375
607
|
const lastModified = typeof entry.lastModified === "number" ? new Date(entry.lastModified) : void 0;
|
|
376
608
|
return {
|
|
377
609
|
id: safePath,
|
|
378
|
-
title: entry.title?.trim() ||
|
|
610
|
+
title: entry.title?.trim() || basename3(safePath),
|
|
379
611
|
path: safePath,
|
|
380
612
|
version: { value: version },
|
|
381
613
|
lastModified,
|
|
@@ -490,9 +722,9 @@ var MacUnityHubProjectsReader = class {
|
|
|
490
722
|
|
|
491
723
|
// src/infrastructure/unityhub.win.ts
|
|
492
724
|
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
493
|
-
import { basename as
|
|
494
|
-
var HUB_DIR =
|
|
495
|
-
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");
|
|
496
728
|
var schemaVersion2 = "v1";
|
|
497
729
|
var toUnityProject2 = (entry) => {
|
|
498
730
|
const safePath = entry.path;
|
|
@@ -506,7 +738,7 @@ var toUnityProject2 = (entry) => {
|
|
|
506
738
|
const lastModified = typeof entry.lastModified === "number" ? new Date(entry.lastModified) : void 0;
|
|
507
739
|
return {
|
|
508
740
|
id: safePath,
|
|
509
|
-
title: entry.title?.trim() ||
|
|
741
|
+
title: entry.title?.trim() || basename4(safePath),
|
|
510
742
|
path: safePath,
|
|
511
743
|
version: { value: version },
|
|
512
744
|
lastModified,
|
|
@@ -594,7 +826,7 @@ var WinUnityHubProjectsReader = class {
|
|
|
594
826
|
await writeFile2(HUB_PROJECTS_PATH2, JSON.stringify(json, void 0, 2), "utf8");
|
|
595
827
|
}
|
|
596
828
|
async readCliArgs(projectPath) {
|
|
597
|
-
const infoPath =
|
|
829
|
+
const infoPath = join6(HUB_DIR, "projectsInfo.json");
|
|
598
830
|
let content;
|
|
599
831
|
try {
|
|
600
832
|
content = await readFile3(infoPath, "utf8");
|
|
@@ -620,18 +852,18 @@ var WinUnityHubProjectsReader = class {
|
|
|
620
852
|
};
|
|
621
853
|
|
|
622
854
|
// src/infrastructure/unityLock.ts
|
|
623
|
-
import { execFile } from "child_process";
|
|
624
|
-
import { constants as
|
|
625
|
-
import { access as
|
|
626
|
-
import { join as
|
|
627
|
-
import { promisify } from "util";
|
|
628
|
-
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);
|
|
629
861
|
var buildBringToFrontScript = (pid) => {
|
|
630
862
|
return `tell application "System Events" to set frontmost of (first process whose unix id is ${pid}) to true`;
|
|
631
863
|
};
|
|
632
864
|
var pathExists = async (target) => {
|
|
633
865
|
try {
|
|
634
|
-
await
|
|
866
|
+
await access5(target, constants5.F_OK);
|
|
635
867
|
return true;
|
|
636
868
|
} catch {
|
|
637
869
|
return false;
|
|
@@ -651,7 +883,7 @@ var UnityLockChecker = class {
|
|
|
651
883
|
await this.bringUnityToFront(activeProcess.pid);
|
|
652
884
|
return "skip";
|
|
653
885
|
}
|
|
654
|
-
const lockfilePath =
|
|
886
|
+
const lockfilePath = join7(projectPath, "Temp", "UnityLockfile");
|
|
655
887
|
const hasLockfile = await pathExists(lockfilePath);
|
|
656
888
|
if (!hasLockfile) {
|
|
657
889
|
return "allow";
|
|
@@ -680,7 +912,7 @@ var UnityLockChecker = class {
|
|
|
680
912
|
}
|
|
681
913
|
try {
|
|
682
914
|
const script = buildBringToFrontScript(pid);
|
|
683
|
-
await
|
|
915
|
+
await execFileAsync3("osascript", ["-e", script]);
|
|
684
916
|
} catch (error) {
|
|
685
917
|
const message = error instanceof Error ? error.message : String(error);
|
|
686
918
|
console.error(`Failed to bring Unity to front: ${message}`);
|
|
@@ -689,16 +921,16 @@ var UnityLockChecker = class {
|
|
|
689
921
|
};
|
|
690
922
|
var UnityLockStatusReader = class {
|
|
691
923
|
async isLocked(projectPath) {
|
|
692
|
-
const lockfilePath =
|
|
924
|
+
const lockfilePath = join7(projectPath, "Temp", "UnityLockfile");
|
|
693
925
|
return await pathExists(lockfilePath);
|
|
694
926
|
}
|
|
695
927
|
};
|
|
696
928
|
|
|
697
929
|
// src/infrastructure/unityProcess.ts
|
|
698
|
-
import { execFile as
|
|
930
|
+
import { execFile as execFile4 } from "child_process";
|
|
699
931
|
import { resolve as resolve2 } from "path";
|
|
700
|
-
import { promisify as
|
|
701
|
-
var
|
|
932
|
+
import { promisify as promisify4 } from "util";
|
|
933
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
702
934
|
var UNITY_EXECUTABLE_PATTERN = /Unity\.app\/Contents\/MacOS\/Unity/i;
|
|
703
935
|
var PROJECT_PATH_PATTERN = /-(?:projectPath|projectpath)(?:=|\s+)("[^"]+"|'[^']+'|[^\s"']+)/i;
|
|
704
936
|
var PROCESS_LIST_ARGS = ["-axo", "pid=,command=", "-ww"];
|
|
@@ -781,7 +1013,7 @@ var MacUnityProcessReader = class {
|
|
|
781
1013
|
async listUnityProcesses() {
|
|
782
1014
|
let stdout;
|
|
783
1015
|
try {
|
|
784
|
-
const result = await
|
|
1016
|
+
const result = await execFileAsync4(PROCESS_LIST_COMMAND, PROCESS_LIST_ARGS);
|
|
785
1017
|
stdout = result.stdout;
|
|
786
1018
|
} catch (error) {
|
|
787
1019
|
throw new Error(`Failed to retrieve Unity process list: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -811,7 +1043,7 @@ var MacUnityProcessReader = class {
|
|
|
811
1043
|
pid: pidValue,
|
|
812
1044
|
projectPath: normalizePath(projectArgument)
|
|
813
1045
|
};
|
|
814
|
-
}).filter((
|
|
1046
|
+
}).filter((process4) => Boolean(process4));
|
|
815
1047
|
}
|
|
816
1048
|
};
|
|
817
1049
|
var MacUnityProcessTerminator = class {
|
|
@@ -826,7 +1058,7 @@ var MacUnityProcessTerminator = class {
|
|
|
826
1058
|
' keystroke "q" using {command down}',
|
|
827
1059
|
"end tell"
|
|
828
1060
|
].join("\n");
|
|
829
|
-
await
|
|
1061
|
+
await execFileAsync4("osascript", ["-e", script]);
|
|
830
1062
|
const deadlineGraceful = Date.now() + GRACEFUL_QUIT_TIMEOUT_MILLIS;
|
|
831
1063
|
while (Date.now() < deadlineGraceful) {
|
|
832
1064
|
await delay(GRACEFUL_QUIT_POLL_INTERVAL_MILLIS);
|
|
@@ -873,10 +1105,10 @@ var MacUnityProcessTerminator = class {
|
|
|
873
1105
|
};
|
|
874
1106
|
|
|
875
1107
|
// src/infrastructure/unityProcess.win.ts
|
|
876
|
-
import { execFile as
|
|
1108
|
+
import { execFile as execFile5 } from "child_process";
|
|
877
1109
|
import { resolve as resolve3 } from "path";
|
|
878
|
-
import { promisify as
|
|
879
|
-
var
|
|
1110
|
+
import { promisify as promisify5 } from "util";
|
|
1111
|
+
var execFileAsync5 = promisify5(execFile5);
|
|
880
1112
|
var PROJECT_PATH_PATTERN2 = /-(?:projectPath|projectpath)(?:=|\s+)("[^"]+"|'[^']+'|[^\s"']+)/i;
|
|
881
1113
|
var TERMINATE_TIMEOUT_MILLIS2 = 5e3;
|
|
882
1114
|
var TERMINATE_POLL_INTERVAL_MILLIS2 = 200;
|
|
@@ -967,7 +1199,7 @@ var WinUnityProcessReader = class {
|
|
|
967
1199
|
].join(" ");
|
|
968
1200
|
let stdout;
|
|
969
1201
|
try {
|
|
970
|
-
const result = await
|
|
1202
|
+
const result = await execFileAsync5(
|
|
971
1203
|
"powershell.exe",
|
|
972
1204
|
[
|
|
973
1205
|
"-NoProfile",
|
|
@@ -1004,7 +1236,7 @@ var WinUnityProcessReader = class {
|
|
|
1004
1236
|
var WinUnityProcessTerminator = class {
|
|
1005
1237
|
async terminate(unityProcess) {
|
|
1006
1238
|
try {
|
|
1007
|
-
await
|
|
1239
|
+
await execFileAsync5("powershell.exe", [
|
|
1008
1240
|
"-NoProfile",
|
|
1009
1241
|
"-NonInteractive",
|
|
1010
1242
|
"-ExecutionPolicy",
|
|
@@ -1026,7 +1258,7 @@ var WinUnityProcessTerminator = class {
|
|
|
1026
1258
|
}
|
|
1027
1259
|
}
|
|
1028
1260
|
try {
|
|
1029
|
-
await
|
|
1261
|
+
await execFileAsync5("powershell.exe", [
|
|
1030
1262
|
"-NoProfile",
|
|
1031
1263
|
"-NonInteractive",
|
|
1032
1264
|
"-ExecutionPolicy",
|
|
@@ -1048,11 +1280,11 @@ var WinUnityProcessTerminator = class {
|
|
|
1048
1280
|
|
|
1049
1281
|
// src/infrastructure/unityTemp.ts
|
|
1050
1282
|
import { rm as rm2 } from "fs/promises";
|
|
1051
|
-
import { join as
|
|
1283
|
+
import { join as join8 } from "path";
|
|
1052
1284
|
var TEMP_DIRECTORY_NAME = "Temp";
|
|
1053
1285
|
var UnityTempDirectoryCleaner = class {
|
|
1054
1286
|
async clean(projectPath) {
|
|
1055
|
-
const tempDirectoryPath =
|
|
1287
|
+
const tempDirectoryPath = join8(projectPath, TEMP_DIRECTORY_NAME);
|
|
1056
1288
|
try {
|
|
1057
1289
|
await rm2(tempDirectoryPath, {
|
|
1058
1290
|
recursive: true,
|
|
@@ -1064,7 +1296,8 @@ var UnityTempDirectoryCleaner = class {
|
|
|
1064
1296
|
};
|
|
1065
1297
|
|
|
1066
1298
|
// src/presentation/App.tsx
|
|
1067
|
-
import { basename as
|
|
1299
|
+
import { basename as basename6 } from "path";
|
|
1300
|
+
import process2 from "process";
|
|
1068
1301
|
import clipboard from "clipboardy";
|
|
1069
1302
|
import { Box as Box6, Text as Text4, useApp, useInput, useStdout as useStdout2 } from "ink";
|
|
1070
1303
|
import { useCallback, useEffect as useEffect4, useMemo as useMemo2, useState as useState4 } from "react";
|
|
@@ -1108,7 +1341,7 @@ var LayoutManager = ({
|
|
|
1108
1341
|
};
|
|
1109
1342
|
|
|
1110
1343
|
// src/presentation/components/ProjectList.tsx
|
|
1111
|
-
import { basename as
|
|
1344
|
+
import { basename as basename5 } from "path";
|
|
1112
1345
|
import { Box as Box3 } from "ink";
|
|
1113
1346
|
import { useMemo } from "react";
|
|
1114
1347
|
|
|
@@ -1258,7 +1491,7 @@ var extractRootFolder = (repository) => {
|
|
|
1258
1491
|
if (!repository?.root) {
|
|
1259
1492
|
return void 0;
|
|
1260
1493
|
}
|
|
1261
|
-
const base =
|
|
1494
|
+
const base = basename5(repository.root);
|
|
1262
1495
|
return base || void 0;
|
|
1263
1496
|
};
|
|
1264
1497
|
var formatProjectName = (projectTitle, repository, useGitRootName) => {
|
|
@@ -1691,11 +1924,12 @@ var extractRootFolder2 = (repository) => {
|
|
|
1691
1924
|
if (!repository?.root) {
|
|
1692
1925
|
return void 0;
|
|
1693
1926
|
}
|
|
1694
|
-
const base =
|
|
1927
|
+
const base = basename6(repository.root);
|
|
1695
1928
|
return base || void 0;
|
|
1696
1929
|
};
|
|
1697
1930
|
var minimumVisibleProjectCount = 4;
|
|
1698
|
-
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`;
|
|
1699
1933
|
var getCopyTargetPath = (view) => {
|
|
1700
1934
|
const root = view.repository?.root;
|
|
1701
1935
|
return root && root.length > 0 ? root : view.project.path;
|
|
@@ -1703,6 +1937,8 @@ var getCopyTargetPath = (view) => {
|
|
|
1703
1937
|
var App = ({
|
|
1704
1938
|
projects,
|
|
1705
1939
|
onLaunch,
|
|
1940
|
+
onLaunchWithEditor,
|
|
1941
|
+
onLaunchEditorOnly,
|
|
1706
1942
|
onTerminate,
|
|
1707
1943
|
onRefresh,
|
|
1708
1944
|
useGitRootName = true
|
|
@@ -1799,9 +2035,9 @@ var App = ({
|
|
|
1799
2035
|
const handleSigint = () => {
|
|
1800
2036
|
exit();
|
|
1801
2037
|
};
|
|
1802
|
-
|
|
2038
|
+
process2.on("SIGINT", handleSigint);
|
|
1803
2039
|
return () => {
|
|
1804
|
-
|
|
2040
|
+
process2.off("SIGINT", handleSigint);
|
|
1805
2041
|
};
|
|
1806
2042
|
}, [exit]);
|
|
1807
2043
|
const limit = Math.max(1, visibleCount);
|
|
@@ -1948,6 +2184,100 @@ var App = ({
|
|
|
1948
2184
|
}, 3e3);
|
|
1949
2185
|
}
|
|
1950
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]);
|
|
1951
2281
|
const terminateSelected = useCallback(async () => {
|
|
1952
2282
|
const projectView = sortedProjects[index];
|
|
1953
2283
|
if (!projectView) {
|
|
@@ -2151,10 +2481,18 @@ var App = ({
|
|
|
2151
2481
|
void terminateSelected();
|
|
2152
2482
|
return;
|
|
2153
2483
|
}
|
|
2484
|
+
if (input === "\xF8" || input === "o" && key.meta) {
|
|
2485
|
+
void launchEditorOnly();
|
|
2486
|
+
return;
|
|
2487
|
+
}
|
|
2154
2488
|
if (input === "o") {
|
|
2155
2489
|
void launchSelected();
|
|
2156
2490
|
return;
|
|
2157
2491
|
}
|
|
2492
|
+
if (input === "O") {
|
|
2493
|
+
void launchSelectedWithEditor();
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2158
2496
|
if (input === "r") {
|
|
2159
2497
|
void refreshProjects();
|
|
2160
2498
|
return;
|
|
@@ -2239,7 +2577,7 @@ var App = ({
|
|
|
2239
2577
|
// src/index.tsx
|
|
2240
2578
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2241
2579
|
var bootstrap = async () => {
|
|
2242
|
-
const isWindows =
|
|
2580
|
+
const isWindows = process3.platform === "win32";
|
|
2243
2581
|
const unityHubReader = isWindows ? new WinUnityHubProjectsReader() : new MacUnityHubProjectsReader();
|
|
2244
2582
|
const gitRepositoryInfoReader = new GitRepositoryInfoReader();
|
|
2245
2583
|
const lockStatusReader = new UnityLockStatusReader();
|
|
@@ -2268,10 +2606,21 @@ var bootstrap = async () => {
|
|
|
2268
2606
|
unityProcessTerminator,
|
|
2269
2607
|
unityTempDirectoryCleaner
|
|
2270
2608
|
);
|
|
2271
|
-
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");
|
|
2272
2621
|
try {
|
|
2273
2622
|
const rawModeSupported = Boolean(
|
|
2274
|
-
|
|
2623
|
+
process3.stdin.isTTY && typeof process3.stdin.setRawMode === "function"
|
|
2275
2624
|
);
|
|
2276
2625
|
if (!rawModeSupported) {
|
|
2277
2626
|
const message = [
|
|
@@ -2284,7 +2633,7 @@ var bootstrap = async () => {
|
|
|
2284
2633
|
"Details: https://github.com/vadimdemedes/ink/#israwmodesupported"
|
|
2285
2634
|
].join("\n");
|
|
2286
2635
|
console.error(message);
|
|
2287
|
-
|
|
2636
|
+
process3.exitCode = 1;
|
|
2288
2637
|
return;
|
|
2289
2638
|
}
|
|
2290
2639
|
const theme = await detectTerminalTheme();
|
|
@@ -2295,6 +2644,8 @@ var bootstrap = async () => {
|
|
|
2295
2644
|
{
|
|
2296
2645
|
projects,
|
|
2297
2646
|
onLaunch: (project) => launchProjectUseCase.execute(project),
|
|
2647
|
+
onLaunchWithEditor: (project) => launchWithEditorUseCase.execute(project),
|
|
2648
|
+
onLaunchEditorOnly: (project) => launchEditorOnlyUseCase.execute(project),
|
|
2298
2649
|
onTerminate: (project) => terminateProjectUseCase.execute(project),
|
|
2299
2650
|
onRefresh: () => listProjectsUseCase.execute(),
|
|
2300
2651
|
useGitRootName
|
|
@@ -2302,15 +2653,15 @@ var bootstrap = async () => {
|
|
|
2302
2653
|
) })
|
|
2303
2654
|
);
|
|
2304
2655
|
await waitUntilExit();
|
|
2305
|
-
|
|
2656
|
+
process3.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
2306
2657
|
} catch (error) {
|
|
2307
2658
|
const message = error instanceof Error ? error.message : String(error);
|
|
2308
2659
|
console.error(message);
|
|
2309
|
-
|
|
2660
|
+
process3.exitCode = 1;
|
|
2310
2661
|
}
|
|
2311
2662
|
};
|
|
2312
2663
|
void bootstrap().catch((error) => {
|
|
2313
2664
|
const message = error instanceof Error ? error.message : String(error);
|
|
2314
2665
|
console.error(message);
|
|
2315
|
-
|
|
2666
|
+
process3.exitCode = 1;
|
|
2316
2667
|
});
|