unity-hub-cli 0.13.1 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +180 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -263,6 +263,101 @@ var NodeProcessLauncher = class {
263
263
  }
264
264
  };
265
265
 
266
+ // src/infrastructure/terminalTheme.ts
267
+ import { createInterface } from "readline";
268
+ var parseOsc11Response = (response) => {
269
+ const rgbMatch = response.match(/rgb:([0-9a-fA-F]+)\/([0-9a-fA-F]+)\/([0-9a-fA-F]+)/);
270
+ if (!rgbMatch) {
271
+ return void 0;
272
+ }
273
+ const [, rHex, gHex, bHex] = rgbMatch;
274
+ if (!rHex || !gHex || !bHex) {
275
+ return void 0;
276
+ }
277
+ const normalizeColorValue = (hex) => {
278
+ const value = parseInt(hex, 16);
279
+ if (hex.length === 4) {
280
+ return Math.floor(value / 256);
281
+ }
282
+ return value;
283
+ };
284
+ return {
285
+ r: normalizeColorValue(rHex),
286
+ g: normalizeColorValue(gHex),
287
+ b: normalizeColorValue(bHex)
288
+ };
289
+ };
290
+ var calculateRelativeLuminance = (color) => {
291
+ const toLinear = (value) => {
292
+ const normalized = value / 255;
293
+ if (normalized <= 0.03928) {
294
+ return normalized / 12.92;
295
+ }
296
+ return Math.pow((normalized + 0.055) / 1.055, 2.4);
297
+ };
298
+ const rLinear = toLinear(color.r);
299
+ const gLinear = toLinear(color.g);
300
+ const bLinear = toLinear(color.b);
301
+ return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear;
302
+ };
303
+ var determineThemeFromLuminance = (luminance) => {
304
+ const darkThreshold = 0.5;
305
+ return luminance < darkThreshold ? "dark" : "light";
306
+ };
307
+ var queryTerminalBackgroundColor = async (timeoutMs) => {
308
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
309
+ return void 0;
310
+ }
311
+ return new Promise((resolve4) => {
312
+ let responseBuffer = "";
313
+ let resolved = false;
314
+ if (process.stdin.isTTY) {
315
+ process.stdin.setRawMode(true);
316
+ }
317
+ const rl = createInterface({
318
+ input: process.stdin,
319
+ escapeCodeTimeout: timeoutMs
320
+ });
321
+ let timeoutId;
322
+ const onData = (chunk) => {
323
+ responseBuffer += chunk.toString();
324
+ if (responseBuffer.includes("\x07") || responseBuffer.includes("\x1B\\")) {
325
+ if (!resolved) {
326
+ resolved = true;
327
+ const color = parseOsc11Response(responseBuffer);
328
+ cleanup();
329
+ resolve4(color);
330
+ }
331
+ }
332
+ };
333
+ const cleanup = () => {
334
+ clearTimeout(timeoutId);
335
+ rl.close();
336
+ process.stdin.off("data", onData);
337
+ if (process.stdin.isTTY && process.stdin.isRaw) {
338
+ process.stdin.setRawMode(false);
339
+ }
340
+ };
341
+ timeoutId = setTimeout(() => {
342
+ if (!resolved) {
343
+ resolved = true;
344
+ cleanup();
345
+ resolve4(void 0);
346
+ }
347
+ }, timeoutMs);
348
+ process.stdin.on("data", onData);
349
+ process.stdout.write("\x1B]11;?\x07");
350
+ });
351
+ };
352
+ var detectTerminalTheme = async (timeoutMs = 100) => {
353
+ const backgroundColor = await queryTerminalBackgroundColor(timeoutMs);
354
+ if (!backgroundColor) {
355
+ return "dark";
356
+ }
357
+ const luminance = calculateRelativeLuminance(backgroundColor);
358
+ return determineThemeFromLuminance(luminance);
359
+ };
360
+
266
361
  // src/infrastructure/unityhub.ts
267
362
  import { readFile as readFile2, writeFile } from "fs/promises";
268
363
  import { basename } from "path";
@@ -652,6 +747,13 @@ var extractProjectPath = (command) => {
652
747
  var isUnityMainProcess = (command) => {
653
748
  return UNITY_EXECUTABLE_PATTERN.test(command);
654
749
  };
750
+ var isUnityAuxiliaryProcess = (command) => {
751
+ const normalized = command.toLowerCase();
752
+ if (normalized.includes("-batchmode")) {
753
+ return true;
754
+ }
755
+ return normalized.includes("assetimportworker");
756
+ };
655
757
  var isProcessMissingError = (error) => {
656
758
  if (typeof error !== "object" || error === null) {
657
759
  return false;
@@ -698,6 +800,9 @@ var MacUnityProcessReader = class {
698
800
  if (!isUnityMainProcess(command)) {
699
801
  return void 0;
700
802
  }
803
+ if (isUnityAuxiliaryProcess(command)) {
804
+ return void 0;
805
+ }
701
806
  const projectArgument = extractProjectPath(command);
702
807
  if (!projectArgument) {
703
808
  return void 0;
@@ -1007,6 +1112,56 @@ import { basename as basename3 } from "path";
1007
1112
  import { Box as Box3 } from "ink";
1008
1113
  import { useMemo } from "react";
1009
1114
 
1115
+ // src/presentation/theme.ts
1116
+ import { createContext, createElement, useContext } from "react";
1117
+ var darkPalette = {
1118
+ projectName: "#abd8e7",
1119
+ // Light cyan
1120
+ branch: "#e3839c",
1121
+ // Pink
1122
+ path: "#719bd8",
1123
+ // Blue
1124
+ border: "green",
1125
+ // Green
1126
+ status: "yellow",
1127
+ // Yellow
1128
+ focus: "green"
1129
+ // Green
1130
+ };
1131
+ var lightPalette = {
1132
+ projectName: "#0044aa",
1133
+ // Deep blue
1134
+ branch: "#991144",
1135
+ // Deep magenta
1136
+ path: "#1a4570",
1137
+ // Deep blue
1138
+ border: "#006400",
1139
+ // Dark green
1140
+ status: "#cc6600",
1141
+ // Dark orange (more visible)
1142
+ focus: "#006400"
1143
+ // Dark green
1144
+ };
1145
+ var getColorPalette = (theme) => {
1146
+ return theme === "dark" ? darkPalette : lightPalette;
1147
+ };
1148
+ var defaultThemeContext = {
1149
+ theme: "dark",
1150
+ colors: darkPalette
1151
+ };
1152
+ var ThemeContext = createContext(defaultThemeContext);
1153
+ var useThemeColors = () => {
1154
+ const context = useContext(ThemeContext);
1155
+ return context.colors;
1156
+ };
1157
+ var ThemeProvider = ({ theme, children }) => {
1158
+ const value = {
1159
+ theme,
1160
+ colors: getColorPalette(theme)
1161
+ };
1162
+ return createElement(ThemeContext.Provider, { value }, children);
1163
+ };
1164
+
1010
1165
  // src/presentation/utils/path.ts
1011
1166
  var homeDirectory = process.env.HOME ?? process.env.USERPROFILE ?? "";
1012
1167
  var normalizedHomeDirectory = homeDirectory.replace(/\\/g, "/");
@@ -1060,13 +1215,14 @@ var ProjectRow = ({
1060
1215
  showSpacer
1061
1216
  }) => {
1062
1217
  const { stdout } = useStdout();
1218
+ const colors = useThemeColors();
1063
1219
  const computedCenterWidth = typeof stdout?.columns === "number" ? Math.max(0, stdout.columns - 6) : void 0;
1064
1220
  const centerWidth = typeof computedCenterWidth === "number" ? Math.max(0, computedCenterWidth - (isSelected ? 1 : 0)) : void 0;
1065
1221
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", children: [
1066
1222
  /* @__PURE__ */ jsxs2(Box2, { width: 1, flexDirection: "column", alignItems: "center", marginLeft: 0, children: [
1067
- /* @__PURE__ */ jsx2(Text, { color: isSelected ? "green" : void 0, children: selectionBar }),
1068
- showBranch ? /* @__PURE__ */ jsx2(Text, { color: isSelected ? "green" : void 0, children: selectionBar }) : null,
1069
- showPath ? /* @__PURE__ */ jsx2(Text, { color: isSelected ? "green" : void 0, children: selectionBar }) : null
1223
+ /* @__PURE__ */ jsx2(Text, { color: isSelected ? colors.focus : void 0, children: selectionBar }),
1224
+ showBranch ? /* @__PURE__ */ jsx2(Text, { color: isSelected ? colors.focus : void 0, children: selectionBar }) : null,
1225
+ showPath ? /* @__PURE__ */ jsx2(Text, { color: isSelected ? colors.focus : void 0, children: selectionBar }) : null
1070
1226
  ] }),
1071
1227
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginLeft: isSelected ? 2 : 1, width: centerWidth, children: [
1072
1228
  /* @__PURE__ */ jsxs2(Text, { wrap: "truncate", children: [
@@ -1078,8 +1234,8 @@ var ProjectRow = ({
1078
1234
  updatedText ? /* @__PURE__ */ jsx2(Text, { children: ` ${updatedText}` }) : null,
1079
1235
  statusLabel && statusColor ? /* @__PURE__ */ jsx2(Text, { color: statusColor, children: ` ${statusLabel}` }) : null
1080
1236
  ] }),
1081
- showBranch ? /* @__PURE__ */ jsx2(Text, { color: "#e3839c", wrap: "truncate", children: branchLine }) : null,
1082
- showPath ? /* @__PURE__ */ jsx2(Text, { color: "#719bd8", wrap: "truncate", children: pathLine }) : null,
1237
+ showBranch ? /* @__PURE__ */ jsx2(Text, { color: colors.branch, wrap: "truncate", children: branchLine }) : null,
1238
+ showPath ? /* @__PURE__ */ jsx2(Text, { color: colors.path, wrap: "truncate", children: pathLine }) : null,
1083
1239
  showSpacer ? /* @__PURE__ */ jsx2(Text, { children: " " }) : null
1084
1240
  ] }),
1085
1241
  /* @__PURE__ */ jsxs2(Box2, { marginLeft: 1, width: 1, flexDirection: "column", alignItems: "center", children: [
@@ -1093,8 +1249,6 @@ var ProjectRow = ({
1093
1249
 
1094
1250
  // src/presentation/components/ProjectList.tsx
1095
1251
  import { jsx as jsx3 } from "react/jsx-runtime";
1096
- var PROJECT_COLOR = "#abd8e7";
1097
- var LOCK_COLOR = "yellow";
1098
1252
  var STATUS_LABELS = {
1099
1253
  idle: "",
1100
1254
  running: "[running]",
@@ -1189,6 +1343,7 @@ var ProjectList = ({
1189
1343
  launchedProjects,
1190
1344
  totalProjects
1191
1345
  }) => {
1346
+ const colors = useThemeColors();
1192
1347
  const scrollbarChars = useMemo(() => {
1193
1348
  const totalLines = totalProjects * linesPerProject;
1194
1349
  const windowProjects = visibleProjects.length;
@@ -1242,14 +1397,14 @@ var ProjectList = ({
1242
1397
  const pathScrollbar = showPath ? scrollbarChars[baseScrollbarIndex + 1 + (showBranch ? 1 : 0)] ?? " " : " ";
1243
1398
  const spacerScrollbar = scrollbarChars[baseScrollbarIndex + linesPerProject - 1] ?? " ";
1244
1399
  const statusLabel = STATUS_LABELS[displayStatus];
1245
- const statusColor = displayStatus === "running" ? LOCK_COLOR : void 0;
1400
+ const statusColor = displayStatus === "running" ? colors.status : void 0;
1246
1401
  return /* @__PURE__ */ jsx3(
1247
1402
  ProjectRow,
1248
1403
  {
1249
1404
  isSelected,
1250
1405
  selectionBar,
1251
1406
  projectName,
1252
- projectColor: PROJECT_COLOR,
1407
+ projectColor: colors.projectName,
1253
1408
  versionLabel,
1254
1409
  updatedText,
1255
1410
  statusLabel,
@@ -1303,10 +1458,11 @@ var SortPanel = ({ sortPreferences, focusedIndex, width }) => {
1303
1458
  const primaryLine = lineForPrimary(sortPreferences.primary);
1304
1459
  const directionLine = lineForDirection(sortPreferences);
1305
1460
  const favoritesLine = lineForFavorites(sortPreferences.favoritesFirst);
1461
+ const colors = useThemeColors();
1306
1462
  const Item = ({ label, selected }) => {
1307
1463
  const prefix = selected ? "> " : " ";
1308
1464
  return /* @__PURE__ */ jsxs3(Text2, { children: [
1309
- selected ? /* @__PURE__ */ jsx4(Text2, { color: "green", children: prefix }) : prefix,
1465
+ selected ? /* @__PURE__ */ jsx4(Text2, { color: colors.focus, children: prefix }) : prefix,
1310
1466
  label
1311
1467
  ] });
1312
1468
  };
@@ -1315,7 +1471,7 @@ var SortPanel = ({ sortPreferences, focusedIndex, width }) => {
1315
1471
  {
1316
1472
  flexDirection: "column",
1317
1473
  borderStyle: "round",
1318
- borderColor: "green",
1474
+ borderColor: colors.border,
1319
1475
  paddingX: 1,
1320
1476
  width,
1321
1477
  children: [
@@ -1335,10 +1491,11 @@ var lineForPath = (on) => `Show path: ${on ? "ON" : "OFF"}`;
1335
1491
  var VisibilityPanel = ({ visibility, focusedIndex, width }) => {
1336
1492
  const branchLine = lineForBranch(visibility.showBranch);
1337
1493
  const pathLine = lineForPath(visibility.showPath);
1494
+ const colors = useThemeColors();
1338
1495
  const Item = ({ label, selected }) => {
1339
1496
  const prefix = selected ? "> " : " ";
1340
1497
  return /* @__PURE__ */ jsxs4(Text3, { children: [
1341
- selected ? /* @__PURE__ */ jsx5(Text3, { color: "green", children: prefix }) : prefix,
1498
+ selected ? /* @__PURE__ */ jsx5(Text3, { color: colors.focus, children: prefix }) : prefix,
1342
1499
  label
1343
1500
  ] });
1344
1501
  };
@@ -1347,7 +1504,7 @@ var VisibilityPanel = ({ visibility, focusedIndex, width }) => {
1347
1504
  {
1348
1505
  flexDirection: "column",
1349
1506
  borderStyle: "round",
1350
- borderColor: "green",
1507
+ borderColor: colors.border,
1351
1508
  paddingX: 1,
1352
1509
  width,
1353
1510
  children: [
@@ -1552,6 +1709,7 @@ var App = ({
1552
1709
  }) => {
1553
1710
  const { exit } = useApp();
1554
1711
  const { stdout } = useStdout2();
1712
+ const colors = useThemeColors();
1555
1713
  const [projectViews, setProjectViews] = useState4(projects);
1556
1714
  const [isSortMenuOpen, setIsSortMenuOpen] = useState4(false);
1557
1715
  const [isVisibilityMenuOpen, setIsVisibilityMenuOpen] = useState4(false);
@@ -2030,7 +2188,7 @@ var App = ({
2030
2188
  {
2031
2189
  flexDirection: "column",
2032
2190
  borderStyle: "round",
2033
- borderColor: "green",
2191
+ borderColor: colors.border,
2034
2192
  width: typeof stdout?.columns === "number" ? stdout.columns : void 0,
2035
2193
  children: sortedProjects.length === 0 ? /* @__PURE__ */ jsx6(Text4, { children: "No Unity Hub projects were found." }) : /* @__PURE__ */ jsx6(
2036
2194
  ProjectList,
@@ -2117,21 +2275,22 @@ var bootstrap = async () => {
2117
2275
  );
2118
2276
  if (!rawModeSupported) {
2119
2277
  const message = [
2120
- "\u3053\u306E\u7AEF\u672B\u3067\u306F\u5BFE\u8A71\u5165\u529B\uFF08Raw mode\uFF09\u304C\u4F7F\u3048\u307E\u305B\u3093\u3002",
2121
- "PowerShell / cmd.exe \u3067\u5B9F\u884C\u3059\u308B\u304B\u3001ConPTY \u30D9\u30FC\u30B9\u306E\u30BF\u30FC\u30DF\u30CA\u30EB\uFF08Windows Terminal, VS Code/Cursor \u306E\u7D71\u5408\u30BF\u30FC\u30DF\u30CA\u30EB\uFF09\u3067 Git Bash \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
2122
- "MinTTY \u306E Git Bash \u3067\u306F\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044:",
2278
+ "Interactive input (Raw mode) is not available in this terminal.",
2279
+ "Please run in PowerShell / cmd.exe, or use Git Bash in a ConPTY-based terminal (Windows Terminal, VS Code/Cursor integrated terminal).",
2280
+ "For MinTTY Git Bash, use one of the following:",
2123
2281
  " - winpty cmd.exe /c npx unity-hub-cli",
2124
2282
  " - winpty powershell.exe -NoProfile -Command npx unity-hub-cli",
2125
- "\uFF08\u30D3\u30EB\u30C9\u6E08\u307F\u306E\u5834\u5408\uFF09npm run build && winpty node dist/index.js",
2126
- "\u8A73\u3057\u304F: https://github.com/vadimdemedes/ink/#israwmodesupported"
2283
+ "(If already built) npm run build && winpty node dist/index.js",
2284
+ "Details: https://github.com/vadimdemedes/ink/#israwmodesupported"
2127
2285
  ].join("\n");
2128
2286
  console.error(message);
2129
2287
  process2.exitCode = 1;
2130
2288
  return;
2131
2289
  }
2290
+ const theme = await detectTerminalTheme();
2132
2291
  const projects = await listProjectsUseCase.execute();
2133
2292
  const { waitUntilExit } = render(
2134
- /* @__PURE__ */ jsx7(
2293
+ /* @__PURE__ */ jsx7(ThemeProvider, { theme, children: /* @__PURE__ */ jsx7(
2135
2294
  App,
2136
2295
  {
2137
2296
  projects,
@@ -2140,7 +2299,7 @@ var bootstrap = async () => {
2140
2299
  onRefresh: () => listProjectsUseCase.execute(),
2141
2300
  useGitRootName
2142
2301
  }
2143
- )
2302
+ ) })
2144
2303
  );
2145
2304
  await waitUntilExit();
2146
2305
  process2.stdout.write("\x1B[2J\x1B[3J\x1B[H");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unity-hub-cli",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "A CLI tool that reads Unity Hub's projects and launches Unity Editor with an interactive TUI",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {