projax 3.3.6 → 3.3.9
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 +24 -4
- package/dist/prxi.tsx +135 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -133,12 +133,32 @@ async function startAPIServer(silent = false) {
|
|
|
133
133
|
console.log('Starting API server...');
|
|
134
134
|
}
|
|
135
135
|
try {
|
|
136
|
-
spawn('node', [apiPath], {
|
|
136
|
+
const child = spawn('node', [apiPath], {
|
|
137
137
|
detached: true,
|
|
138
|
-
stdio: 'ignore',
|
|
139
|
-
})
|
|
138
|
+
stdio: silent ? 'ignore' : 'inherit',
|
|
139
|
+
});
|
|
140
|
+
// When not silent, keep the process attached for a moment to see any immediate errors
|
|
140
141
|
if (!silent) {
|
|
141
|
-
|
|
142
|
+
await new Promise((resolve, reject) => {
|
|
143
|
+
const timeout = setTimeout(() => {
|
|
144
|
+
child.unref();
|
|
145
|
+
console.log('✓ API server started');
|
|
146
|
+
resolve(true);
|
|
147
|
+
}, 1000);
|
|
148
|
+
child.on('error', (err) => {
|
|
149
|
+
clearTimeout(timeout);
|
|
150
|
+
reject(err);
|
|
151
|
+
});
|
|
152
|
+
child.on('exit', (code) => {
|
|
153
|
+
if (code !== 0 && code !== null) {
|
|
154
|
+
clearTimeout(timeout);
|
|
155
|
+
reject(new Error(`API server exited with code ${code}`));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
child.unref();
|
|
142
162
|
}
|
|
143
163
|
return true;
|
|
144
164
|
}
|
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
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "3.3.9",
|
|
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": {
|