projax 3.3.6 → 3.3.7

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/prxi.tsx +135 -22
  2. package/package.json +1 -1
package/dist/prxi.tsx CHANGED
@@ -157,6 +157,82 @@ const ErrorModal: React.FC<ErrorModalProps> = ({ message, onClose }) => {
157
157
  );
158
158
  };
159
159
 
160
+ interface ScriptSelectionModalProps {
161
+ scripts: Map<string, any>;
162
+ projectName: string;
163
+ projectPath: string;
164
+ onSelect: (scriptName: string, background: boolean) => void;
165
+ onClose: () => void;
166
+ }
167
+
168
+ const ScriptSelectionModal: React.FC<ScriptSelectionModalProps> = ({
169
+ scripts,
170
+ projectName,
171
+ projectPath,
172
+ onSelect,
173
+ onClose
174
+ }) => {
175
+ const [selectedIndex, setSelectedIndex] = useState(0);
176
+ const scriptArray = Array.from(scripts.entries());
177
+
178
+ useInput((input: string, key: any) => {
179
+ if (key.escape || input === 'q') {
180
+ onClose();
181
+ return;
182
+ }
183
+
184
+ if (key.upArrow || input === 'k') {
185
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
186
+ return;
187
+ }
188
+
189
+ if (key.downArrow || input === 'j') {
190
+ setSelectedIndex((prev) => Math.min(scriptArray.length - 1, prev + 1));
191
+ return;
192
+ }
193
+
194
+ if (key.return) {
195
+ const [scriptName] = scriptArray[selectedIndex];
196
+ onSelect(scriptName, false);
197
+ return;
198
+ }
199
+
200
+ if (input === 'b') {
201
+ const [scriptName] = scriptArray[selectedIndex];
202
+ onSelect(scriptName, true);
203
+ return;
204
+ }
205
+ });
206
+
207
+ return (
208
+ <Box
209
+ flexDirection="column"
210
+ borderStyle="round"
211
+ borderColor={colors.accentCyan}
212
+ padding={1}
213
+ width={80}
214
+ >
215
+ <Text bold color={colors.accentCyan}>
216
+ Run Script - {projectName}
217
+ </Text>
218
+ <Text> </Text>
219
+ {scriptArray.map(([name, script], index) => {
220
+ const isSelected = index === selectedIndex;
221
+ return (
222
+ <Text key={name} color={isSelected ? colors.accentCyan : colors.textPrimary} bold={isSelected}>
223
+ {isSelected ? '▶ ' : ' '}
224
+ <Text color={colors.accentGreen}>{name}</Text>
225
+ {' - '}
226
+ <Text color={colors.textSecondary}>{truncateText(script.command, 50)}</Text>
227
+ </Text>
228
+ );
229
+ })}
230
+ <Text> </Text>
231
+ <Text color={colors.textSecondary}>↑↓/kj: Navigate | Enter: Run | b: Background | Esc/q: Cancel</Text>
232
+ </Box>
233
+ );
234
+ };
235
+
160
236
  interface ProjectListProps {
161
237
  projects: Project[];
162
238
  selectedIndex: number;
@@ -686,6 +762,10 @@ const App: React.FC = () => {
686
762
  const [listScrollOffset, setListScrollOffset] = useState(0);
687
763
  const [detailsScrollOffset, setDetailsScrollOffset] = useState(0);
688
764
 
765
+ // Script selection state
766
+ const [showScriptModal, setShowScriptModal] = useState(false);
767
+ const [scriptModalData, setScriptModalData] = useState<{ scripts: Map<string, any>; projectName: string; projectPath: string } | null>(null);
768
+
689
769
  // Get terminal dimensions
690
770
  const terminalHeight = process.stdout.rows || 24;
691
771
  const availableHeight = terminalHeight - 3; // Subtract status bar
@@ -896,6 +976,31 @@ const App: React.FC = () => {
896
976
  return Array.from(urls).sort();
897
977
  };
898
978
 
979
+ // Handler for script selection
980
+ const handleScriptSelect = async (scriptName: string, background: boolean) => {
981
+ if (!scriptModalData) return;
982
+
983
+ setShowScriptModal(false);
984
+ setIsLoading(true);
985
+ setLoadingMessage(`Running ${scriptName}${background ? ' in background' : ''}...`);
986
+
987
+ try {
988
+ if (background) {
989
+ await runScriptInBackground(scriptModalData.projectPath, scriptModalData.projectName, scriptName, [], false);
990
+ setIsLoading(false);
991
+ await loadRunningProcesses();
992
+ } else {
993
+ setIsLoading(false);
994
+ // Run in foreground - the CLI will exit and terminal control will be handed over
995
+ await runScript(scriptModalData.projectPath, scriptName, [], false);
996
+ exit();
997
+ }
998
+ } catch (err) {
999
+ setIsLoading(false);
1000
+ setError(err instanceof Error ? err.message : String(err));
1001
+ }
1002
+ };
1003
+
899
1004
  useInput((input: string, key: any) => {
900
1005
  // Handle search mode
901
1006
  if (showSearch) {
@@ -929,7 +1034,7 @@ const App: React.FC = () => {
929
1034
  }
930
1035
 
931
1036
  // Don't process input if modal is showing
932
- if (showHelp || isLoading || error || showUrls) {
1037
+ if (showHelp || isLoading || error || showUrls || showScriptModal) {
933
1038
  // Handle URLs modal
934
1039
  if (showUrls && (key.escape || key.return || input === 'q' || input === 'u')) {
935
1040
  setShowUrls(false);
@@ -1175,28 +1280,22 @@ const App: React.FC = () => {
1175
1280
 
1176
1281
  // Run script
1177
1282
  if (input === 'r' && selectedProject) {
1178
- setIsLoading(true);
1179
- setLoadingMessage('Select script to run...');
1180
-
1181
- setTimeout(async () => {
1182
- try {
1183
- const projectScripts = getProjectScripts(selectedProject.path);
1184
- if (projectScripts.scripts.size === 0) {
1185
- setIsLoading(false);
1186
- setError(`No scripts found in ${selectedProject.name}`);
1187
- return;
1188
- }
1189
-
1190
- setIsLoading(false);
1191
-
1192
- // For now, just show error to select a script
1193
- const scriptList = Array.from(projectScripts.scripts.keys()).join(', ');
1194
- setError(`Use CLI to run scripts: prx run ${selectedProject.name} <script>\nAvailable: ${scriptList}`);
1195
- } catch (err) {
1196
- setIsLoading(false);
1197
- setError(err instanceof Error ? err.message : String(err));
1283
+ try {
1284
+ const projectScripts = getProjectScripts(selectedProject.path);
1285
+ if (projectScripts.scripts.size === 0) {
1286
+ setError(`No scripts found in ${selectedProject.name}`);
1287
+ return;
1198
1288
  }
1199
- }, 100);
1289
+
1290
+ setScriptModalData({
1291
+ scripts: projectScripts.scripts,
1292
+ projectName: selectedProject.name,
1293
+ projectPath: selectedProject.path,
1294
+ });
1295
+ setShowScriptModal(true);
1296
+ } catch (err) {
1297
+ setError(err instanceof Error ? err.message : String(err));
1298
+ }
1200
1299
  return;
1201
1300
  }
1202
1301
 
@@ -1318,6 +1417,20 @@ const App: React.FC = () => {
1318
1417
  );
1319
1418
  }
1320
1419
 
1420
+ if (showScriptModal && scriptModalData) {
1421
+ return (
1422
+ <Box flexDirection="column" padding={1}>
1423
+ <ScriptSelectionModal
1424
+ scripts={scriptModalData.scripts}
1425
+ projectName={scriptModalData.projectName}
1426
+ projectPath={scriptModalData.projectPath}
1427
+ onSelect={handleScriptSelect}
1428
+ onClose={() => setShowScriptModal(false)}
1429
+ />
1430
+ </Box>
1431
+ );
1432
+ }
1433
+
1321
1434
  const handleTagRemove = (tag: string) => {
1322
1435
  if (selectedProject) {
1323
1436
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projax",
3
- "version": "3.3.6",
3
+ "version": "3.3.7",
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": {