Files
unreal-engine-mcp-system-pl…/agent-gateway/public/index.html
Bonchellon eba71c4ca8 Unified, portable Unreal Engine MCP system plugin
Merge of the two project copies into one self-contained plugin (the superset:
variable_op + variables.py, full pcg_op runtime/declarative/preset ops, the
CreateWidgetFloatAnimation widget tool, and full Voxel graph authoring).

Made fully project- and machine-agnostic — no hardcoded paths:
- New src/projectPaths.js auto-detects the host .uproject (walk-up), project
  name, Editor build target, log file, and engine install (EngineAssociation
  via launcher manifest/registry, else installed-engine scan). All overridable
  via UE_* env vars.
- Rewired buildOrchestrator/insights/launcher/insightsExporter/server.js and the
  Python workers (cpp_scaffold, live_coding, apply_graph, console) off the old
  C:/Github/ihy, IHY*, E:/UE_Versions and UE_5.7 literals onto the resolver.
- Voxel made optional: Build.cs auto-detects the Voxel plugin (env
  UE_MCP_WITH_VOXEL override) and the C++ compiles to stubs under WITH_MCP_VOXEL,
  so the module builds in projects without Voxel; .uplugin marks Voxel optional.
- De-branded the agent-gateway and docs; scrubbed a leaked API key; excluded
  node_modules/Binaries/Intermediate/__pycache__/secrets from the repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 01:07:24 +03:00

195 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en" data-theme="eclipse">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Unreal Copilot</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="app">
<!-- ============ SIDEBAR ============ -->
<aside class="sidebar">
<div class="sb-head">
<div class="brand">
<div class="brand-mark" id="brandMark"></div>
<div>
<div class="brand-name">Unreal Copilot</div>
<div class="brand-sub">ue-blueprint MCP</div>
</div>
</div>
<button class="icon-btn" id="historyBtn" title="История"></button>
</div>
<button class="new-chat" id="newChat">
<span id="newChatIcon"></span>
<span>Новый чат</span>
<span class="kbd">Ctrl N</span>
</button>
<div class="sb-search">
<span id="searchIcon"></span>
<input id="search" placeholder="Поиск по чатам…" />
</div>
<div class="sb-scroll" id="sbScroll"></div>
<div class="sb-foot">
<div class="mcp-card">
<div class="mcp-head" id="mcpHead">
<span id="mcpIcon"></span>
<span class="mcp-title">MCP Servers</span>
<span class="mcp-count" id="mcpCount"></span>
</div>
<div class="mcp-list" id="mcpList"></div>
</div>
<div class="user-row" id="userRow">
<div class="avatar" id="userAvatar">EX</div>
<div class="user-copy">
<div class="user-name">Exbyte Studios</div>
<div class="user-plan">Studio · Pro</div>
</div>
<span class="user-chev" id="userChev"></span>
</div>
<div class="user-pop pop" id="userPop" style="display:none">
<div class="pop-label">Workspace</div>
<div class="pop-item" id="settingsBtn"><span class="pop-ico" id="settingsIcon"></span><div class="pi-main"><div class="pi-name">Settings</div><div class="pi-desc">Integrations and import paths</div></div></div>
</div>
</div>
</aside>
<!-- ============ MAIN ============ -->
<main class="main">
<header class="topbar">
<div class="tb-titles">
<div class="tb-title" id="convTitle">Новый разговор</div>
<div class="tb-sub" id="convSub">ue-blueprint MCP</div>
</div>
<div class="tb-spacer"></div>
<div class="status-pill" id="statusPill"><span class="dot off" id="statusDot"></span><span id="statusText">connecting…</span></div>
<div style="position:relative">
<button class="model-btn" id="modelBtn">
<span class="m-glyph" id="modelGlyph"></span>
<span id="modelName">agent</span>
<span id="modelChev" style="color:var(--text-dim)"></span>
</button>
<div class="pop" id="modelPop" style="display:none; right:0; top:40px;"></div>
</div>
</header>
<div class="thread-wrap" id="threadWrap">
<div class="thread" id="thread"></div>
</div>
<div class="composer-wrap">
<div class="composer-inner">
<div class="attach-row" id="attachRow" style="display:none"></div>
<div class="composer" id="composer">
<textarea class="composer-field" id="input" rows="1" placeholder="Попроси Forge отредактировать Blueprint или @упомяни ассет…"></textarea>
<div class="composer-bar">
<button class="cb-btn" id="mentionBtn"><span id="mentionIcon"></span> Mention</button>
<button class="cb-btn" id="attachBtn"><span id="attachIcon"></span> Attach</button>
<button class="cb-btn" id="toolsBtn"><span id="toolsIcon"></span> Tools <span id="toolsCount" style="color:var(--text-dim)"></span></button>
<button class="cb-btn" id="soundBtn"><span id="soundIcon"></span> Sound</button>
<button class="cb-btn" id="textFixBtn"><span id="textFixIcon"></span> Text Fix</button>
<button class="cb-btn" id="translateBtn"><span id="translateIcon"></span> Translate</button>
<span class="cb-spacer"></span>
<button class="send-btn" id="send" title="Отправить"><span id="sendIcon"></span></button>
</div>
</div>
<div class="mention-pop pop" id="mentionPop" style="display:none"></div>
<div class="composer-hint">Forge правит Blueprints и C++ через <strong style="color:var(--text-muted);font-weight:600">ue-blueprint</strong> MCP · <span class="kbd"></span> отправить · <span class="kbd">⇧⏎</span> перенос</div>
</div>
</div>
</main>
</div>
<input type="file" id="fileInput" accept="image/*" multiple style="display:none" />
<div class="modal-backdrop" id="settingsModal" style="display:none">
<div class="modal settings-modal">
<div class="modal-head">
<div><div class="modal-title">Settings</div><div class="modal-sub">Forge workspace configuration</div></div>
<button class="modal-close" data-close-modal="settingsModal">x</button>
</div>
<div class="settings-body">
<div class="settings-nav">
<button class="settings-nav-item active"><span id="integrationsIcon"></span> Integrations</button>
</div>
<div class="settings-content">
<div class="settings-kicker">Integrations</div>
<h2>Connected services</h2>
<p class="settings-intro">Secrets are stored locally beside the Forge gateway and never returned to the browser.</p>
<section class="integration-card">
<div class="integration-head">
<div class="integration-logo eleven">XI</div>
<div class="integration-meta"><h3>ElevenLabs</h3><p>Generate sound-effect variations and import selected WAV assets into Unreal.</p></div>
<span class="integration-state" id="elevenState">Not configured</span>
</div>
<label class="field"><span>API key</span><input id="elevenApiKey" type="password" autocomplete="off" placeholder="xi-api-key" /><small id="elevenKeyHint">Enter a key to connect ElevenLabs.</small></label>
<div class="field-grid">
<label class="field"><span>Unreal import folder</span><input id="elevenImportPath" value="/Game/Audio/Generated" /></label>
<label class="field"><span>Variations per prompt</span><select id="elevenVariations"><option>1</option><option>2</option><option selected>3</option><option>4</option></select></label>
</div>
<label class="field"><span>Sound Effects model</span><select id="elevenSoundModel"><option value="eleven_text_to_sound_v2">Sound Effects v2 · eleven_text_to_sound_v2</option></select><small>Current ElevenLabs flagship sound-effects model. Looping is supported by v2.</small></label>
<div class="field-grid">
<label class="field"><span>Default duration</span><select id="elevenDefaultDuration"><option value="">Auto</option><option value="1">1s</option><option value="2">2s</option><option value="3">3s</option><option value="5">5s</option><option value="10">10s</option><option value="15">15s</option><option value="30">30s</option></select></label>
<label class="field"><span>Default prompt influence</span><select id="elevenDefaultInfluence"><option value="0.2">20% · more variety</option><option value="0.3">30% · balanced</option><option value="0.5">50% · closer prompt</option><option value="0.7">70% · strict prompt</option><option value="0.9">90% · very strict</option></select></label>
</div>
</section>
<section class="integration-card">
<div class="integration-head">
<div class="integration-logo codex">&lt;/&gt;</div>
<div class="integration-meta"><h3>Codex</h3><p>Authorize Codex locally through OAuth. Forge only reads the connection state.</p></div>
<span class="integration-state" id="codexState">Checking...</span>
</div>
<div class="integration-actions"><button class="secondary-btn" id="codexLogin">Authorize with OAuth</button><span class="integration-note">OAuth credentials stay in <code>~/.codex/auth.json</code>.</span></div>
</section>
<section class="integration-card">
<div class="integration-head">
<div class="integration-logo textfix">Aa</div>
<div class="integration-meta"><h3>Text Fix Improver</h3><p>Improve composer drafts with your own editing instruction.</p></div>
<span class="integration-state connected">Enabled</span>
</div>
<label class="field"><span>Improvement prompt</span><textarea id="textFixPrompt" rows="4" maxlength="2000"></textarea><small>Forge returns only the improved text and replaces the current draft.</small></label>
<label class="field"><span>Translate languages</span><input id="translateLanguages" placeholder="English, Russian, German" /><small>Comma-separated languages shown in the Translate dialog.</small></label>
</section>
</div>
</div>
<div class="modal-foot"><button class="secondary-btn" data-close-modal="settingsModal">Cancel</button><button class="primary-btn" id="saveSettings">Save changes</button></div>
</div>
</div>
<div class="modal-backdrop" id="soundModal" style="display:none">
<div class="modal sound-modal">
<div class="modal-head">
<div><div class="modal-title">Generate sound effects</div><div class="modal-sub">ElevenLabs creates multiple variations for review in this chat.</div></div>
<button class="modal-close" data-close-modal="soundModal">x</button>
</div>
<div class="sound-modal-body">
<label class="field"><span>Sound prompt</span><textarea id="soundPrompt" rows="5" maxlength="450" placeholder="Heavy metal door slamming shut in a damp industrial corridor, horror game one-shot"></textarea><small><span id="soundPromptCount">0</span>/450 characters</small></label>
<div class="sound-options">
<label class="sound-option"><span class="sound-option-label">Loop</span><button class="toggle-btn" id="soundLoop" type="button" aria-pressed="false"><span class="toggle-track"><span class="toggle-knob"></span></span><strong>Off</strong></button></label>
<label class="sound-option"><span class="sound-option-label">Duration</span><select id="soundDuration"><option value="">Auto</option><option value="1">1s</option><option value="2">2s</option><option value="3">3s</option><option value="5">5s</option><option value="10">10s</option><option value="15">15s</option><option value="30">30s</option></select></label>
<label class="sound-option influence"><span class="sound-option-label">Prompt influence</span><div class="range-row"><input id="soundInfluence" type="range" min="0" max="100" value="30" step="5" /><strong id="soundInfluenceValue">30%</strong></div></label>
<label class="sound-option compact"><span class="sound-option-label">Variations</span><select id="soundVariations"><option>1</option><option>2</option><option selected>3</option><option>4</option></select></label>
</div>
</div>
<div class="modal-foot"><button class="secondary-btn" data-close-modal="soundModal">Cancel</button><button class="primary-btn" id="generateSound">Generate variations</button></div>
</div>
</div>
<div class="modal-backdrop" id="translateModal" style="display:none">
<div class="modal translate-modal">
<div class="modal-head">
<div><div class="modal-title">Translate draft</div><div class="modal-sub">Choose the language for the current composer text.</div></div>
<button class="modal-close" data-close-modal="translateModal">x</button>
</div>
<div class="sound-modal-body">
<label class="field"><span>Target language</span><select id="translateLanguage"></select></label>
</div>
<div class="modal-foot"><button class="secondary-btn" data-close-modal="translateModal">Cancel</button><button class="primary-btn" id="translateDraft">Translate</button></div>
</div>
</div>
<div class="toast" id="toast"></div>
<script src="app.js"></script>
</body>
</html>