Unreal build configurations (Development / Shipping / …)

Build Solution now opens a configuration picker (Development Editor,
Development, DebugGame, Shipping) and passes /p:Configuration=<cfg>
/p:Platform=Win64 to MSBuild, which drives UnrealBuildTool for the chosen
config. Streams into the same minimizable build overlay.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bonchellon
2026-07-07 16:40:01 +03:00
parent de6d55ad84
commit 168b6afab3
4 changed files with 72 additions and 11 deletions

View File

@ -302,6 +302,7 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
const [buildOk, setBuildOk] = useState<boolean | null>(null);
const [buildOpen, setBuildOpen] = useState(false);
const [buildMin, setBuildMin] = useState(false); // collapsed to a small floating chip
const [buildPick, setBuildPick] = useState(false); // configuration picker before building
const [ctx, setCtx] = useState<null | { x: number; y: number; items: CtxItem[] }>(null); // right-click menu
const [logs, setLogs] = useState<LogEntry[]>([]); // live p4 command log (P4V-style)
const [logOpen, setLogOpen] = useState(false);
@ -533,11 +534,17 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
catch (e) { flash(String(e), true); }
}
// build the solution found in the working folder (MSBuild) with live output
async function buildSln() {
// open the configuration picker (Development / Shipping / …) before building
function startBuild() {
if (!slnPath) { flash(t("No .sln found in the working folder. Choose the project folder first."), true); return; }
setBuildPick(true);
}
// build the solution (MSBuild) with the chosen UE configuration, live output
async function buildSln(config: string) {
setBuildPick(false);
if (!slnPath) return;
setBuildLog([]); setBuildOk(null); setBuilding(true); setBuildOpen(true); setBuildMin(false);
try { await p4.buildSln(slnPath); }
try { await p4.buildSln(slnPath, config); }
catch { /* the overlay already shows the failure line */ }
}
@ -716,7 +723,7 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
Connection: [{ label: t("Refresh"), kb: "F5", act: () => refresh() }, { label: t("Choose working folder…"), act: () => browseTo("") }],
Actions: [{ label: t("Rescan changes"), kb: "Ctrl+R", act: rescan }, { label: t("Get Latest"), kb: "Ctrl+G", act: getLatest }, { label: t("Commit (local)"), act: doCommit }, { label: t("Submit to server"), kb: "Ctrl+S", act: pushAll }, { label: t("Revert All…"), act: revertAll }, { label: t("Refresh"), kb: "F5", act: () => refresh() }],
Window: [{ label: (logOpen ? "✓ " : "") + t("Log"), kb: "Ctrl+L", act: () => setLogOpen((o) => !o) }],
Tools: [{ label: slnPath ? t("Build Solution (.sln)") : t("Build Solution — no .sln"), kb: "Ctrl+B", act: buildSln }, { label: t("Settings…"), act: () => setModal({ kind: "settings" }) }],
Tools: [{ label: slnPath ? t("Build Solution (.sln)") : t("Build Solution — no .sln"), kb: "Ctrl+B", act: startBuild }, { label: t("Settings…"), act: () => setModal({ kind: "settings" }) }],
Help: [{ label: t("About"), act: () => setModal({ kind: "about" }) }],
};
@ -759,7 +766,7 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
<div className="tzone" onClick={() => browseTo("")} title={t("Choose the project working folder · right-click for more")}
onContextMenu={(e) => openCtx(e, [
...(uproject ? [{ label: t("Launch Unreal Engine"), icon: I.ue, act: launchUE }] : []),
...(slnPath ? [{ label: t("Build Solution (.sln)"), icon: I.hammer, act: buildSln }] : []),
...(slnPath ? [{ label: t("Build Solution (.sln)"), icon: I.hammer, act: startBuild }] : []),
{ label: t("Open in Explorer"), act: () => reveal(activePath || String(info?.clientRoot || "")) },
{ label: t("Choose another folder…"), act: () => browseTo("") },
{ label: t("Copy path"), act: () => copyText(activePath || "//…", t("Path")) },
@ -920,6 +927,7 @@ function Workbench({ info, session, light, toggleTheme, lang, setLang, zoom, set
{ask && <ConfirmModal {...ask} onClose={(v) => { ask.resolve(v); setAsk(null); }} />}
{prompt && <TextPrompt title={prompt.title} label={prompt.label} value={prompt.value} onSave={prompt.onSave} onClose={() => setPrompt(null)} />}
{showXfer && transfer && <TransferDialog transfer={transfer} />}
{buildPick && <BuildPicker onPick={buildSln} onClose={() => setBuildPick(false)} />}
{buildOpen && !buildMin && <BuildOverlay lines={buildLog} building={building} ok={buildOk} sln={slnPath} onMinimize={() => setBuildMin(true)} onClose={() => setBuildOpen(false)} />}
{buildOpen && buildMin && <BuildChip building={building} ok={buildOk} onExpand={() => setBuildMin(false)} onClose={() => setBuildOpen(false)} />}
{ctx && <ContextMenu x={ctx.x} y={ctx.y} items={ctx.items} onClose={() => setCtx(null)} />}
@ -1151,6 +1159,39 @@ function BuildOverlay({ lines, building, ok, sln, onMinimize, onClose }: { lines
</div>
);
}
/* build configuration picker (Unreal: Development / Shipping / …) */
function BuildPicker({ onPick, onClose }: { onPick: (config: string) => void; onClose: () => void }) {
useEffect(() => {
const onKey = (e: KeyboardEvent) => e.key === "Escape" && onClose();
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [onClose]);
const opts: { config: string; name: string; desc: string }[] = [
{ config: "Development Editor", name: "Development Editor", desc: t("Editor build for working in-engine (default)") },
{ config: "Development", name: "Development", desc: t("Standalone game, dev config") },
{ config: "DebugGame", name: "DebugGame", desc: t("Game with debuggable game code") },
{ config: "Shipping", name: "Shipping", desc: t("Optimized release build") },
];
return (
<div className="modal-back" onClick={onClose}>
<div className="picker" onClick={(e) => e.stopPropagation()}>
<div className="picker-head">{I.hammer}<h3>{t("Build configuration")}</h3>
<button className="x" onClick={onClose}><svg viewBox="0 0 24 24" width="14" height="14" fill="none"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" /></svg></button>
</div>
<div className="picker-list">
{opts.map((o) => (
<div key={o.config} className="picker-item" onClick={() => onPick(o.config)}>
<span className="pi-ic">{I.hammer}</span>
<span className="pi-body"><span className="pi-name">{o.name} · Win64</span><span className="pi-sub">{o.desc}</span></span>
</div>
))}
</div>
<div className="picker-foot">{t("Builds via MSBuild → UnrealBuildTool")}</div>
</div>
</div>
);
}
/* minimized build indicator — small floating chip, click to expand */
function BuildChip({ building, ok, onExpand, onClose }: { building: boolean; ok: boolean | null; onExpand: () => void; onClose: () => void }) {
return (