create-interview-cockpit 0.10.0 → 0.12.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.
|
@@ -56,6 +56,11 @@ import {
|
|
|
56
56
|
updateNextjsFiles,
|
|
57
57
|
updateModuleFederationFiles,
|
|
58
58
|
stopNextjsSandbox,
|
|
59
|
+
startReactLabSandbox,
|
|
60
|
+
updateReactLabFiles,
|
|
61
|
+
streamReactLabCommand,
|
|
62
|
+
stopReactLabSandbox,
|
|
63
|
+
readReactLabFile,
|
|
59
64
|
} from "../api";
|
|
60
65
|
import ReactMarkdown from "react-markdown";
|
|
61
66
|
import remarkGfm from "remark-gfm";
|
|
@@ -646,6 +651,15 @@ export default function CodeRunnerModal() {
|
|
|
646
651
|
const [reactNavInput, setReactNavInput] = useState("/");
|
|
647
652
|
const [reactNavHistory, setReactNavHistory] = useState<string[]>(["/"]);
|
|
648
653
|
const [reactNavIndex, setReactNavIndex] = useState(0);
|
|
654
|
+
// Real Vite dev-server state (React lab)
|
|
655
|
+
const [viteSandboxId, setViteSandboxId] = useState<string | null>(null);
|
|
656
|
+
const [viteSandboxUrl, setViteSandboxUrl] = useState<string | null>(null);
|
|
657
|
+
const [viteStarting, setViteStarting] = useState(false);
|
|
658
|
+
const [viteError, setViteError] = useState<string | null>(null);
|
|
659
|
+
const [viteConsoleCommand, setViteConsoleCommand] = useState("npm install");
|
|
660
|
+
const [viteConsoleOutput, setViteConsoleOutput] = useState<OutputLine[]>([]);
|
|
661
|
+
const [viteConsoleRunning, setViteConsoleRunning] = useState(false);
|
|
662
|
+
const viteIframeRef = useRef<HTMLIFrameElement>(null);
|
|
649
663
|
|
|
650
664
|
// ── Sandbox output tab ("output" | "console" | "chat") ─────────────
|
|
651
665
|
const [sbxBottomTab, setSbxBottomTab] = useState<SbxBottomTab>("output");
|
|
@@ -873,6 +887,13 @@ export default function CodeRunnerModal() {
|
|
|
873
887
|
}
|
|
874
888
|
setReactPreviewSrc(null);
|
|
875
889
|
setReactClientTab("edit");
|
|
890
|
+
if (ct === "react") {
|
|
891
|
+
setViteSandboxId(null);
|
|
892
|
+
setViteSandboxUrl(null);
|
|
893
|
+
setViteError(null);
|
|
894
|
+
setViteConsoleOutput([]);
|
|
895
|
+
setViteConsoleRunning(false);
|
|
896
|
+
}
|
|
876
897
|
if (ct === "module-federation") {
|
|
877
898
|
setServerCollapsed(true);
|
|
878
899
|
setClientCollapsed(false);
|
|
@@ -901,7 +922,13 @@ export default function CodeRunnerModal() {
|
|
|
901
922
|
if (sbxBottomTab === "console") {
|
|
902
923
|
mfConsoleEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
903
924
|
}
|
|
904
|
-
}, [
|
|
925
|
+
}, [
|
|
926
|
+
mfConsoleOutput,
|
|
927
|
+
viteConsoleOutput,
|
|
928
|
+
mfConsoleRunning,
|
|
929
|
+
viteConsoleRunning,
|
|
930
|
+
sbxBottomTab,
|
|
931
|
+
]);
|
|
905
932
|
|
|
906
933
|
useEffect(() => {
|
|
907
934
|
if (clientType !== "module-federation") return;
|
|
@@ -1278,7 +1305,11 @@ export default function CodeRunnerModal() {
|
|
|
1278
1305
|
return files["app/page.tsx"]
|
|
1279
1306
|
? "app/page.tsx"
|
|
1280
1307
|
: (Object.keys(files)[0] ?? "");
|
|
1281
|
-
return files["
|
|
1308
|
+
return files["main.tsx"]
|
|
1309
|
+
? "main.tsx"
|
|
1310
|
+
: files["App.tsx"]
|
|
1311
|
+
? "App.tsx"
|
|
1312
|
+
: (Object.keys(files)[0] ?? "");
|
|
1282
1313
|
};
|
|
1283
1314
|
|
|
1284
1315
|
const refreshPreview = useCallback(
|
|
@@ -1325,6 +1356,15 @@ export default function CodeRunnerModal() {
|
|
|
1325
1356
|
entry = resolved;
|
|
1326
1357
|
} else {
|
|
1327
1358
|
entry = getReactEntry(reactFiles, type);
|
|
1359
|
+
if (
|
|
1360
|
+
entry === "main.tsx" &&
|
|
1361
|
+
!/export\s+function\s+mount\s*\(/.test(reactFiles[entry] ?? "") &&
|
|
1362
|
+
reactFiles["App.tsx"]
|
|
1363
|
+
) {
|
|
1364
|
+
// Compatibility path for older React labs created before main.tsx
|
|
1365
|
+
// exported an explicit mount() entry contract.
|
|
1366
|
+
entry = "App.tsx";
|
|
1367
|
+
}
|
|
1328
1368
|
}
|
|
1329
1369
|
if (!entry) return;
|
|
1330
1370
|
const html = generatePreviewHTML(
|
|
@@ -1349,6 +1389,19 @@ export default function CodeRunnerModal() {
|
|
|
1349
1389
|
}
|
|
1350
1390
|
}, [reactFiles, reactClientTab, reactPreviewSrc, refreshPreview]);
|
|
1351
1391
|
|
|
1392
|
+
// Auto-sync file edits to the running Vite server (HMR picks them up)
|
|
1393
|
+
const viteFileSyncTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
1394
|
+
useEffect(() => {
|
|
1395
|
+
if (!viteSandboxId) return;
|
|
1396
|
+
if (viteFileSyncTimer.current) clearTimeout(viteFileSyncTimer.current);
|
|
1397
|
+
viteFileSyncTimer.current = setTimeout(() => {
|
|
1398
|
+
void updateReactLabFiles(viteSandboxId, reactFiles);
|
|
1399
|
+
}, 400);
|
|
1400
|
+
return () => {
|
|
1401
|
+
if (viteFileSyncTimer.current) clearTimeout(viteFileSyncTimer.current);
|
|
1402
|
+
};
|
|
1403
|
+
}, [reactFiles, viteSandboxId]);
|
|
1404
|
+
|
|
1352
1405
|
/** Navigate to a new path (updates URL bar + history + re-renders preview). */
|
|
1353
1406
|
const navigatePreview = useCallback(
|
|
1354
1407
|
(to: string) => {
|
|
@@ -1365,12 +1418,23 @@ export default function CodeRunnerModal() {
|
|
|
1365
1418
|
[reactNavIndex, refreshPreview],
|
|
1366
1419
|
);
|
|
1367
1420
|
|
|
1368
|
-
// Listen for rlab-nav messages from the preview iframe
|
|
1421
|
+
// Listen for rlab-nav and rlab-err messages from the preview iframe
|
|
1369
1422
|
useEffect(() => {
|
|
1370
1423
|
const handler = (e: MessageEvent) => {
|
|
1371
1424
|
if (e.data?.type === "rlab-nav" && typeof e.data.to === "string") {
|
|
1372
1425
|
navigatePreview(e.data.to);
|
|
1373
1426
|
}
|
|
1427
|
+
if (e.data?.type === "rlab-err" && typeof e.data.error === "string") {
|
|
1428
|
+
setOutput((prev) => [
|
|
1429
|
+
...prev,
|
|
1430
|
+
{
|
|
1431
|
+
kind: "stderr",
|
|
1432
|
+
text: `Preview error: ${e.data.error}`,
|
|
1433
|
+
source: "client",
|
|
1434
|
+
},
|
|
1435
|
+
]);
|
|
1436
|
+
setSbxBottomTab("output");
|
|
1437
|
+
}
|
|
1374
1438
|
};
|
|
1375
1439
|
window.addEventListener("message", handler);
|
|
1376
1440
|
return () => window.removeEventListener("message", handler);
|
|
@@ -1782,16 +1846,84 @@ export default function CodeRunnerModal() {
|
|
|
1782
1846
|
current === "console" || current === "inspector" ? "output" : current,
|
|
1783
1847
|
);
|
|
1784
1848
|
}
|
|
1785
|
-
|
|
1849
|
+
if (prev === "react" && clientType !== "react" && viteSandboxId) {
|
|
1850
|
+
void stopReactLabSandbox(viteSandboxId);
|
|
1851
|
+
setViteSandboxId(null);
|
|
1852
|
+
setViteSandboxUrl(null);
|
|
1853
|
+
setViteConsoleRunning(false);
|
|
1854
|
+
setSbxBottomTab((current) =>
|
|
1855
|
+
current === "console" ? "output" : current,
|
|
1856
|
+
);
|
|
1857
|
+
}
|
|
1858
|
+
}, [clientType, nxSandboxId, mfSandboxId, viteSandboxId]);
|
|
1786
1859
|
|
|
1787
1860
|
// Clean up on unmount
|
|
1788
1861
|
useEffect(() => {
|
|
1789
1862
|
return () => {
|
|
1790
1863
|
if (nxSandboxId) void stopNextjsSandbox(nxSandboxId);
|
|
1791
1864
|
if (mfSandboxId) void stopModuleFederationSandbox(mfSandboxId);
|
|
1865
|
+
if (viteSandboxId) void stopReactLabSandbox(viteSandboxId);
|
|
1792
1866
|
};
|
|
1793
1867
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1794
|
-
}, [nxSandboxId, mfSandboxId]);
|
|
1868
|
+
}, [nxSandboxId, mfSandboxId, viteSandboxId]);
|
|
1869
|
+
|
|
1870
|
+
const startViteServer = useCallback(async () => {
|
|
1871
|
+
if (viteStarting) return;
|
|
1872
|
+
setViteStarting(true);
|
|
1873
|
+
setViteError(null);
|
|
1874
|
+
setViteConsoleOutput([]);
|
|
1875
|
+
try {
|
|
1876
|
+
const info = await startReactLabSandbox(reactFiles);
|
|
1877
|
+
setViteSandboxId(info.id);
|
|
1878
|
+
setViteSandboxUrl(info.url);
|
|
1879
|
+
setReactClientTab("preview");
|
|
1880
|
+
} catch (err: any) {
|
|
1881
|
+
setViteError(err?.message ?? String(err));
|
|
1882
|
+
} finally {
|
|
1883
|
+
setViteStarting(false);
|
|
1884
|
+
}
|
|
1885
|
+
}, [viteStarting, reactFiles]);
|
|
1886
|
+
|
|
1887
|
+
const runViteCommand = useCallback(async () => {
|
|
1888
|
+
if (!viteSandboxId || viteConsoleRunning) return;
|
|
1889
|
+
const command = viteConsoleCommand.trim();
|
|
1890
|
+
if (!command) return;
|
|
1891
|
+
setViteError(null);
|
|
1892
|
+
setViteConsoleRunning(true);
|
|
1893
|
+
setSbxBottomTab("console");
|
|
1894
|
+
try {
|
|
1895
|
+
await streamReactLabCommand({ id: viteSandboxId, command }, (message) => {
|
|
1896
|
+
if (message.type === "output") {
|
|
1897
|
+
setViteConsoleOutput((prev) => [
|
|
1898
|
+
...prev,
|
|
1899
|
+
{ kind: message.kind, text: message.text, source: "server" },
|
|
1900
|
+
]);
|
|
1901
|
+
} else if (message.type === "error") {
|
|
1902
|
+
setViteConsoleOutput((prev) => [
|
|
1903
|
+
...prev,
|
|
1904
|
+
{ kind: "stderr", text: message.error, source: "server" },
|
|
1905
|
+
]);
|
|
1906
|
+
}
|
|
1907
|
+
});
|
|
1908
|
+
// After any npm command that mutates dependencies, read package.json back
|
|
1909
|
+
// from disk so the editor reflects what npm actually wrote.
|
|
1910
|
+
const tokens = command.trim().split(/\s+/);
|
|
1911
|
+
const sub = tokens[1];
|
|
1912
|
+
if (
|
|
1913
|
+
sub &&
|
|
1914
|
+
/^(install|i|uninstall|un|remove|rm|r|update|up|upgrade|add)$/.test(sub)
|
|
1915
|
+
) {
|
|
1916
|
+
const updated = await readReactLabFile(viteSandboxId, "package.json");
|
|
1917
|
+
if (updated !== null) {
|
|
1918
|
+
setReactFiles((prev) => ({ ...prev, "package.json": updated }));
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
} catch (err: any) {
|
|
1922
|
+
setViteError(err?.message ?? String(err));
|
|
1923
|
+
} finally {
|
|
1924
|
+
setViteConsoleRunning(false);
|
|
1925
|
+
}
|
|
1926
|
+
}, [viteConsoleCommand, viteConsoleRunning, viteSandboxId]);
|
|
1795
1927
|
|
|
1796
1928
|
const handleClientTypeChange = useCallback(
|
|
1797
1929
|
(ct: FrontendClientType) => {
|
|
@@ -1808,6 +1940,10 @@ export default function CodeRunnerModal() {
|
|
|
1808
1940
|
current === "console" || current === "inspector" ? "output" : current,
|
|
1809
1941
|
);
|
|
1810
1942
|
}
|
|
1943
|
+
if (ct !== "react") {
|
|
1944
|
+
setViteConsoleOutput([]);
|
|
1945
|
+
setViteConsoleRunning(false);
|
|
1946
|
+
}
|
|
1811
1947
|
if (ct !== "script") {
|
|
1812
1948
|
const defs = defaultForType(ct);
|
|
1813
1949
|
setReactFiles(defs.files);
|
|
@@ -2111,6 +2247,10 @@ export default function CodeRunnerModal() {
|
|
|
2111
2247
|
const isActiveModuleFederationGeneratedFile =
|
|
2112
2248
|
clientType === "module-federation" &&
|
|
2113
2249
|
moduleFederationGeneratedFileSet.has(reactActiveFile);
|
|
2250
|
+
const usesClientExplorer =
|
|
2251
|
+
clientType === "react" ||
|
|
2252
|
+
clientType === "nextjs" ||
|
|
2253
|
+
clientType === "module-federation";
|
|
2114
2254
|
const mfInspectorRuntimeCount = new Set(
|
|
2115
2255
|
mfInspectorEvents.map((event) => event.runtimeId),
|
|
2116
2256
|
).size;
|
|
@@ -2398,6 +2538,161 @@ export default function CodeRunnerModal() {
|
|
|
2398
2538
|
const mfInspectorIssueCount =
|
|
2399
2539
|
mfInspectorDifferentRemoteCount + mfInspectorErroredRemoteCount;
|
|
2400
2540
|
const mfInspectorViewCopy = MF_INSPECTOR_VIEW_COPY[mfInspectorView];
|
|
2541
|
+
const mfInspectorWorkspacePackageMap = new Map<
|
|
2542
|
+
string,
|
|
2543
|
+
{
|
|
2544
|
+
apps: string[];
|
|
2545
|
+
requiredVersions: Array<{ app: string; version: string | false | null }>;
|
|
2546
|
+
}
|
|
2547
|
+
>();
|
|
2548
|
+
|
|
2549
|
+
for (const [filePath, fileContents] of Object.entries(reactFiles)) {
|
|
2550
|
+
const match = filePath.match(/^apps\/([^/]+)\/package\.json$/);
|
|
2551
|
+
if (!match) continue;
|
|
2552
|
+
|
|
2553
|
+
try {
|
|
2554
|
+
const packageJson = JSON.parse(fileContents);
|
|
2555
|
+
const appName = match[1];
|
|
2556
|
+
const dependencyVersions = asInspectorRecord(packageJson.dependencies);
|
|
2557
|
+
|
|
2558
|
+
for (const [packageName, versionValue] of Object.entries(
|
|
2559
|
+
dependencyVersions,
|
|
2560
|
+
)) {
|
|
2561
|
+
const existing = mfInspectorWorkspacePackageMap.get(packageName) ?? {
|
|
2562
|
+
apps: [],
|
|
2563
|
+
requiredVersions: [],
|
|
2564
|
+
};
|
|
2565
|
+
|
|
2566
|
+
if (!existing.apps.includes(appName)) {
|
|
2567
|
+
existing.apps.push(appName);
|
|
2568
|
+
}
|
|
2569
|
+
existing.requiredVersions.push({
|
|
2570
|
+
app: appName,
|
|
2571
|
+
version: typeof versionValue === "string" ? versionValue : null,
|
|
2572
|
+
});
|
|
2573
|
+
mfInspectorWorkspacePackageMap.set(packageName, existing);
|
|
2574
|
+
}
|
|
2575
|
+
} catch {
|
|
2576
|
+
// Ignore malformed package.json edits while the user is typing.
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
const mfInspectorDeclaredSharedPackageMap = new Map<
|
|
2581
|
+
string,
|
|
2582
|
+
{
|
|
2583
|
+
apps: string[];
|
|
2584
|
+
requiredVersions: Array<{ app: string; version: string | false | null }>;
|
|
2585
|
+
singletonPreferred: boolean;
|
|
2586
|
+
}
|
|
2587
|
+
>();
|
|
2588
|
+
|
|
2589
|
+
for (const [packageName, packageInfo] of mfInspectorWorkspacePackageMap) {
|
|
2590
|
+
mfInspectorDeclaredSharedPackageMap.set(packageName, {
|
|
2591
|
+
apps: [...packageInfo.apps],
|
|
2592
|
+
requiredVersions: [...packageInfo.requiredVersions],
|
|
2593
|
+
singletonPreferred: false,
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
for (const { app, declaredConfig } of mfInspectorDeclaredConfigs) {
|
|
2598
|
+
const sharedPackages = asInspectorRecord(declaredConfig.shared);
|
|
2599
|
+
for (const packageName of Object.keys(sharedPackages)) {
|
|
2600
|
+
const packageConfig = asInspectorRecord(sharedPackages[packageName]);
|
|
2601
|
+
const existing = mfInspectorDeclaredSharedPackageMap.get(packageName) ?? {
|
|
2602
|
+
apps: [],
|
|
2603
|
+
requiredVersions: [],
|
|
2604
|
+
singletonPreferred: false,
|
|
2605
|
+
};
|
|
2606
|
+
|
|
2607
|
+
if (!existing.apps.includes(app)) {
|
|
2608
|
+
existing.apps.push(app);
|
|
2609
|
+
}
|
|
2610
|
+
existing.requiredVersions.push({
|
|
2611
|
+
app,
|
|
2612
|
+
version:
|
|
2613
|
+
typeof packageConfig.requiredVersion === "string"
|
|
2614
|
+
? packageConfig.requiredVersion
|
|
2615
|
+
: packageConfig.requiredVersion === false
|
|
2616
|
+
? false
|
|
2617
|
+
: null,
|
|
2618
|
+
});
|
|
2619
|
+
existing.singletonPreferred =
|
|
2620
|
+
existing.singletonPreferred || packageConfig.singleton === true;
|
|
2621
|
+
mfInspectorDeclaredSharedPackageMap.set(packageName, existing);
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
const mfInspectorShareScopeNames = Array.from(
|
|
2626
|
+
new Set([
|
|
2627
|
+
...Object.keys(mfShareScopeForMatrix?.shareScopes ?? {}).filter(
|
|
2628
|
+
(scopeName) => scopeName !== "__error",
|
|
2629
|
+
),
|
|
2630
|
+
...(mfInspectorDeclaredSharedPackageMap.size > 0 ? ["default"] : []),
|
|
2631
|
+
]),
|
|
2632
|
+
);
|
|
2633
|
+
const mfInspectorRuntimeSharedPackageMap = new Map<
|
|
2634
|
+
string,
|
|
2635
|
+
{ registeredVersionCount: number; loaded: boolean }
|
|
2636
|
+
>();
|
|
2637
|
+
|
|
2638
|
+
for (const [scopeName, scopeValue] of Object.entries(
|
|
2639
|
+
mfShareScopeForMatrix?.shareScopes ?? {},
|
|
2640
|
+
)) {
|
|
2641
|
+
if (scopeName === "__error") continue;
|
|
2642
|
+
for (const [packageName, versionValue] of Object.entries(
|
|
2643
|
+
asInspectorRecord(scopeValue),
|
|
2644
|
+
)) {
|
|
2645
|
+
const versionEntries = Array.isArray(versionValue) ? versionValue : [];
|
|
2646
|
+
const existing = mfInspectorRuntimeSharedPackageMap.get(packageName) ?? {
|
|
2647
|
+
registeredVersionCount: 0,
|
|
2648
|
+
loaded: false,
|
|
2649
|
+
};
|
|
2650
|
+
existing.registeredVersionCount = Math.max(
|
|
2651
|
+
existing.registeredVersionCount,
|
|
2652
|
+
versionEntries.length,
|
|
2653
|
+
);
|
|
2654
|
+
existing.loaded =
|
|
2655
|
+
existing.loaded ||
|
|
2656
|
+
versionEntries.some(
|
|
2657
|
+
(entry) => asInspectorRecord(entry).loaded === true,
|
|
2658
|
+
);
|
|
2659
|
+
mfInspectorRuntimeSharedPackageMap.set(packageName, existing);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
const mfInspectorSharedPackagesByApp = new Map<
|
|
2664
|
+
string,
|
|
2665
|
+
Array<{
|
|
2666
|
+
packageName: string;
|
|
2667
|
+
singletonPreferred: boolean;
|
|
2668
|
+
runtimeRegistered: boolean;
|
|
2669
|
+
runtimeLoaded: boolean;
|
|
2670
|
+
}>
|
|
2671
|
+
>();
|
|
2672
|
+
|
|
2673
|
+
for (const [
|
|
2674
|
+
packageName,
|
|
2675
|
+
packageInfo,
|
|
2676
|
+
] of mfInspectorDeclaredSharedPackageMap) {
|
|
2677
|
+
for (const appName of packageInfo.apps) {
|
|
2678
|
+
const appPackages = mfInspectorSharedPackagesByApp.get(appName) ?? [];
|
|
2679
|
+
const runtimePackageInfo =
|
|
2680
|
+
mfInspectorRuntimeSharedPackageMap.get(packageName) ?? null;
|
|
2681
|
+
appPackages.push({
|
|
2682
|
+
packageName,
|
|
2683
|
+
singletonPreferred: packageInfo.singletonPreferred,
|
|
2684
|
+
runtimeRegistered: runtimePackageInfo != null,
|
|
2685
|
+
runtimeLoaded: runtimePackageInfo?.loaded === true,
|
|
2686
|
+
});
|
|
2687
|
+
mfInspectorSharedPackagesByApp.set(appName, appPackages);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
for (const packages of mfInspectorSharedPackagesByApp.values()) {
|
|
2692
|
+
packages.sort((left, right) =>
|
|
2693
|
+
left.packageName.localeCompare(right.packageName),
|
|
2694
|
+
);
|
|
2695
|
+
}
|
|
2401
2696
|
const mfInspectorSummaryCards = [
|
|
2402
2697
|
{
|
|
2403
2698
|
label: "Host page",
|
|
@@ -2442,6 +2737,64 @@ export default function CodeRunnerModal() {
|
|
|
2442
2737
|
},
|
|
2443
2738
|
];
|
|
2444
2739
|
|
|
2740
|
+
const getMfInspectorAppSharedPackages = (appName: string) =>
|
|
2741
|
+
mfInspectorSharedPackagesByApp.get(appName) ?? [];
|
|
2742
|
+
|
|
2743
|
+
const getMfInspectorAppSharedPackageSummary = (appName: string) => {
|
|
2744
|
+
const packages = getMfInspectorAppSharedPackages(appName);
|
|
2745
|
+
const previewItems = packages.slice(0, 2).map((entry) => entry.packageName);
|
|
2746
|
+
const hiddenCount = packages.length - previewItems.length;
|
|
2747
|
+
|
|
2748
|
+
return {
|
|
2749
|
+
count: packages.length,
|
|
2750
|
+
countLabel:
|
|
2751
|
+
packages.length === 1
|
|
2752
|
+
? "1 shared package"
|
|
2753
|
+
: `${packages.length} shared packages`,
|
|
2754
|
+
previewText:
|
|
2755
|
+
packages.length === 0
|
|
2756
|
+
? "none declared"
|
|
2757
|
+
: previewItems.join(", ") +
|
|
2758
|
+
(hiddenCount > 0 ? ` +${hiddenCount}` : ""),
|
|
2759
|
+
};
|
|
2760
|
+
};
|
|
2761
|
+
|
|
2762
|
+
const renderMfInspectorPackageBadges = (appName: string) => {
|
|
2763
|
+
const packages = getMfInspectorAppSharedPackages(appName);
|
|
2764
|
+
|
|
2765
|
+
if (packages.length === 0) {
|
|
2766
|
+
return <span className="text-slate-600">none declared</span>;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
return (
|
|
2770
|
+
<div className="flex flex-wrap gap-1">
|
|
2771
|
+
{packages.map((entry) => {
|
|
2772
|
+
const toneClass = entry.runtimeLoaded
|
|
2773
|
+
? "border-emerald-500/30 bg-emerald-500/10 text-emerald-200"
|
|
2774
|
+
: entry.runtimeRegistered
|
|
2775
|
+
? "border-cyan-500/30 bg-cyan-500/10 text-cyan-200"
|
|
2776
|
+
: "border-slate-700 bg-slate-800/50 text-slate-300";
|
|
2777
|
+
return (
|
|
2778
|
+
<span
|
|
2779
|
+
key={`${appName}-${entry.packageName}`}
|
|
2780
|
+
className={`rounded border px-1.5 py-0.5 text-[10px] font-mono ${toneClass}`}
|
|
2781
|
+
title={
|
|
2782
|
+
entry.runtimeLoaded
|
|
2783
|
+
? "Seen and loaded at runtime"
|
|
2784
|
+
: entry.runtimeRegistered
|
|
2785
|
+
? "Seen in webpack runtime registry"
|
|
2786
|
+
: "Declared in package.json but not yet seen at runtime"
|
|
2787
|
+
}
|
|
2788
|
+
>
|
|
2789
|
+
{entry.packageName}
|
|
2790
|
+
{entry.singletonPreferred ? " · one copy" : ""}
|
|
2791
|
+
</span>
|
|
2792
|
+
);
|
|
2793
|
+
})}
|
|
2794
|
+
</div>
|
|
2795
|
+
);
|
|
2796
|
+
};
|
|
2797
|
+
|
|
2445
2798
|
const renderMfInspectorTruthCell = (
|
|
2446
2799
|
sameInstance: boolean | null,
|
|
2447
2800
|
error: string | null,
|
|
@@ -3239,7 +3592,7 @@ export default function CodeRunnerModal() {
|
|
|
3239
3592
|
</button>
|
|
3240
3593
|
</>
|
|
3241
3594
|
)}
|
|
3242
|
-
{/* React/Next mode
|
|
3595
|
+
{/* React/Next/Webpack mode controls */}
|
|
3243
3596
|
{(clientType === "react" ||
|
|
3244
3597
|
clientType === "nextjs" ||
|
|
3245
3598
|
clientType === "module-federation") && (
|
|
@@ -3252,17 +3605,69 @@ export default function CodeRunnerModal() {
|
|
|
3252
3605
|
{sandboxUrl}
|
|
3253
3606
|
</span>
|
|
3254
3607
|
)}
|
|
3255
|
-
{/* React mode:
|
|
3608
|
+
{/* React mode: Run Vite OR edit/preview toggle */}
|
|
3256
3609
|
{clientType === "react" && (
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3610
|
+
<>
|
|
3611
|
+
{!viteSandboxUrl ? (
|
|
3612
|
+
<button
|
|
3613
|
+
type="button"
|
|
3614
|
+
onClick={() => void startViteServer()}
|
|
3615
|
+
disabled={viteStarting}
|
|
3616
|
+
className="flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium bg-cyan-600/20 hover:bg-cyan-600/40 text-cyan-400 disabled:opacity-50 transition-colors shrink-0"
|
|
3617
|
+
title="Install dependencies and start Vite dev server"
|
|
3618
|
+
>
|
|
3619
|
+
{viteStarting ? (
|
|
3620
|
+
<Loader2 className="w-3 h-3 animate-spin" />
|
|
3621
|
+
) : (
|
|
3622
|
+
<Play className="w-3 h-3" />
|
|
3623
|
+
)}
|
|
3624
|
+
{viteStarting ? "Starting…" : "Run Vite"}
|
|
3625
|
+
</button>
|
|
3626
|
+
) : (
|
|
3627
|
+
<>
|
|
3628
|
+
<div className="flex items-center rounded overflow-hidden border border-slate-700/50 text-[9px] shrink-0">
|
|
3629
|
+
<button
|
|
3630
|
+
type="button"
|
|
3631
|
+
onClick={() => setReactClientTab("edit")}
|
|
3632
|
+
className={`flex items-center gap-0.5 px-1.5 py-0.5 transition-colors ${
|
|
3633
|
+
reactClientTab === "edit"
|
|
3634
|
+
? "bg-slate-700 text-slate-200"
|
|
3635
|
+
: "text-slate-500 hover:text-slate-400"
|
|
3636
|
+
}`}
|
|
3637
|
+
title="Edit code"
|
|
3638
|
+
>
|
|
3639
|
+
<Code2 className="w-2.5 h-2.5" />
|
|
3640
|
+
</button>
|
|
3641
|
+
<button
|
|
3642
|
+
type="button"
|
|
3643
|
+
onClick={() => setReactClientTab("preview")}
|
|
3644
|
+
className={`flex items-center gap-0.5 px-1.5 py-0.5 transition-colors ${
|
|
3645
|
+
reactClientTab === "preview"
|
|
3646
|
+
? "bg-slate-700 text-slate-200"
|
|
3647
|
+
: "text-slate-500 hover:text-slate-400"
|
|
3648
|
+
}`}
|
|
3649
|
+
title="Live preview"
|
|
3650
|
+
>
|
|
3651
|
+
<Eye className="w-2.5 h-2.5" />
|
|
3652
|
+
</button>
|
|
3653
|
+
</div>
|
|
3654
|
+
<button
|
|
3655
|
+
type="button"
|
|
3656
|
+
onClick={() => {
|
|
3657
|
+
if (viteSandboxId)
|
|
3658
|
+
void stopReactLabSandbox(viteSandboxId);
|
|
3659
|
+
setViteSandboxId(null);
|
|
3660
|
+
setViteSandboxUrl(null);
|
|
3661
|
+
setReactClientTab("edit");
|
|
3662
|
+
}}
|
|
3663
|
+
className="p-0.5 rounded text-slate-600 hover:text-red-400 transition-colors shrink-0"
|
|
3664
|
+
title="Stop Vite lab"
|
|
3665
|
+
>
|
|
3666
|
+
<StopCircle className="w-3 h-3" />
|
|
3667
|
+
</button>
|
|
3668
|
+
</>
|
|
3669
|
+
)}
|
|
3670
|
+
</>
|
|
3266
3671
|
)}
|
|
3267
3672
|
{/* Next.js mode: start real server OR edit/preview toggle */}
|
|
3268
3673
|
{clientType === "nextjs" && (
|
|
@@ -3383,129 +3788,12 @@ export default function CodeRunnerModal() {
|
|
|
3383
3788
|
)}
|
|
3384
3789
|
</div>
|
|
3385
3790
|
|
|
3386
|
-
{/* File tabs row (React only — Next.js uses the tree sidebar) */}
|
|
3387
|
-
{clientType === "react" && (
|
|
3388
|
-
<div className="flex items-center gap-0.5 px-2 py-1 bg-slate-800/40 border-b border-slate-700 shrink-0 overflow-x-auto">
|
|
3389
|
-
{Object.keys(reactFiles).map((fname) => (
|
|
3390
|
-
<button
|
|
3391
|
-
key={fname}
|
|
3392
|
-
type="button"
|
|
3393
|
-
onClick={() => {
|
|
3394
|
-
setReactActiveFile(fname);
|
|
3395
|
-
setReactClientTab("edit");
|
|
3396
|
-
}}
|
|
3397
|
-
className={`flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-mono whitespace-nowrap transition-colors ${
|
|
3398
|
-
fname === reactActiveFile && reactClientTab === "edit"
|
|
3399
|
-
? "bg-slate-900 text-slate-200 border border-slate-600"
|
|
3400
|
-
: "text-slate-500 hover:text-slate-300 hover:bg-slate-800/50"
|
|
3401
|
-
}`}
|
|
3402
|
-
>
|
|
3403
|
-
{fname.includes("/") ? fname.split("/").pop() : fname}
|
|
3404
|
-
<span
|
|
3405
|
-
role="button"
|
|
3406
|
-
onClick={(e) => {
|
|
3407
|
-
e.stopPropagation();
|
|
3408
|
-
if (Object.keys(reactFiles).length <= 1) return;
|
|
3409
|
-
const remaining = Object.keys(reactFiles).filter(
|
|
3410
|
-
(f) => f !== fname,
|
|
3411
|
-
);
|
|
3412
|
-
setReactFiles((prev) => {
|
|
3413
|
-
const next = { ...prev };
|
|
3414
|
-
delete next[fname];
|
|
3415
|
-
return next;
|
|
3416
|
-
});
|
|
3417
|
-
if (reactActiveFile === fname)
|
|
3418
|
-
setReactActiveFile(remaining[0] ?? "");
|
|
3419
|
-
}}
|
|
3420
|
-
className="w-3 h-3 flex items-center justify-center text-slate-600 hover:text-red-400 rounded transition-colors"
|
|
3421
|
-
title="Delete file"
|
|
3422
|
-
>
|
|
3423
|
-
<X className="w-2.5 h-2.5" />
|
|
3424
|
-
</span>
|
|
3425
|
-
</button>
|
|
3426
|
-
))}
|
|
3427
|
-
{/* Add new file */}
|
|
3428
|
-
{reactAddingFile ? (
|
|
3429
|
-
<input
|
|
3430
|
-
autoFocus
|
|
3431
|
-
value={reactNewFileName}
|
|
3432
|
-
onChange={(e) => setReactNewFileName(e.target.value)}
|
|
3433
|
-
onBlur={() => {
|
|
3434
|
-
setReactAddingFile(false);
|
|
3435
|
-
setReactNewFileName("");
|
|
3436
|
-
}}
|
|
3437
|
-
onKeyDown={(e) => {
|
|
3438
|
-
if (e.key === "Enter") {
|
|
3439
|
-
e.preventDefault();
|
|
3440
|
-
const name = reactNewFileName.trim();
|
|
3441
|
-
if (name && !reactFiles[name]) {
|
|
3442
|
-
setReactFiles((prev) => ({
|
|
3443
|
-
...prev,
|
|
3444
|
-
[name]: newFileContent(name),
|
|
3445
|
-
}));
|
|
3446
|
-
setReactActiveFile(name);
|
|
3447
|
-
setReactClientTab("edit");
|
|
3448
|
-
}
|
|
3449
|
-
setReactAddingFile(false);
|
|
3450
|
-
setReactNewFileName("");
|
|
3451
|
-
} else if (e.key === "Escape") {
|
|
3452
|
-
setReactAddingFile(false);
|
|
3453
|
-
setReactNewFileName("");
|
|
3454
|
-
}
|
|
3455
|
-
}}
|
|
3456
|
-
placeholder="filename.tsx"
|
|
3457
|
-
className="w-28 bg-slate-900 border border-cyan-600/50 rounded px-1.5 py-0.5 text-[10px] font-mono text-slate-200 placeholder-slate-600 outline-none focus:border-cyan-500"
|
|
3458
|
-
/>
|
|
3459
|
-
) : (
|
|
3460
|
-
<button
|
|
3461
|
-
type="button"
|
|
3462
|
-
onClick={() => setReactAddingFile(true)}
|
|
3463
|
-
className="p-0.5 rounded text-slate-600 hover:text-cyan-400 transition-colors shrink-0"
|
|
3464
|
-
title="New file"
|
|
3465
|
-
>
|
|
3466
|
-
<FilePlus className="w-3 h-3" />
|
|
3467
|
-
</button>
|
|
3468
|
-
)}
|
|
3469
|
-
{/* Edit / Preview tab toggle */}
|
|
3470
|
-
<div className="ml-auto flex items-center rounded overflow-hidden border border-slate-700/50 text-[9px] shrink-0">
|
|
3471
|
-
<button
|
|
3472
|
-
type="button"
|
|
3473
|
-
onClick={() => setReactClientTab("edit")}
|
|
3474
|
-
className={`flex items-center gap-0.5 px-1.5 py-0.5 transition-colors ${
|
|
3475
|
-
reactClientTab === "edit"
|
|
3476
|
-
? "bg-slate-700 text-slate-200"
|
|
3477
|
-
: "text-slate-500 hover:text-slate-400"
|
|
3478
|
-
}`}
|
|
3479
|
-
title="Edit code"
|
|
3480
|
-
>
|
|
3481
|
-
<Code2 className="w-2.5 h-2.5" />
|
|
3482
|
-
</button>
|
|
3483
|
-
<button
|
|
3484
|
-
type="button"
|
|
3485
|
-
onClick={() => {
|
|
3486
|
-
if (!reactPreviewSrc) refreshPreview();
|
|
3487
|
-
else setReactClientTab("preview");
|
|
3488
|
-
}}
|
|
3489
|
-
className={`flex items-center gap-0.5 px-1.5 py-0.5 transition-colors ${
|
|
3490
|
-
reactClientTab === "preview"
|
|
3491
|
-
? "bg-slate-700 text-slate-200"
|
|
3492
|
-
: "text-slate-500 hover:text-slate-400"
|
|
3493
|
-
}`}
|
|
3494
|
-
title="Live preview"
|
|
3495
|
-
>
|
|
3496
|
-
<Eye className="w-2.5 h-2.5" />
|
|
3497
|
-
</button>
|
|
3498
|
-
</div>
|
|
3499
|
-
</div>
|
|
3500
|
-
)}
|
|
3501
|
-
|
|
3502
3791
|
{/* Client body */}
|
|
3503
3792
|
<div
|
|
3504
|
-
className={`flex-1 min-h-0 ${
|
|
3793
|
+
className={`flex-1 min-h-0 ${usesClientExplorer ? "flex flex-row" : "relative"}`}
|
|
3505
3794
|
>
|
|
3506
|
-
{/* ──
|
|
3507
|
-
{
|
|
3508
|
-
clientType === "module-federation") && (
|
|
3795
|
+
{/* ── VS Code-style file tree sidebar ── */}
|
|
3796
|
+
{usesClientExplorer && (
|
|
3509
3797
|
<div className="w-36 shrink-0 flex flex-col border-r border-slate-700 bg-slate-900/60 overflow-y-auto">
|
|
3510
3798
|
{/* Sidebar header */}
|
|
3511
3799
|
<div className="flex items-center justify-between px-2 py-1.5 border-b border-slate-700/60">
|
|
@@ -3546,7 +3834,9 @@ export default function CodeRunnerModal() {
|
|
|
3546
3834
|
placeholder={
|
|
3547
3835
|
clientType === "module-federation"
|
|
3548
3836
|
? "apps/orders/src/App.jsx"
|
|
3549
|
-
: "
|
|
3837
|
+
: clientType === "nextjs"
|
|
3838
|
+
? "app/new.tsx"
|
|
3839
|
+
: "components/NewWidget.tsx"
|
|
3550
3840
|
}
|
|
3551
3841
|
className="w-full bg-slate-800 border border-cyan-600/50 rounded px-1 py-0.5 text-[9px] font-mono text-slate-200 placeholder-slate-600 outline-none focus:border-cyan-500"
|
|
3552
3842
|
/>
|
|
@@ -3558,7 +3848,9 @@ export default function CodeRunnerModal() {
|
|
|
3558
3848
|
title={
|
|
3559
3849
|
clientType === "module-federation"
|
|
3560
3850
|
? "New file (use paths like apps/orders/src/App.jsx)"
|
|
3561
|
-
:
|
|
3851
|
+
: clientType === "nextjs"
|
|
3852
|
+
? "New file (use paths like app/dashboard/page.tsx)"
|
|
3853
|
+
: "New file (use paths like components/Button.tsx)"
|
|
3562
3854
|
}
|
|
3563
3855
|
>
|
|
3564
3856
|
<FilePlus className="w-3 h-3" />
|
|
@@ -3751,7 +4043,7 @@ export default function CodeRunnerModal() {
|
|
|
3751
4043
|
|
|
3752
4044
|
{/* ── Editor / Preview area ── */}
|
|
3753
4045
|
<div
|
|
3754
|
-
className={`${
|
|
4046
|
+
className={`${usesClientExplorer ? "flex-1 min-w-0 relative" : "absolute inset-0"}`}
|
|
3755
4047
|
>
|
|
3756
4048
|
{clientType === "script" ? (
|
|
3757
4049
|
<SyntaxEditor
|
|
@@ -4047,23 +4339,29 @@ export default function CodeRunnerModal() {
|
|
|
4047
4339
|
</div>
|
|
4048
4340
|
)}
|
|
4049
4341
|
{((clientType === "module-federation" && mfError) ||
|
|
4050
|
-
(clientType
|
|
4342
|
+
(clientType === "nextjs" && nxError) ||
|
|
4343
|
+
(clientType === "react" && viteError)) && (
|
|
4051
4344
|
<div className="text-[10px] text-red-400 bg-red-950/40 border-b border-red-800 px-3 py-1.5 shrink-0 font-mono">
|
|
4052
4345
|
{clientType === "module-federation"
|
|
4053
4346
|
? mfError
|
|
4054
|
-
:
|
|
4347
|
+
: clientType === "react"
|
|
4348
|
+
? viteError
|
|
4349
|
+
: nxError}
|
|
4055
4350
|
</div>
|
|
4056
4351
|
)}
|
|
4057
|
-
{(nxStarting || mfStarting) && (
|
|
4352
|
+
{(nxStarting || mfStarting || viteStarting) && (
|
|
4058
4353
|
<div className="flex-1 flex flex-col items-center justify-center gap-3 text-slate-400 text-sm bg-slate-950">
|
|
4059
4354
|
<Loader2 className="w-8 h-8 animate-spin text-cyan-400" />
|
|
4060
4355
|
<p className="text-[12px]">
|
|
4061
4356
|
{clientType === "module-federation"
|
|
4062
4357
|
? "Installing dependencies and starting webpack apps…"
|
|
4063
|
-
:
|
|
4358
|
+
: clientType === "react"
|
|
4359
|
+
? "Installing dependencies and starting Vite…"
|
|
4360
|
+
: "Starting Next.js dev server…"}
|
|
4064
4361
|
</p>
|
|
4065
4362
|
<p className="text-[10px] text-slate-600 max-w-md text-center px-4">
|
|
4066
|
-
{clientType === "module-federation"
|
|
4363
|
+
{clientType === "module-federation" ||
|
|
4364
|
+
clientType === "react"
|
|
4067
4365
|
? "The first run can take a little longer because npm install runs inside the lab sandbox."
|
|
4068
4366
|
: "This takes ~10 seconds on the first run"}
|
|
4069
4367
|
</p>
|
|
@@ -4129,14 +4427,47 @@ export default function CodeRunnerModal() {
|
|
|
4129
4427
|
</p>
|
|
4130
4428
|
</div>
|
|
4131
4429
|
)}
|
|
4132
|
-
{!
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4430
|
+
{!viteStarting &&
|
|
4431
|
+
clientType === "react" &&
|
|
4432
|
+
viteSandboxUrl && (
|
|
4433
|
+
<iframe
|
|
4434
|
+
ref={viteIframeRef}
|
|
4435
|
+
src={viteSandboxUrl}
|
|
4436
|
+
className="flex-1 min-h-0 w-full border-0 bg-white"
|
|
4437
|
+
style={
|
|
4438
|
+
isDraggingResize
|
|
4439
|
+
? { pointerEvents: "none" }
|
|
4440
|
+
: undefined
|
|
4441
|
+
}
|
|
4442
|
+
title="React Lab Preview"
|
|
4443
|
+
/>
|
|
4444
|
+
)}
|
|
4445
|
+
{!viteStarting &&
|
|
4446
|
+
clientType === "react" &&
|
|
4447
|
+
!viteSandboxUrl && (
|
|
4448
|
+
<div className="flex-1 flex flex-col items-center justify-center gap-3 text-slate-400 text-sm bg-slate-950 px-6 text-center">
|
|
4449
|
+
<Server className="w-8 h-8 text-cyan-400/70" />
|
|
4450
|
+
<p className="text-[12px]">
|
|
4451
|
+
Click{" "}
|
|
4452
|
+
<span className="text-cyan-400 font-medium">
|
|
4453
|
+
Run Vite
|
|
4454
|
+
</span>{" "}
|
|
4455
|
+
to install dependencies and start the dev
|
|
4456
|
+
server.
|
|
4457
|
+
</p>
|
|
4458
|
+
<p className="text-[10px] text-slate-600 max-w-md">
|
|
4459
|
+
Edit{" "}
|
|
4460
|
+
<code className="text-slate-400">
|
|
4461
|
+
package.json
|
|
4462
|
+
</code>{" "}
|
|
4463
|
+
to add packages, then use the Console tab to run{" "}
|
|
4464
|
+
<code className="text-slate-400">
|
|
4465
|
+
npm install
|
|
4466
|
+
</code>
|
|
4467
|
+
.
|
|
4468
|
+
</p>
|
|
4469
|
+
</div>
|
|
4470
|
+
)}
|
|
4140
4471
|
</div>
|
|
4141
4472
|
)}
|
|
4142
4473
|
</div>
|
|
@@ -4183,7 +4514,8 @@ export default function CodeRunnerModal() {
|
|
|
4183
4514
|
) : null}
|
|
4184
4515
|
Output
|
|
4185
4516
|
</button>
|
|
4186
|
-
{clientType === "module-federation"
|
|
4517
|
+
{(clientType === "module-federation" ||
|
|
4518
|
+
clientType === "react") && (
|
|
4187
4519
|
<button
|
|
4188
4520
|
type="button"
|
|
4189
4521
|
onClick={() => setSbxBottomTab("console")}
|
|
@@ -4231,9 +4563,10 @@ export default function CodeRunnerModal() {
|
|
|
4231
4563
|
(serverStarting || clientRunning) && (
|
|
4232
4564
|
<Loader2 className="w-3 h-3 text-emerald-400 animate-spin mr-1" />
|
|
4233
4565
|
)}
|
|
4234
|
-
{sbxBottomTab === "console" &&
|
|
4235
|
-
|
|
4236
|
-
|
|
4566
|
+
{sbxBottomTab === "console" &&
|
|
4567
|
+
(mfConsoleRunning || viteConsoleRunning) && (
|
|
4568
|
+
<Loader2 className="w-3 h-3 text-cyan-400 animate-spin mr-1" />
|
|
4569
|
+
)}
|
|
4237
4570
|
{sbxBottomTab === "inspector" && mfInspectorEvents.length > 0 && (
|
|
4238
4571
|
<div className="flex items-center gap-1 mr-1">
|
|
4239
4572
|
<button
|
|
@@ -4282,30 +4615,39 @@ export default function CodeRunnerModal() {
|
|
|
4282
4615
|
</button>
|
|
4283
4616
|
</div>
|
|
4284
4617
|
)}
|
|
4285
|
-
{sbxBottomTab === "console" &&
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
<
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4618
|
+
{sbxBottomTab === "console" &&
|
|
4619
|
+
(mfConsoleOutput.length > 0 ||
|
|
4620
|
+
viteConsoleOutput.length > 0) && (
|
|
4621
|
+
<div className="flex items-center gap-1 mr-1">
|
|
4622
|
+
<button
|
|
4623
|
+
type="button"
|
|
4624
|
+
onClick={() => {
|
|
4625
|
+
const out =
|
|
4626
|
+
clientType === "react"
|
|
4627
|
+
? viteConsoleOutput
|
|
4628
|
+
: mfConsoleOutput;
|
|
4629
|
+
navigator.clipboard.writeText(
|
|
4630
|
+
out.map((line) => line.text).join("\n"),
|
|
4631
|
+
);
|
|
4632
|
+
}}
|
|
4633
|
+
className="p-0.5 rounded text-slate-600 hover:text-slate-300 transition-colors"
|
|
4634
|
+
title="Copy console output"
|
|
4635
|
+
>
|
|
4636
|
+
<Copy className="w-3 h-3" />
|
|
4637
|
+
</button>
|
|
4638
|
+
<button
|
|
4639
|
+
type="button"
|
|
4640
|
+
onClick={() => {
|
|
4641
|
+
if (clientType === "react") setViteConsoleOutput([]);
|
|
4642
|
+
else setMfConsoleOutput([]);
|
|
4643
|
+
}}
|
|
4644
|
+
className="p-0.5 rounded text-slate-600 hover:text-slate-300 transition-colors"
|
|
4645
|
+
title="Clear console output"
|
|
4646
|
+
>
|
|
4647
|
+
<Trash2 className="w-3 h-3" />
|
|
4648
|
+
</button>
|
|
4649
|
+
</div>
|
|
4650
|
+
)}
|
|
4309
4651
|
{sbxBottomTab === "chat" && sbxChatMessages.length > 0 && (
|
|
4310
4652
|
<button
|
|
4311
4653
|
type="button"
|
|
@@ -4338,7 +4680,11 @@ export default function CodeRunnerModal() {
|
|
|
4338
4680
|
<span className="text-slate-600">
|
|
4339
4681
|
{clientType === "module-federation"
|
|
4340
4682
|
? "Run webpack to start the host and remotes"
|
|
4341
|
-
:
|
|
4683
|
+
: clientType === "nextjs"
|
|
4684
|
+
? "Start Next.js to launch the live preview"
|
|
4685
|
+
: clientType === "react"
|
|
4686
|
+
? "Click Run Vite to start the dev server"
|
|
4687
|
+
: "Start the server, then run the client"}
|
|
4342
4688
|
</span>
|
|
4343
4689
|
)}
|
|
4344
4690
|
{sandboxOutput.map((line, i) => (
|
|
@@ -4526,9 +4872,11 @@ export default function CodeRunnerModal() {
|
|
|
4526
4872
|
const remoteY = 156;
|
|
4527
4873
|
const nodeW = 96;
|
|
4528
4874
|
const nodeH = 46;
|
|
4529
|
-
const svgH =
|
|
4875
|
+
const svgH = 250;
|
|
4530
4876
|
const hostBooted = mfInspectorBootMap.has("host");
|
|
4531
4877
|
const hostData = mfInspectorBootMap.get("host");
|
|
4878
|
+
const hostPackageSummary =
|
|
4879
|
+
getMfInspectorAppSharedPackageSummary("host");
|
|
4532
4880
|
|
|
4533
4881
|
const getConnectionStatus = (
|
|
4534
4882
|
remoteName: string,
|
|
@@ -4716,6 +5064,30 @@ export default function CodeRunnerModal() {
|
|
|
4716
5064
|
{"▣ DOM " + hostData.reactDomVersion}
|
|
4717
5065
|
</text>
|
|
4718
5066
|
)}
|
|
5067
|
+
{hostPackageSummary.count > 0 && (
|
|
5068
|
+
<>
|
|
5069
|
+
<text
|
|
5070
|
+
x={W / 2}
|
|
5071
|
+
y={hostY + nodeH / 2 + 16}
|
|
5072
|
+
textAnchor="middle"
|
|
5073
|
+
fill="#cbd5e1"
|
|
5074
|
+
fontSize={8}
|
|
5075
|
+
fontFamily="ui-sans-serif, sans-serif"
|
|
5076
|
+
>
|
|
5077
|
+
{hostPackageSummary.countLabel}
|
|
5078
|
+
</text>
|
|
5079
|
+
<text
|
|
5080
|
+
x={W / 2}
|
|
5081
|
+
y={hostY + nodeH / 2 + 27}
|
|
5082
|
+
textAnchor="middle"
|
|
5083
|
+
fill="#64748b"
|
|
5084
|
+
fontSize={7.5}
|
|
5085
|
+
fontFamily="ui-monospace, monospace"
|
|
5086
|
+
>
|
|
5087
|
+
{hostPackageSummary.previewText}
|
|
5088
|
+
</text>
|
|
5089
|
+
</>
|
|
5090
|
+
)}
|
|
4719
5091
|
|
|
4720
5092
|
{/* REMOTE nodes */}
|
|
4721
5093
|
{displayRemotes.map((remoteName, i) => {
|
|
@@ -4723,6 +5095,10 @@ export default function CodeRunnerModal() {
|
|
|
4723
5095
|
const status = isPlaceholder
|
|
4724
5096
|
? "idle"
|
|
4725
5097
|
: getConnectionStatus(remoteName);
|
|
5098
|
+
const packageSummary =
|
|
5099
|
+
getMfInspectorAppSharedPackageSummary(
|
|
5100
|
+
remoteName,
|
|
5101
|
+
);
|
|
4726
5102
|
const remoteData =
|
|
4727
5103
|
mfInspectorBootMap.get(remoteName);
|
|
4728
5104
|
const keys =
|
|
@@ -4958,6 +5334,30 @@ export default function CodeRunnerModal() {
|
|
|
4958
5334
|
)}
|
|
4959
5335
|
</g>
|
|
4960
5336
|
)}
|
|
5337
|
+
{packageSummary.count > 0 && (
|
|
5338
|
+
<g>
|
|
5339
|
+
<text
|
|
5340
|
+
x={rx}
|
|
5341
|
+
y={remoteY + nodeH / 2 + 26}
|
|
5342
|
+
textAnchor="middle"
|
|
5343
|
+
fill="#cbd5e1"
|
|
5344
|
+
fontSize={8}
|
|
5345
|
+
fontFamily="ui-sans-serif, sans-serif"
|
|
5346
|
+
>
|
|
5347
|
+
{packageSummary.countLabel}
|
|
5348
|
+
</text>
|
|
5349
|
+
<text
|
|
5350
|
+
x={rx}
|
|
5351
|
+
y={remoteY + nodeH / 2 + 37}
|
|
5352
|
+
textAnchor="middle"
|
|
5353
|
+
fill="#64748b"
|
|
5354
|
+
fontSize={7.5}
|
|
5355
|
+
fontFamily="ui-monospace, monospace"
|
|
5356
|
+
>
|
|
5357
|
+
{packageSummary.previewText}
|
|
5358
|
+
</text>
|
|
5359
|
+
</g>
|
|
5360
|
+
)}
|
|
4961
5361
|
</g>
|
|
4962
5362
|
);
|
|
4963
5363
|
})}
|
|
@@ -5036,7 +5436,9 @@ export default function CodeRunnerModal() {
|
|
|
5036
5436
|
<div className="text-[11px] text-slate-500 mb-2 leading-relaxed">
|
|
5037
5437
|
"Same copy" is the healthy result for React and
|
|
5038
5438
|
ReactDOM. "Different copy" often leads to hook,
|
|
5039
|
-
context, or shared-state bugs.
|
|
5439
|
+
context, or shared-state bugs. The package list
|
|
5440
|
+
column reflects the current app package.json
|
|
5441
|
+
files.
|
|
5040
5442
|
</div>
|
|
5041
5443
|
<div className="rounded-lg border border-slate-800 overflow-hidden">
|
|
5042
5444
|
<table className="w-full text-[11px]">
|
|
@@ -5051,6 +5453,9 @@ export default function CodeRunnerModal() {
|
|
|
5051
5453
|
<th className="text-left px-3 py-1.5 text-slate-400 font-medium">
|
|
5052
5454
|
DOM copy
|
|
5053
5455
|
</th>
|
|
5456
|
+
<th className="text-left px-3 py-1.5 text-slate-400 font-medium">
|
|
5457
|
+
Current shared packages
|
|
5458
|
+
</th>
|
|
5054
5459
|
<th className="text-left px-3 py-1.5 text-slate-400 font-medium">
|
|
5055
5460
|
Version seen
|
|
5056
5461
|
</th>
|
|
@@ -5066,6 +5471,10 @@ export default function CodeRunnerModal() {
|
|
|
5066
5471
|
mfInspectorIdentityResultMap.get(
|
|
5067
5472
|
remoteKey,
|
|
5068
5473
|
) ?? null;
|
|
5474
|
+
const remoteAppName =
|
|
5475
|
+
result?.remoteApp ??
|
|
5476
|
+
remoteKey.split("/")[0] ??
|
|
5477
|
+
"unknown";
|
|
5069
5478
|
const loadInfo =
|
|
5070
5479
|
mfInspectorRemoteLoadMap.get(
|
|
5071
5480
|
remoteKey,
|
|
@@ -5092,6 +5501,11 @@ export default function CodeRunnerModal() {
|
|
|
5092
5501
|
loadInfo?.status ?? null,
|
|
5093
5502
|
)}
|
|
5094
5503
|
</td>
|
|
5504
|
+
<td className="px-3 py-2 align-top">
|
|
5505
|
+
{renderMfInspectorPackageBadges(
|
|
5506
|
+
remoteAppName,
|
|
5507
|
+
)}
|
|
5508
|
+
</td>
|
|
5095
5509
|
<td className="px-3 py-2 font-mono text-slate-400 text-[10px]">
|
|
5096
5510
|
{result?.reactVersion ??
|
|
5097
5511
|
(loadInfo?.status ===
|
|
@@ -5257,13 +5671,13 @@ export default function CodeRunnerModal() {
|
|
|
5257
5671
|
<>
|
|
5258
5672
|
<div className="rounded-lg border border-slate-800 bg-slate-900/50 px-3 py-2.5 mb-3">
|
|
5259
5673
|
<div className="text-[12px] font-semibold text-slate-100">
|
|
5260
|
-
|
|
5674
|
+
Shared package picture
|
|
5261
5675
|
</div>
|
|
5262
5676
|
<p className="mt-1 text-[11px] text-slate-400 leading-relaxed">
|
|
5263
|
-
This
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5677
|
+
This tab merges two sources: the packages found in
|
|
5678
|
+
the current app package.json files in the editor,
|
|
5679
|
+
and the packages webpack has actually registered
|
|
5680
|
+
in its live runtime share scope.
|
|
5267
5681
|
</p>
|
|
5268
5682
|
<p className="mt-1 text-[10px] text-slate-500">
|
|
5269
5683
|
Snapshot source:{" "}
|
|
@@ -5276,168 +5690,239 @@ export default function CodeRunnerModal() {
|
|
|
5276
5690
|
)}
|
|
5277
5691
|
</p>
|
|
5278
5692
|
</div>
|
|
5279
|
-
{
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5693
|
+
{mfInspectorShareScopeNames.map((scopeName) => {
|
|
5694
|
+
const scopeValue =
|
|
5695
|
+
scopeName === "default"
|
|
5696
|
+
? asInspectorRecord(
|
|
5697
|
+
mfShareScopeForMatrix.shareScopes.default,
|
|
5698
|
+
)
|
|
5699
|
+
: asInspectorRecord(
|
|
5700
|
+
mfShareScopeForMatrix.shareScopes[
|
|
5701
|
+
scopeName
|
|
5702
|
+
],
|
|
5703
|
+
);
|
|
5704
|
+
const packageNames = Array.from(
|
|
5705
|
+
new Set([
|
|
5706
|
+
...Object.keys(scopeValue),
|
|
5707
|
+
...(scopeName === "default"
|
|
5708
|
+
? Array.from(
|
|
5709
|
+
mfInspectorDeclaredSharedPackageMap.keys(),
|
|
5710
|
+
)
|
|
5711
|
+
: []),
|
|
5712
|
+
]),
|
|
5713
|
+
).sort((left, right) => left.localeCompare(right));
|
|
5714
|
+
|
|
5715
|
+
if (packageNames.length === 0) return null;
|
|
5716
|
+
|
|
5717
|
+
return (
|
|
5718
|
+
<div key={scopeName} className="mb-4">
|
|
5719
|
+
<div className="text-[10px] text-slate-500 uppercase tracking-wider mb-2 font-mono">
|
|
5720
|
+
sharing group: {scopeName}
|
|
5721
|
+
</div>
|
|
5722
|
+
<div className="rounded-lg border border-slate-800 overflow-hidden">
|
|
5723
|
+
<table className="w-full text-[11px]">
|
|
5724
|
+
<thead>
|
|
5725
|
+
<tr className="bg-slate-900/80 border-b border-slate-800">
|
|
5726
|
+
<th className="text-left px-3 py-1.5 text-slate-400 font-medium w-32">
|
|
5727
|
+
Package
|
|
5728
|
+
</th>
|
|
5729
|
+
<th className="text-left px-3 py-1.5 text-slate-400 font-medium w-56">
|
|
5730
|
+
Declared by apps
|
|
5731
|
+
</th>
|
|
5732
|
+
<th className="text-left px-3 py-1.5 text-slate-400 font-medium">
|
|
5733
|
+
Versions webpack can reuse
|
|
5734
|
+
</th>
|
|
5735
|
+
</tr>
|
|
5736
|
+
</thead>
|
|
5737
|
+
<tbody>
|
|
5738
|
+
{packageNames.map((pkgName) => {
|
|
5739
|
+
const versionValue =
|
|
5740
|
+
scopeValue[pkgName];
|
|
5741
|
+
const versionEntries = Array.isArray(
|
|
5742
|
+
versionValue,
|
|
5743
|
+
)
|
|
5744
|
+
? versionValue
|
|
5745
|
+
: [];
|
|
5746
|
+
const declaredInfo =
|
|
5747
|
+
mfInspectorDeclaredSharedPackageMap.get(
|
|
5748
|
+
pkgName,
|
|
5749
|
+
) ?? null;
|
|
5750
|
+
const declaredSingleton =
|
|
5751
|
+
declaredInfo?.singletonPreferred ===
|
|
5752
|
+
true;
|
|
5753
|
+
const declaredVersionSummary =
|
|
5754
|
+
declaredInfo?.requiredVersions
|
|
5755
|
+
.filter(
|
|
5756
|
+
({ version }) => version !== null,
|
|
5757
|
+
)
|
|
5758
|
+
.map(({ app, version }) =>
|
|
5759
|
+
version === false
|
|
5760
|
+
? `${app}: any`
|
|
5761
|
+
: `${app}: ${version}`,
|
|
5762
|
+
) ?? [];
|
|
5763
|
+
const loadedCount =
|
|
5764
|
+
versionEntries.filter(
|
|
5765
|
+
(ve) =>
|
|
5766
|
+
asInspectorRecord(ve).loaded ===
|
|
5767
|
+
true,
|
|
5768
|
+
).length;
|
|
5769
|
+
return (
|
|
5770
|
+
<tr
|
|
5771
|
+
key={pkgName}
|
|
5772
|
+
className="border-b border-slate-800/50 last:border-0"
|
|
5773
|
+
>
|
|
5774
|
+
<td className="px-3 py-2.5 align-top">
|
|
5775
|
+
<div className="font-mono text-slate-100 font-medium">
|
|
5776
|
+
{pkgName}
|
|
5777
|
+
</div>
|
|
5778
|
+
<div className="flex flex-wrap gap-1 mt-1">
|
|
5779
|
+
{declaredSingleton && (
|
|
5780
|
+
<span className="rounded border border-amber-500/30 bg-amber-500/10 px-1 py-0.5 text-[9px] text-amber-300">
|
|
5781
|
+
one copy preferred
|
|
5782
|
+
</span>
|
|
5783
|
+
)}
|
|
5784
|
+
{declaredInfo ? (
|
|
5785
|
+
<span className="rounded border border-cyan-500/30 bg-cyan-500/10 px-1 py-0.5 text-[9px] text-cyan-300">
|
|
5786
|
+
from current package.json
|
|
5787
|
+
</span>
|
|
5788
|
+
) : (
|
|
5789
|
+
<span className="rounded border border-slate-700 bg-slate-800/50 px-1 py-0.5 text-[9px] text-slate-500">
|
|
5790
|
+
runtime only
|
|
5791
|
+
</span>
|
|
5792
|
+
)}
|
|
5793
|
+
<span className="rounded border border-slate-700 bg-slate-800/50 px-1 py-0.5 text-[9px] text-slate-400">
|
|
5794
|
+
{loadedCount}/
|
|
5795
|
+
{versionEntries.length}{" "}
|
|
5796
|
+
already used
|
|
5797
|
+
</span>
|
|
5798
|
+
</div>
|
|
5799
|
+
</td>
|
|
5800
|
+
<td className="px-3 py-2.5 align-top">
|
|
5801
|
+
{declaredInfo ? (
|
|
5802
|
+
<div>
|
|
5803
|
+
<div className="flex flex-wrap gap-1">
|
|
5804
|
+
{declaredInfo.apps.map(
|
|
5805
|
+
(appName) => (
|
|
5806
|
+
<span
|
|
5807
|
+
key={appName}
|
|
5808
|
+
className="rounded border border-slate-700 bg-slate-800/50 px-1.5 py-0.5 text-[9px] text-slate-300 font-mono"
|
|
5809
|
+
>
|
|
5810
|
+
{appName}
|
|
5811
|
+
</span>
|
|
5812
|
+
),
|
|
5344
5813
|
)}
|
|
5345
|
-
<span className="rounded border border-slate-700 bg-slate-800/50 px-1 py-0.5 text-[9px] text-slate-400">
|
|
5346
|
-
{loadedCount}/
|
|
5347
|
-
{versionEntries.length}{" "}
|
|
5348
|
-
already used
|
|
5349
|
-
</span>
|
|
5350
5814
|
</div>
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5815
|
+
{declaredVersionSummary.length >
|
|
5816
|
+
0 && (
|
|
5817
|
+
<div className="mt-1 text-[9px] text-slate-500 leading-relaxed">
|
|
5818
|
+
required versions:{" "}
|
|
5819
|
+
{declaredVersionSummary.join(
|
|
5820
|
+
", ",
|
|
5821
|
+
)}
|
|
5822
|
+
</div>
|
|
5823
|
+
)}
|
|
5824
|
+
</div>
|
|
5825
|
+
) : (
|
|
5826
|
+
<span className="text-slate-600">
|
|
5827
|
+
not declared in current app
|
|
5828
|
+
config
|
|
5829
|
+
</span>
|
|
5830
|
+
)}
|
|
5831
|
+
</td>
|
|
5832
|
+
<td className="px-3 py-2.5">
|
|
5833
|
+
<div className="flex flex-wrap gap-2">
|
|
5834
|
+
{versionEntries.length === 0 ? (
|
|
5835
|
+
declaredInfo ? (
|
|
5836
|
+
<div className="rounded-lg border border-slate-700 bg-slate-800/40 px-2 py-1.5 text-[10px] text-slate-400 leading-relaxed max-w-[22rem]">
|
|
5837
|
+
Declared for sharing, but
|
|
5838
|
+
webpack has not put it in
|
|
5839
|
+
the live runtime registry
|
|
5840
|
+
yet.
|
|
5841
|
+
</div>
|
|
5842
|
+
) : (
|
|
5843
|
+
<span className="text-slate-600">
|
|
5844
|
+
none yet
|
|
5845
|
+
</span>
|
|
5846
|
+
)
|
|
5847
|
+
) : (
|
|
5848
|
+
versionEntries.map(
|
|
5849
|
+
(ve, vi) => {
|
|
5850
|
+
const vr =
|
|
5851
|
+
asInspectorRecord(ve);
|
|
5852
|
+
const version =
|
|
5853
|
+
typeof vr.version ===
|
|
5854
|
+
"string"
|
|
5855
|
+
? vr.version
|
|
5856
|
+
: "?";
|
|
5857
|
+
const from =
|
|
5858
|
+
typeof vr.from ===
|
|
5859
|
+
"string"
|
|
5860
|
+
? vr.from
|
|
5861
|
+
: null;
|
|
5862
|
+
const loaded =
|
|
5863
|
+
vr.loaded === true;
|
|
5864
|
+
const eager =
|
|
5865
|
+
vr.eager === true;
|
|
5866
|
+
return (
|
|
5867
|
+
<div
|
|
5868
|
+
key={vi}
|
|
5869
|
+
className={`rounded-lg border px-2 py-1.5 font-mono text-[10px] ${
|
|
5870
|
+
loaded
|
|
5871
|
+
? "border-emerald-500/30 bg-emerald-500/5"
|
|
5872
|
+
: "border-slate-700 bg-slate-800/40"
|
|
5873
|
+
}`}
|
|
5874
|
+
>
|
|
5875
|
+
<div
|
|
5876
|
+
className={`font-semibold ${loaded ? "text-emerald-200" : "text-slate-400"}`}
|
|
5877
|
+
>
|
|
5878
|
+
{version}
|
|
5879
|
+
</div>
|
|
5880
|
+
{from && (
|
|
5881
|
+
<div className="text-slate-500 mt-0.5 text-[9px]">
|
|
5882
|
+
registered by{" "}
|
|
5883
|
+
{from}
|
|
5884
|
+
</div>
|
|
5885
|
+
)}
|
|
5886
|
+
<div className="flex gap-1 mt-1">
|
|
5887
|
+
<span
|
|
5888
|
+
className={`rounded px-1 py-0.5 text-[8px] ${
|
|
5384
5889
|
loaded
|
|
5385
|
-
? "
|
|
5386
|
-
: "
|
|
5890
|
+
? "bg-emerald-500/20 text-emerald-300"
|
|
5891
|
+
: "bg-slate-700 text-slate-500"
|
|
5387
5892
|
}`}
|
|
5388
5893
|
>
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
{
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
}`}
|
|
5418
|
-
>
|
|
5419
|
-
{eager
|
|
5420
|
-
? "load now"
|
|
5421
|
-
: "load later"}
|
|
5422
|
-
</span>
|
|
5423
|
-
</div>
|
|
5424
|
-
</div>
|
|
5425
|
-
);
|
|
5426
|
-
},
|
|
5427
|
-
)
|
|
5428
|
-
)}
|
|
5429
|
-
</div>
|
|
5430
|
-
</td>
|
|
5431
|
-
</tr>
|
|
5432
|
-
);
|
|
5433
|
-
},
|
|
5434
|
-
)}
|
|
5435
|
-
</tbody>
|
|
5436
|
-
</table>
|
|
5437
|
-
</div>
|
|
5894
|
+
{loaded
|
|
5895
|
+
? "in use"
|
|
5896
|
+
: "registered"}
|
|
5897
|
+
</span>
|
|
5898
|
+
<span
|
|
5899
|
+
className={`rounded px-1 py-0.5 text-[8px] ${
|
|
5900
|
+
eager
|
|
5901
|
+
? "bg-amber-500/20 text-amber-300"
|
|
5902
|
+
: "bg-slate-800 text-slate-600"
|
|
5903
|
+
}`}
|
|
5904
|
+
>
|
|
5905
|
+
{eager
|
|
5906
|
+
? "load now"
|
|
5907
|
+
: "load later"}
|
|
5908
|
+
</span>
|
|
5909
|
+
</div>
|
|
5910
|
+
</div>
|
|
5911
|
+
);
|
|
5912
|
+
},
|
|
5913
|
+
)
|
|
5914
|
+
)}
|
|
5915
|
+
</div>
|
|
5916
|
+
</td>
|
|
5917
|
+
</tr>
|
|
5918
|
+
);
|
|
5919
|
+
})}
|
|
5920
|
+
</tbody>
|
|
5921
|
+
</table>
|
|
5438
5922
|
</div>
|
|
5439
|
-
|
|
5440
|
-
|
|
5923
|
+
</div>
|
|
5924
|
+
);
|
|
5925
|
+
})}
|
|
5441
5926
|
{typeof mfShareScopeForMatrix.shareScopes.__error ===
|
|
5442
5927
|
"string" && (
|
|
5443
5928
|
<div className="rounded border border-red-500/20 bg-red-500/10 px-3 py-2 text-[11px] text-red-200">
|
|
@@ -5533,90 +6018,143 @@ export default function CodeRunnerModal() {
|
|
|
5533
6018
|
{sbxBottomTab === "console" && (
|
|
5534
6019
|
<div className="flex-1 min-h-0 flex flex-col">
|
|
5535
6020
|
<div className="shrink-0 border-b border-slate-800 bg-slate-900/70 px-3 py-2 flex items-center gap-2">
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
{root
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
6021
|
+
{clientType === "module-federation" && (
|
|
6022
|
+
<select
|
|
6023
|
+
value={mfConsoleCwd}
|
|
6024
|
+
onChange={(e) => setMfConsoleCwd(e.target.value)}
|
|
6025
|
+
disabled={!mfSandboxId || mfConsoleRunning}
|
|
6026
|
+
className="bg-slate-950 border border-slate-700 rounded px-2 py-1 text-[11px] font-mono text-slate-200 outline-none disabled:opacity-50"
|
|
6027
|
+
>
|
|
6028
|
+
{moduleFederationCommandRoots.map((root) => (
|
|
6029
|
+
<option key={root} value={root}>
|
|
6030
|
+
{root === "." ? "root" : root}
|
|
6031
|
+
</option>
|
|
6032
|
+
))}
|
|
6033
|
+
</select>
|
|
6034
|
+
)}
|
|
5548
6035
|
<input
|
|
5549
|
-
value={
|
|
5550
|
-
|
|
6036
|
+
value={
|
|
6037
|
+
clientType === "react"
|
|
6038
|
+
? viteConsoleCommand
|
|
6039
|
+
: mfConsoleCommand
|
|
6040
|
+
}
|
|
6041
|
+
onChange={(e) =>
|
|
6042
|
+
clientType === "react"
|
|
6043
|
+
? setViteConsoleCommand(e.target.value)
|
|
6044
|
+
: setMfConsoleCommand(e.target.value)
|
|
6045
|
+
}
|
|
5551
6046
|
onKeyDown={(e) => {
|
|
5552
6047
|
if (e.key === "Enter") {
|
|
5553
6048
|
e.preventDefault();
|
|
5554
|
-
void
|
|
6049
|
+
if (clientType === "react") void runViteCommand();
|
|
6050
|
+
else void runModuleFederationCommand();
|
|
5555
6051
|
}
|
|
5556
6052
|
}}
|
|
5557
|
-
disabled={
|
|
5558
|
-
|
|
6053
|
+
disabled={
|
|
6054
|
+
clientType === "react"
|
|
6055
|
+
? !viteSandboxId || viteConsoleRunning
|
|
6056
|
+
: !mfSandboxId || mfConsoleRunning
|
|
6057
|
+
}
|
|
6058
|
+
placeholder={
|
|
6059
|
+
clientType === "react"
|
|
6060
|
+
? "npm install <package>"
|
|
6061
|
+
: "npm run build"
|
|
6062
|
+
}
|
|
5559
6063
|
className="flex-1 bg-slate-950 border border-slate-700 rounded px-2 py-1 text-[11px] font-mono text-slate-200 placeholder-slate-600 outline-none disabled:opacity-50"
|
|
5560
6064
|
spellCheck={false}
|
|
5561
6065
|
/>
|
|
5562
6066
|
<button
|
|
5563
6067
|
type="button"
|
|
5564
|
-
onClick={() =>
|
|
6068
|
+
onClick={() => {
|
|
6069
|
+
if (clientType === "react") void runViteCommand();
|
|
6070
|
+
else void runModuleFederationCommand();
|
|
6071
|
+
}}
|
|
5565
6072
|
disabled={
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
6073
|
+
clientType === "react"
|
|
6074
|
+
? !viteSandboxId ||
|
|
6075
|
+
viteConsoleRunning ||
|
|
6076
|
+
!viteConsoleCommand.trim()
|
|
6077
|
+
: !mfSandboxId ||
|
|
6078
|
+
mfConsoleRunning ||
|
|
6079
|
+
!mfConsoleCommand.trim()
|
|
5569
6080
|
}
|
|
5570
6081
|
className="flex items-center gap-1 px-2.5 py-1 rounded text-[11px] font-medium bg-cyan-600/20 hover:bg-cyan-600/35 text-cyan-300 disabled:opacity-50 transition-colors shrink-0"
|
|
5571
6082
|
>
|
|
5572
|
-
{
|
|
6083
|
+
{(
|
|
6084
|
+
clientType === "react"
|
|
6085
|
+
? viteConsoleRunning
|
|
6086
|
+
: mfConsoleRunning
|
|
6087
|
+
) ? (
|
|
5573
6088
|
<Loader2 className="w-3 h-3 animate-spin" />
|
|
5574
6089
|
) : (
|
|
5575
6090
|
<Play className="w-3 h-3" />
|
|
5576
6091
|
)}
|
|
5577
6092
|
Run
|
|
5578
6093
|
</button>
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
6094
|
+
{clientType === "module-federation" && (
|
|
6095
|
+
<button
|
|
6096
|
+
type="button"
|
|
6097
|
+
onClick={() =>
|
|
6098
|
+
void refreshModuleFederationGeneratedFiles()
|
|
6099
|
+
}
|
|
6100
|
+
disabled={!mfSandboxId || mfConsoleRunning}
|
|
6101
|
+
className="px-2 py-1 rounded text-[10px] font-medium text-slate-400 hover:text-slate-200 hover:bg-slate-800 disabled:opacity-50 transition-colors shrink-0"
|
|
6102
|
+
>
|
|
6103
|
+
Refresh dist
|
|
6104
|
+
</button>
|
|
6105
|
+
)}
|
|
5587
6106
|
</div>
|
|
5588
6107
|
<div className="shrink-0 px-3 py-1.5 text-[10px] text-slate-500 border-b border-slate-800">
|
|
5589
|
-
|
|
5590
|
-
|
|
6108
|
+
{clientType === "react"
|
|
6109
|
+
? "Run npm commands in the React lab — e.g. npm install lodash. Vite HMR reloads automatically."
|
|
6110
|
+
: "Run npm scripts in the selected webpack app. Generated dist files appear in the explorer as read-only artifacts."}
|
|
5591
6111
|
</div>
|
|
5592
6112
|
<div className="flex-1 overflow-y-auto px-3 py-2 font-mono text-[12px] leading-relaxed">
|
|
5593
|
-
{
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
?
|
|
5597
|
-
:
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
className=
|
|
5607
|
-
|
|
5608
|
-
?
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
6113
|
+
{(() => {
|
|
6114
|
+
const out =
|
|
6115
|
+
clientType === "react"
|
|
6116
|
+
? viteConsoleOutput
|
|
6117
|
+
: mfConsoleOutput;
|
|
6118
|
+
const running =
|
|
6119
|
+
clientType === "react"
|
|
6120
|
+
? viteConsoleRunning
|
|
6121
|
+
: mfConsoleRunning;
|
|
6122
|
+
const sandboxActive =
|
|
6123
|
+
clientType === "react" ? !!viteSandboxId : !!mfSandboxId;
|
|
6124
|
+
if (out.length === 0 && !running) {
|
|
6125
|
+
return (
|
|
6126
|
+
<span className="text-slate-600">
|
|
6127
|
+
{sandboxActive
|
|
6128
|
+
? clientType === "react"
|
|
6129
|
+
? "Run npm install <pkg> to add a package."
|
|
6130
|
+
: "Run npm run build in apps/host, apps/profile, or apps/checkout to inspect dist/."
|
|
6131
|
+
: clientType === "react"
|
|
6132
|
+
? "Start Vite first, then run npm commands here."
|
|
6133
|
+
: "Start webpack first, then run npm commands here."}
|
|
6134
|
+
</span>
|
|
6135
|
+
);
|
|
6136
|
+
}
|
|
6137
|
+
return out.map((line, index) => (
|
|
6138
|
+
<div key={index} className="flex items-start gap-2">
|
|
6139
|
+
<span className="shrink-0 text-[9px] font-bold mt-0.5 w-7 text-right text-cyan-600">
|
|
6140
|
+
cmd
|
|
6141
|
+
</span>
|
|
6142
|
+
<span
|
|
6143
|
+
className={
|
|
6144
|
+
line.kind === "stderr"
|
|
6145
|
+
? "text-red-400 whitespace-pre-wrap"
|
|
6146
|
+
: line.kind === "warn"
|
|
6147
|
+
? "text-amber-400 whitespace-pre-wrap"
|
|
6148
|
+
: line.kind === "info"
|
|
6149
|
+
? "text-slate-500 italic whitespace-pre-wrap"
|
|
6150
|
+
: "text-slate-200 whitespace-pre-wrap"
|
|
6151
|
+
}
|
|
6152
|
+
>
|
|
6153
|
+
{line.text}
|
|
6154
|
+
</span>
|
|
6155
|
+
</div>
|
|
6156
|
+
));
|
|
6157
|
+
})()}
|
|
5620
6158
|
<div ref={mfConsoleEndRef} />
|
|
5621
6159
|
</div>
|
|
5622
6160
|
</div>
|