unity-hub-cli 0.13.2 → 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 +170 -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";
@@ -1017,6 +1112,56 @@ import { basename as basename3 } from "path";
1017
1112
  import { Box as Box3 } from "ink";
1018
1113
  import { useMemo } from "react";
1019
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
+
1020
1165
  // src/presentation/utils/path.ts
1021
1166
  var homeDirectory = process.env.HOME ?? process.env.USERPROFILE ?? "";
1022
1167
  var normalizedHomeDirectory = homeDirectory.replace(/\\/g, "/");
@@ -1070,13 +1215,14 @@ var ProjectRow = ({
1070
1215
  showSpacer
1071
1216
  }) => {
1072
1217
  const { stdout } = useStdout();
1218
+ const colors = useThemeColors();
1073
1219
  const computedCenterWidth = typeof stdout?.columns === "number" ? Math.max(0, stdout.columns - 6) : void 0;
1074
1220
  const centerWidth = typeof computedCenterWidth === "number" ? Math.max(0, computedCenterWidth - (isSelected ? 1 : 0)) : void 0;
1075
1221
  return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "row", children: [
1076
1222
  /* @__PURE__ */ jsxs2(Box2, { width: 1, flexDirection: "column", alignItems: "center", marginLeft: 0, children: [
1077
- /* @__PURE__ */ jsx2(Text, { color: isSelected ? "green" : void 0, children: selectionBar }),
1078
- showBranch ? /* @__PURE__ */ jsx2(Text, { color: isSelected ? "green" : void 0, children: selectionBar }) : null,
1079
- 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
1080
1226
  ] }),
1081
1227
  /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginLeft: isSelected ? 2 : 1, width: centerWidth, children: [
1082
1228
  /* @__PURE__ */ jsxs2(Text, { wrap: "truncate", children: [
@@ -1088,8 +1234,8 @@ var ProjectRow = ({
1088
1234
  updatedText ? /* @__PURE__ */ jsx2(Text, { children: ` ${updatedText}` }) : null,
1089
1235
  statusLabel && statusColor ? /* @__PURE__ */ jsx2(Text, { color: statusColor, children: ` ${statusLabel}` }) : null
1090
1236
  ] }),
1091
- showBranch ? /* @__PURE__ */ jsx2(Text, { color: "#e3839c", wrap: "truncate", children: branchLine }) : null,
1092
- 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,
1093
1239
  showSpacer ? /* @__PURE__ */ jsx2(Text, { children: " " }) : null
1094
1240
  ] }),
1095
1241
  /* @__PURE__ */ jsxs2(Box2, { marginLeft: 1, width: 1, flexDirection: "column", alignItems: "center", children: [
@@ -1103,8 +1249,6 @@ var ProjectRow = ({
1103
1249
 
1104
1250
  // src/presentation/components/ProjectList.tsx
1105
1251
  import { jsx as jsx3 } from "react/jsx-runtime";
1106
- var PROJECT_COLOR = "#abd8e7";
1107
- var LOCK_COLOR = "yellow";
1108
1252
  var STATUS_LABELS = {
1109
1253
  idle: "",
1110
1254
  running: "[running]",
@@ -1199,6 +1343,7 @@ var ProjectList = ({
1199
1343
  launchedProjects,
1200
1344
  totalProjects
1201
1345
  }) => {
1346
+ const colors = useThemeColors();
1202
1347
  const scrollbarChars = useMemo(() => {
1203
1348
  const totalLines = totalProjects * linesPerProject;
1204
1349
  const windowProjects = visibleProjects.length;
@@ -1252,14 +1397,14 @@ var ProjectList = ({
1252
1397
  const pathScrollbar = showPath ? scrollbarChars[baseScrollbarIndex + 1 + (showBranch ? 1 : 0)] ?? " " : " ";
1253
1398
  const spacerScrollbar = scrollbarChars[baseScrollbarIndex + linesPerProject - 1] ?? " ";
1254
1399
  const statusLabel = STATUS_LABELS[displayStatus];
1255
- const statusColor = displayStatus === "running" ? LOCK_COLOR : void 0;
1400
+ const statusColor = displayStatus === "running" ? colors.status : void 0;
1256
1401
  return /* @__PURE__ */ jsx3(
1257
1402
  ProjectRow,
1258
1403
  {
1259
1404
  isSelected,
1260
1405
  selectionBar,
1261
1406
  projectName,
1262
- projectColor: PROJECT_COLOR,
1407
+ projectColor: colors.projectName,
1263
1408
  versionLabel,
1264
1409
  updatedText,
1265
1410
  statusLabel,
@@ -1313,10 +1458,11 @@ var SortPanel = ({ sortPreferences, focusedIndex, width }) => {
1313
1458
  const primaryLine = lineForPrimary(sortPreferences.primary);
1314
1459
  const directionLine = lineForDirection(sortPreferences);
1315
1460
  const favoritesLine = lineForFavorites(sortPreferences.favoritesFirst);
1461
+ const colors = useThemeColors();
1316
1462
  const Item = ({ label, selected }) => {
1317
1463
  const prefix = selected ? "> " : " ";
1318
1464
  return /* @__PURE__ */ jsxs3(Text2, { children: [
1319
- selected ? /* @__PURE__ */ jsx4(Text2, { color: "green", children: prefix }) : prefix,
1465
+ selected ? /* @__PURE__ */ jsx4(Text2, { color: colors.focus, children: prefix }) : prefix,
1320
1466
  label
1321
1467
  ] });
1322
1468
  };
@@ -1325,7 +1471,7 @@ var SortPanel = ({ sortPreferences, focusedIndex, width }) => {
1325
1471
  {
1326
1472
  flexDirection: "column",
1327
1473
  borderStyle: "round",
1328
- borderColor: "green",
1474
+ borderColor: colors.border,
1329
1475
  paddingX: 1,
1330
1476
  width,
1331
1477
  children: [
@@ -1345,10 +1491,11 @@ var lineForPath = (on) => `Show path: ${on ? "ON" : "OFF"}`;
1345
1491
  var VisibilityPanel = ({ visibility, focusedIndex, width }) => {
1346
1492
  const branchLine = lineForBranch(visibility.showBranch);
1347
1493
  const pathLine = lineForPath(visibility.showPath);
1494
+ const colors = useThemeColors();
1348
1495
  const Item = ({ label, selected }) => {
1349
1496
  const prefix = selected ? "> " : " ";
1350
1497
  return /* @__PURE__ */ jsxs4(Text3, { children: [
1351
- selected ? /* @__PURE__ */ jsx5(Text3, { color: "green", children: prefix }) : prefix,
1498
+ selected ? /* @__PURE__ */ jsx5(Text3, { color: colors.focus, children: prefix }) : prefix,
1352
1499
  label
1353
1500
  ] });
1354
1501
  };
@@ -1357,7 +1504,7 @@ var VisibilityPanel = ({ visibility, focusedIndex, width }) => {
1357
1504
  {
1358
1505
  flexDirection: "column",
1359
1506
  borderStyle: "round",
1360
- borderColor: "green",
1507
+ borderColor: colors.border,
1361
1508
  paddingX: 1,
1362
1509
  width,
1363
1510
  children: [
@@ -1562,6 +1709,7 @@ var App = ({
1562
1709
  }) => {
1563
1710
  const { exit } = useApp();
1564
1711
  const { stdout } = useStdout2();
1712
+ const colors = useThemeColors();
1565
1713
  const [projectViews, setProjectViews] = useState4(projects);
1566
1714
  const [isSortMenuOpen, setIsSortMenuOpen] = useState4(false);
1567
1715
  const [isVisibilityMenuOpen, setIsVisibilityMenuOpen] = useState4(false);
@@ -2040,7 +2188,7 @@ var App = ({
2040
2188
  {
2041
2189
  flexDirection: "column",
2042
2190
  borderStyle: "round",
2043
- borderColor: "green",
2191
+ borderColor: colors.border,
2044
2192
  width: typeof stdout?.columns === "number" ? stdout.columns : void 0,
2045
2193
  children: sortedProjects.length === 0 ? /* @__PURE__ */ jsx6(Text4, { children: "No Unity Hub projects were found." }) : /* @__PURE__ */ jsx6(
2046
2194
  ProjectList,
@@ -2127,21 +2275,22 @@ var bootstrap = async () => {
2127
2275
  );
2128
2276
  if (!rawModeSupported) {
2129
2277
  const message = [
2130
- "\u3053\u306E\u7AEF\u672B\u3067\u306F\u5BFE\u8A71\u5165\u529B\uFF08Raw mode\uFF09\u304C\u4F7F\u3048\u307E\u305B\u3093\u3002",
2131
- "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",
2132
- "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:",
2133
2281
  " - winpty cmd.exe /c npx unity-hub-cli",
2134
2282
  " - winpty powershell.exe -NoProfile -Command npx unity-hub-cli",
2135
- "\uFF08\u30D3\u30EB\u30C9\u6E08\u307F\u306E\u5834\u5408\uFF09npm run build && winpty node dist/index.js",
2136
- "\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"
2137
2285
  ].join("\n");
2138
2286
  console.error(message);
2139
2287
  process2.exitCode = 1;
2140
2288
  return;
2141
2289
  }
2290
+ const theme = await detectTerminalTheme();
2142
2291
  const projects = await listProjectsUseCase.execute();
2143
2292
  const { waitUntilExit } = render(
2144
- /* @__PURE__ */ jsx7(
2293
+ /* @__PURE__ */ jsx7(ThemeProvider, { theme, children: /* @__PURE__ */ jsx7(
2145
2294
  App,
2146
2295
  {
2147
2296
  projects,
@@ -2150,7 +2299,7 @@ var bootstrap = async () => {
2150
2299
  onRefresh: () => listProjectsUseCase.execute(),
2151
2300
  useGitRootName
2152
2301
  }
2153
- )
2302
+ ) })
2154
2303
  );
2155
2304
  await waitUntilExit();
2156
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.2",
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": {