projax 3.3.67 → 3.3.69

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 (3) hide show
  1. package/dist/prxi.js +53 -7
  2. package/dist/prxi.tsx +52 -6
  3. package/package.json +21 -22
package/dist/prxi.js CHANGED
@@ -816,6 +816,7 @@ const App = () => {
816
816
  const [showTerminalPanel, setShowTerminalPanel] = (0, react_1.useState)(false);
817
817
  const [terminalLogs, setTerminalLogs] = (0, react_1.useState)([]);
818
818
  const [selectedProcessPid, setSelectedProcessPid] = (0, react_1.useState)(null);
819
+ const [selectedProcessIndex, setSelectedProcessIndex] = (0, react_1.useState)(0);
819
820
  // Settings state
820
821
  const [settings, setSettings] = (0, react_1.useState)({
821
822
  editor: { type: 'vscode' },
@@ -984,6 +985,15 @@ const App = () => {
984
985
  (0, react_1.useEffect)(() => {
985
986
  applyFilterAndSort(allProjects, searchQuery, filterType, sortType);
986
987
  }, [filterType, sortType, runningProcesses]);
988
+ // Reset/clamp selectedProcessIndex when processes change
989
+ (0, react_1.useEffect)(() => {
990
+ if (runningProcesses.length === 0) {
991
+ setSelectedProcessIndex(0);
992
+ }
993
+ else {
994
+ setSelectedProcessIndex((prev) => Math.min(prev, runningProcesses.length - 1));
995
+ }
996
+ }, [runningProcesses.length]);
987
997
  const loadRunningProcesses = async () => {
988
998
  try {
989
999
  const processes = await (0, script_runner_1.getRunningProcessesClean)();
@@ -1310,10 +1320,38 @@ const App = () => {
1310
1320
  }
1311
1321
  // Handle navigation in processes view
1312
1322
  if (currentView === 'processes') {
1323
+ if (key.upArrow || input === 'k') {
1324
+ setSelectedProcessIndex((prev) => Math.max(0, prev - 1));
1325
+ return;
1326
+ }
1327
+ if (key.downArrow || input === 'j') {
1328
+ setSelectedProcessIndex((prev) => Math.min(runningProcesses.length - 1, prev + 1));
1329
+ return;
1330
+ }
1313
1331
  if (input === 'x' && runningProcesses.length > 0) {
1314
- // Stop all processes (or could select one)
1332
+ // Stop the selected process
1333
+ const selectedProc = runningProcesses[selectedProcessIndex];
1334
+ if (!selectedProc)
1335
+ return;
1336
+ setIsLoading(true);
1337
+ setLoadingMessage(`Stopping process ${selectedProc.pid}...`);
1338
+ setTimeout(async () => {
1339
+ try {
1340
+ await (0, script_runner_1.stopScript)(selectedProc.pid);
1341
+ await loadRunningProcesses();
1342
+ setIsLoading(false);
1343
+ }
1344
+ catch (err) {
1345
+ setIsLoading(false);
1346
+ setError(err instanceof Error ? err.message : String(err));
1347
+ }
1348
+ }, 100);
1349
+ return;
1350
+ }
1351
+ if (input === 'X' && runningProcesses.length > 0) {
1352
+ // Stop ALL processes
1315
1353
  setIsLoading(true);
1316
- setLoadingMessage('Stopping processes...');
1354
+ setLoadingMessage('Stopping all processes...');
1317
1355
  setTimeout(async () => {
1318
1356
  try {
1319
1357
  for (const proc of runningProcesses) {
@@ -1846,19 +1884,24 @@ const App = () => {
1846
1884
  // Ignore workspace loading errors
1847
1885
  }
1848
1886
  };
1849
- // Render Processes view placeholder
1887
+ // Render Processes view
1850
1888
  const renderProcessesView = () => (react_1.default.createElement(ink_1.Box, { flexDirection: "column", padding: 2 },
1851
1889
  react_1.default.createElement(ink_1.Text, { bold: true, color: colors.accentCyan },
1852
1890
  "Running Processes (",
1853
1891
  runningProcesses.length,
1854
1892
  ")"),
1855
1893
  react_1.default.createElement(ink_1.Text, null, " "),
1856
- runningProcesses.length === 0 ? (react_1.default.createElement(ink_1.Text, { color: colors.textTertiary }, "No running processes")) : (runningProcesses.map((proc) => {
1894
+ runningProcesses.length === 0 ? (react_1.default.createElement(ink_1.Text, { color: colors.textTertiary }, "No running processes")) : (runningProcesses.map((proc, index) => {
1857
1895
  const uptime = Math.floor((Date.now() - proc.startedAt) / 1000);
1858
1896
  const minutes = Math.floor(uptime / 60);
1859
1897
  const seconds = uptime % 60;
1860
1898
  const uptimeStr = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
1861
- return (react_1.default.createElement(ink_1.Text, { key: proc.pid, color: colors.textPrimary },
1899
+ const isSelected = index === selectedProcessIndex;
1900
+ const portsStr = proc.detectedPorts && proc.detectedPorts.length > 0
1901
+ ? ` [ports: ${proc.detectedPorts.join(', ')}]`
1902
+ : '';
1903
+ return (react_1.default.createElement(ink_1.Text, { key: proc.pid, color: isSelected ? colors.accentCyan : colors.textPrimary, bold: isSelected },
1904
+ isSelected ? '> ' : ' ',
1862
1905
  react_1.default.createElement(ink_1.Text, { color: colors.accentGreen }, "\u25CF"),
1863
1906
  " PID ",
1864
1907
  proc.pid,
@@ -1867,10 +1910,13 @@ const App = () => {
1867
1910
  " (",
1868
1911
  proc.scriptName,
1869
1912
  ") - ",
1870
- uptimeStr));
1913
+ uptimeStr,
1914
+ portsStr && react_1.default.createElement(ink_1.Text, { color: colors.accentCyan }, portsStr)));
1871
1915
  })),
1872
1916
  react_1.default.createElement(ink_1.Text, null, " "),
1873
- react_1.default.createElement(ink_1.Text, { color: colors.textTertiary }, "Press 1 to return to Projects")));
1917
+ react_1.default.createElement(ink_1.Text, { color: colors.textTertiary }, runningProcesses.length > 0
1918
+ ? 'up/down: navigate | x: stop selected | X: stop all | 1: back to projects'
1919
+ : 'Press 1 to return to Projects')));
1874
1920
  // Render Settings view
1875
1921
  const renderSettingsView = () => {
1876
1922
  const currentOptions = settingsSection === 'editor' ? settingsEditorOptions : settingsBrowserOptions;
package/dist/prxi.tsx CHANGED
@@ -1191,6 +1191,7 @@ const App: React.FC = () => {
1191
1191
  const [showTerminalPanel, setShowTerminalPanel] = useState(false);
1192
1192
  const [terminalLogs, setTerminalLogs] = useState<string[]>([]);
1193
1193
  const [selectedProcessPid, setSelectedProcessPid] = useState<number | null>(null);
1194
+ const [selectedProcessIndex, setSelectedProcessIndex] = useState(0);
1194
1195
 
1195
1196
  // Settings state
1196
1197
  const [settings, setSettings] = useState<AppSettings>({
@@ -1382,6 +1383,15 @@ const App: React.FC = () => {
1382
1383
  applyFilterAndSort(allProjects, searchQuery, filterType, sortType);
1383
1384
  }, [filterType, sortType, runningProcesses]);
1384
1385
 
1386
+ // Reset/clamp selectedProcessIndex when processes change
1387
+ useEffect(() => {
1388
+ if (runningProcesses.length === 0) {
1389
+ setSelectedProcessIndex(0);
1390
+ } else {
1391
+ setSelectedProcessIndex((prev) => Math.min(prev, runningProcesses.length - 1));
1392
+ }
1393
+ }, [runningProcesses.length]);
1394
+
1385
1395
  const loadRunningProcesses = async () => {
1386
1396
  try {
1387
1397
  const processes = await getRunningProcessesClean();
@@ -1733,10 +1743,36 @@ const App: React.FC = () => {
1733
1743
 
1734
1744
  // Handle navigation in processes view
1735
1745
  if (currentView === 'processes') {
1746
+ if (key.upArrow || input === 'k') {
1747
+ setSelectedProcessIndex((prev) => Math.max(0, prev - 1));
1748
+ return;
1749
+ }
1750
+ if (key.downArrow || input === 'j') {
1751
+ setSelectedProcessIndex((prev) => Math.min(runningProcesses.length - 1, prev + 1));
1752
+ return;
1753
+ }
1736
1754
  if (input === 'x' && runningProcesses.length > 0) {
1737
- // Stop all processes (or could select one)
1755
+ // Stop the selected process
1756
+ const selectedProc = runningProcesses[selectedProcessIndex];
1757
+ if (!selectedProc) return;
1758
+ setIsLoading(true);
1759
+ setLoadingMessage(`Stopping process ${selectedProc.pid}...`);
1760
+ setTimeout(async () => {
1761
+ try {
1762
+ await stopScript(selectedProc.pid);
1763
+ await loadRunningProcesses();
1764
+ setIsLoading(false);
1765
+ } catch (err) {
1766
+ setIsLoading(false);
1767
+ setError(err instanceof Error ? err.message : String(err));
1768
+ }
1769
+ }, 100);
1770
+ return;
1771
+ }
1772
+ if (input === 'X' && runningProcesses.length > 0) {
1773
+ // Stop ALL processes
1738
1774
  setIsLoading(true);
1739
- setLoadingMessage('Stopping processes...');
1775
+ setLoadingMessage('Stopping all processes...');
1740
1776
  setTimeout(async () => {
1741
1777
  try {
1742
1778
  for (const proc of runningProcesses) {
@@ -2439,7 +2475,7 @@ const App: React.FC = () => {
2439
2475
  }
2440
2476
  };
2441
2477
 
2442
- // Render Processes view placeholder
2478
+ // Render Processes view
2443
2479
  const renderProcessesView = () => (
2444
2480
  <Box flexDirection="column" padding={2}>
2445
2481
  <Text bold color={colors.accentCyan}>Running Processes ({runningProcesses.length})</Text>
@@ -2447,20 +2483,30 @@ const App: React.FC = () => {
2447
2483
  {runningProcesses.length === 0 ? (
2448
2484
  <Text color={colors.textTertiary}>No running processes</Text>
2449
2485
  ) : (
2450
- runningProcesses.map((proc: any) => {
2486
+ runningProcesses.map((proc: any, index: number) => {
2451
2487
  const uptime = Math.floor((Date.now() - proc.startedAt) / 1000);
2452
2488
  const minutes = Math.floor(uptime / 60);
2453
2489
  const seconds = uptime % 60;
2454
2490
  const uptimeStr = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
2491
+ const isSelected = index === selectedProcessIndex;
2492
+ const portsStr = proc.detectedPorts && proc.detectedPorts.length > 0
2493
+ ? ` [ports: ${proc.detectedPorts.join(', ')}]`
2494
+ : '';
2455
2495
  return (
2456
- <Text key={proc.pid} color={colors.textPrimary}>
2496
+ <Text key={proc.pid} color={isSelected ? colors.accentCyan : colors.textPrimary} bold={isSelected}>
2497
+ {isSelected ? '> ' : ' '}
2457
2498
  <Text color={colors.accentGreen}>●</Text> PID {proc.pid}: {proc.projectName} ({proc.scriptName}) - {uptimeStr}
2499
+ {portsStr && <Text color={colors.accentCyan}>{portsStr}</Text>}
2458
2500
  </Text>
2459
2501
  );
2460
2502
  })
2461
2503
  )}
2462
2504
  <Text> </Text>
2463
- <Text color={colors.textTertiary}>Press 1 to return to Projects</Text>
2505
+ <Text color={colors.textTertiary}>
2506
+ {runningProcesses.length > 0
2507
+ ? 'up/down: navigate | x: stop selected | X: stop all | 1: back to projects'
2508
+ : 'Press 1 to return to Projects'}
2509
+ </Text>
2464
2510
  </Box>
2465
2511
  );
2466
2512
 
package/package.json CHANGED
@@ -1,28 +1,11 @@
1
1
  {
2
2
  "name": "projax",
3
- "version": "3.3.67",
3
+ "version": "3.3.69",
4
4
  "description": "Cross-platform project management dashboard for tracking local development projects. Features CLI, Terminal UI, Desktop app, REST API, and built-in tools for test detection, port management, and script execution.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "prx": "./dist/index.js"
8
8
  },
9
- "scripts": {
10
- "build": "tsc",
11
- "typecheck": "echo \"Skipping cli typecheck (legacy)\"",
12
- "lint": "echo \"Skipping cli lint (not configured)\"",
13
- "lint:fix": "echo \"Skipping cli lint (not configured)\"",
14
- "build:electron": "cd ../desktop && pnpm run build",
15
- "copy:electron": "mkdir -p dist/electron && cp -r ../desktop/dist/* dist/electron/ && cp dist/script-runner.* dist/electron/ && cp dist/port-*.* dist/electron/ && cp -R dist/core dist/electron/",
16
- "copy:api": "mkdir -p dist/api && cp -r ../api/dist/* dist/api/ && cp ../api/package.json dist/api/",
17
- "copy:core": "mkdir -p dist/core && cp -r ../core/dist/* dist/core/",
18
- "copy:prxi": "cp src/prxi.tsx dist/prxi.tsx",
19
- "build:all": "pnpm run build && pnpm run copy:core && pnpm run build:electron && pnpm run copy:electron && pnpm run copy:api && pnpm run copy:prxi",
20
- "clean": "rm -rf dist",
21
- "test": "jest",
22
- "test:watch": "jest --watch",
23
- "test:coverage": "jest --coverage",
24
- "prepublishOnly": "pnpm run build:all"
25
- },
26
9
  "dependencies": {
27
10
  "chokidar": "^3.6.0",
28
11
  "commander": "^11.1.0",
@@ -67,12 +50,28 @@
67
50
  ],
68
51
  "repository": {
69
52
  "type": "git",
70
- "url": "git+https://github.com/josetwentyfour/projax.git"
53
+ "url": "git+https://github.com/josmanvis/projax.git"
71
54
  },
72
55
  "bugs": {
73
- "url": "https://github.com/josetwentyfour/projax/issues"
56
+ "url": "https://github.com/josmanvis/projax/issues"
74
57
  },
75
58
  "homepage": "https://projax.dev",
76
59
  "author": "",
77
- "license": "MIT"
78
- }
60
+ "license": "MIT",
61
+ "scripts": {
62
+ "build": "tsc",
63
+ "typecheck": "echo \"Skipping cli typecheck (legacy)\"",
64
+ "lint": "echo \"Skipping cli lint (not configured)\"",
65
+ "lint:fix": "echo \"Skipping cli lint (not configured)\"",
66
+ "build:electron": "cd ../desktop && pnpm run build",
67
+ "copy:electron": "mkdir -p dist/electron && cp -r ../desktop/dist/* dist/electron/ && cp dist/script-runner.* dist/electron/ && cp dist/port-*.* dist/electron/ && cp -R dist/core dist/electron/",
68
+ "copy:api": "mkdir -p dist/api && cp -r ../api/dist/* dist/api/ && cp ../api/package.json dist/api/",
69
+ "copy:core": "mkdir -p dist/core && cp -r ../core/dist/* dist/core/",
70
+ "copy:prxi": "cp src/prxi.tsx dist/prxi.tsx",
71
+ "build:all": "pnpm run build && pnpm run copy:core && pnpm run build:electron && pnpm run copy:electron && pnpm run copy:api && pnpm run copy:prxi",
72
+ "clean": "rm -rf dist",
73
+ "test": "jest",
74
+ "test:watch": "jest --watch",
75
+ "test:coverage": "jest --coverage"
76
+ }
77
+ }