phantom-grid / ui /web /index.html
unity4ar's picture
Revert to use_cache=False (eager attn also broken); hide all audio UI
df99cf8 verified
Raw
History Blame Contribute Delete
18.3 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Phantom Grid</title>
<link rel="stylesheet" href="/static/app.css?v=19" />
</head>
<body>
<main class="game-board" id="gameScene" aria-live="polite">
<header class="top-banner">
<section class="bureau-crest" aria-label="Lantern Watch Bureau">
<img src="/static/assets/reference/crest_frame.png" alt="" />
</section>
<section class="title-panel" aria-label="Case title">
<h1 id="gameTitle" contenteditable="true" spellcheck="false">Phantom Grid</h1>
<p id="gameSubtitle" contenteditable="true" spellcheck="false">Catch John Doe before he vanishes again!</p>
</section>
<section class="turn-panel" aria-label="Turn counter">
<span>Turn</span>
<strong id="caseClock">-</strong>
<small id="turnPhase">Evening</small>
</section>
<button id="helpButton" class="gear-button help-button" type="button" title="How to play" aria-label="Open how-to-play tutorial">
<span aria-hidden="true">?</span>
</button>
<button id="settingsButton" class="gear-button" type="button" title="Settings" aria-label="Open settings">
<span aria-hidden="true">&#9881;</span>
</button>
</header>
<section class="table-grid">
<aside class="left-rail">
<section class="wanted-card" aria-label="Wanted poster">
<h2>Wanted</h2>
<img id="suspectImage" class="suspect-image" src="/static/assets/reference/suspect_portrait_placeholder.png" alt="Current suspect portrait" />
<dl>
<dt>Alias</dt>
<dd id="wantedAlias">John Doe</dd>
<dt>Description</dt>
<dd id="wantedDescription">Male, approx. 35-45. Gray raincoat. Carries red folder.</dd>
<dt>Last Seen</dt>
<dd id="wantedLastSeen">Awaiting confirmed location</dd>
</dl>
<strong>&pound;5,000 Reward</strong>
<small>Dead or Alive</small>
</section>
<section class="active-units" aria-label="Active unit counts">
<h2>Active Units</h2>
<div class="unit-row" id="unitIcons"></div>
<strong id="activeUnitsText">12 / 12 left</strong>
</section>
<button id="advanceButton" class="advance-button" type="button">Advance Turn</button>
<button id="newCaseButton" class="new-case-button" type="button">New Case</button>
<div class="case-control-row">
<button id="stopGameButton" type="button">Stop Game</button>
<button id="restartGameButton" type="button">Restart</button>
</div>
</aside>
<section class="center-stage">
<section class="tactic-strip" aria-label="Drag tactics onto the map">
<h2>Drag Tactics Onto The Map</h2>
<div id="tacticTray" class="tactic-tray"></div>
</section>
<section class="map-shell" aria-label="Investigation map">
<nav class="layer-tabs" id="layerTabs" aria-label="Map layers"></nav>
<div class="map-wrap" id="mapWrap">
<div class="map-canvas" id="mapCanvas">
<img id="mapImage" alt="London junction map" draggable="false" />
<div id="selectionLayer" class="overlay-layer"></div>
<div id="witnessLayer" class="overlay-layer"></div>
<div id="tacticLayer" class="overlay-layer"></div>
</div>
<div class="map-controls" aria-label="Map zoom and pan controls">
<button id="zoomOutButton" type="button" aria-label="Zoom out">-</button>
<output id="zoomValue">145%</output>
<button id="zoomInButton" type="button" aria-label="Zoom in">+</button>
<button id="zoomResetButton" type="button">Reset</button>
<span class="map-control-divider" aria-hidden="true"></span>
<button id="toggleWitnessesButton" class="map-visibility-toggle active" type="button" aria-pressed="true">Witnesses</button>
<button id="toggleTacticsButton" class="map-visibility-toggle active" type="button" aria-pressed="true">Tactics</button>
<button id="toggleFocusButton" class="map-visibility-toggle active" type="button" aria-pressed="true">Focus</button>
<button id="witnessModeButton" type="button">Witness Mode</button>
</div>
<div id="mapMessage" class="map-message">Drop tactics on junctions. Drag the map to navigate.</div>
</div>
<footer class="legend-strip" id="legendStrip"></footer>
</section>
</section>
<aside class="right-rail">
<section class="lookout-board">
<h2>Commissioner's Notepad</h2>
<article class="paper-note">
<h3>Case Notes</h3>
<textarea id="notesText" spellcheck="true" placeholder="Write deductions, routes, descriptions, and questions here..."></textarea>
<p id="notesStatus">Saved with this case.</p>
</article>
</section>
<section class="statements-panel">
<h2>Previous Witness Statements</h2>
<div id="statementList" class="statement-list"></div>
</section>
</aside>
</section>
<div class="event-ticker" id="eventTicker">Opening the board...</div>
</main>
<section id="setupOverlay" class="setup-overlay" aria-live="polite">
<div class="setup-card">
<small>Phantom Grid Local AI</small>
<h2 id="setupTitle">Preparing Your Investigation Desk</h2>
<p id="setupMessage">Checking the bundled llama.cpp runtime and MiniCPM-o model...</p>
<section id="setupPicker" class="setup-picker" hidden>
<p class="setup-picker-intro">Pick the model and hardware Phantom Grid should run on. You can change these later from Settings.</p>
<p id="setupStorageHint" class="setup-storage-hint"></p>
<div class="setup-picker-grid">
<label>
Model variant
<select id="pickerQuantization"></select>
<small id="pickerQuantHint">Smaller variants fit lower-VRAM cards; larger ones give cleaner answers.</small>
</label>
<label>
Run on
<select id="pickerDevice"></select>
<small id="pickerDeviceHint">Detected from your system. Pick CPU to skip GPU acceleration.</small>
</label>
<label>
GPU offload
<select id="pickerGpuLayers"></select>
<small>How many transformer layers to load onto the GPU. Auto is safe.</small>
</label>
<label>
Context length
<select id="pickerContext"></select>
<small>Larger context handles longer cases but uses more VRAM.</small>
</label>
</div>
</section>
<progress id="setupProgress" max="100" value="0"></progress>
<span id="setupProgressText">Checking setup</span>
<button id="setupStartButton" class="setup-start-button" type="button" disabled>Preparing Local AI...</button>
<button id="setupSettingsButton" class="setup-settings-button" type="button">Advanced Settings</button>
</div>
</section>
<section id="detailPopup" class="detail-popup" hidden aria-live="polite"></section>
<dialog id="tutorialDialog" class="tutorial-dialog">
<section class="tutorial-shell" tabindex="-1">
<header class="tutorial-head">
<div>
<small>Lantern Watch Bureau / Field Manual</small>
<h2 id="tutorialTitle">How To Play</h2>
</div>
<button id="tutorialSkip" type="button" class="tutorial-skip">Skip tutorial</button>
</header>
<div class="tutorial-body">
<figure class="tutorial-figure">
<img id="tutorialImage" alt="" draggable="false" />
</figure>
<div class="tutorial-copy">
<span id="tutorialTag" class="tutorial-tag"></span>
<h3 id="tutorialHeading"></h3>
<ul id="tutorialText" class="tutorial-text"></ul>
</div>
</div>
<footer class="tutorial-foot">
<div id="tutorialDots" class="tutorial-dots" aria-hidden="true"></div>
<div class="tutorial-nav">
<span id="tutorialCounter" class="tutorial-counter"></span>
<button id="tutorialBack" type="button">Back</button>
<button id="tutorialNext" type="button" class="tutorial-primary">Next</button>
</div>
</footer>
</section>
</dialog>
<dialog id="caseIntroDialog" class="case-intro-dialog">
<section class="case-intro-shell" tabindex="-1">
<header class="case-intro-heading">
<div>
<small>Lantern Watch Bureau / Priority Dossier</small>
<h2 id="caseIntroTitle">A New Case</h2>
<p id="caseIntroKicker"></p>
</div>
<span class="case-stamp">Case Open</span>
</header>
<div class="case-intro-cards">
<article class="intro-card crime-card">
<span class="intro-card-number">01</span>
<small>The Crime</small>
<h3 id="caseIntroCrime"></h3>
<p id="caseIntroNarrative"></p>
<dl><dt>Stolen</dt><dd id="caseIntroStolen"></dd><dt>Victim</dt><dd id="caseIntroVictim"></dd></dl>
</article>
<article class="intro-card suspect-card">
<span class="intro-card-number">02</span>
<small>The Thief</small>
<img id="caseIntroImage" src="/static/assets/reference/suspect_portrait_placeholder.png" alt="Current suspect portrait" />
<h3 id="caseIntroAlias"></h3>
<p id="caseIntroDescription"></p>
</article>
<article class="intro-card sightings-card">
<span class="intro-card-number">03</span>
<small>The Trail</small>
<h3>Last Seen</h3>
<ol id="caseIntroSightings"></ol>
</article>
</div>
<footer>
<p>Study the trail. The thief is already moving.</p>
<button id="beginInvestigationButton" type="button">Begin Investigation</button>
</footer>
</section>
</dialog>
<dialog id="settingsDialog" class="settings-dialog">
<form method="dialog" class="settings-panel">
<header>
<h2>Settings</h2>
<button id="settingsCloseButton" type="button" aria-label="Close settings">x</button>
</header>
<section class="settings-grid">
<label class="settings-wide">
Text AI Backend
<select id="providerSetting">
<option value="zerogpu_transformers">ZeroGPU transformers (this Space)</option>
<option value="llama_cpp_server">Local GGUF via llama.cpp</option>
<option value="external_llama_cpp_server">User-started llama.cpp server</option>
</select>
</label>
<div id="customModelSettings" class="settings-subgrid settings-wide" hidden>
<label class="settings-wide">
GGUF Model Path
<input id="modelPathSetting" type="text" autocomplete="off" placeholder="C:\\Models\\model.gguf" />
</label>
<label class="settings-wide">
llama-server Executable
<input id="serverBinSetting" type="text" autocomplete="off" />
</label>
</div>
<div id="llamaConnectionSettings" class="settings-subgrid settings-wide" hidden>
<label>
llama.cpp API URL
<input id="baseUrlSetting" type="text" autocomplete="off" />
</label>
<label>
Model ID
<input id="llmModelSetting" type="text" autocomplete="off" placeholder="Model ID from /v1/models" />
</label>
<p id="externalServerHint" class="settings-hint settings-wide" hidden>
Start and configure llama-server yourself. Phantom Grid only sends OpenAI-compatible requests to this URL.
</p>
</div>
<label>
Sound
<select id="soundSetting">
<option value="on">On</option>
<option value="off">Off</option>
</select>
</label>
<label>
Difficulty
<select id="difficultySetting">
<option value="easy">Easy</option>
<option value="normal">Normal</option>
<option value="hard">Hard</option>
</select>
</label>
<label>
Comni Gateway URL
<input id="gatewayUrlSetting" type="text" autocomplete="off" />
</label>
<label>
Comni Launcher Path
<input id="launcherPathSetting" type="text" autocomplete="off" />
</label>
<label>
Comni Checkout Directory
<input id="comniCheckoutSetting" type="text" autocomplete="off" />
</label>
<label>
llama.cpp-omni Root
<input id="omniRootSetting" type="text" autocomplete="off" />
</label>
<label>
MiniCPM-o Model Directory
<input id="modelDirSetting" type="text" autocomplete="off" />
</label>
<label>
Quantization
<select id="quantizationSetting"></select>
</label>
<label>
Context Length
<select id="contextLengthSetting">
<option value="4096">4,096</option>
<option value="8192">8,192</option>
<option value="16384">16,384</option>
<option value="24576">24,576</option>
<option value="32768">32,768</option>
</select>
</label>
<label>
GPU Device
<select id="gpuDeviceSetting"></select>
</label>
<label hidden>
Voice in Text Chat
<select id="witnessChatTtsSetting">
<option value="0">Off — text only</option>
</select>
</label>
<label>
GPU Layers
<input id="gpuLayersSetting" type="text" placeholder="auto, 0, or an explicit count" autocomplete="off" />
</label>
<label hidden>
Witness Voice Directory
<input id="voiceDirSetting" type="text" autocomplete="off" />
</label>
</section>
<p id="llamaStatusText" class="llama-status">AI backend status unknown.</p>
<div class="settings-actions">
<button id="settingsSaveButton" type="button">Save</button>
<button id="llamaStartButton" type="button">Start Backend</button>
<button id="llamaRestartButton" type="button">Restart Backend</button>
<button id="llamaStopButton" type="button">Stop Backend</button>
</div>
</form>
</dialog>
<dialog id="noticeDialog" class="notice-dialog">
<form method="dialog" class="notice-panel">
<header>
<div><small>Public appeal at <span id="noticeJunctionLabel">selected junction</span></small><h2>Issue Public Notice</h2></div>
<button id="noticeCloseButton" type="button" aria-label="Close notice">x</button>
</header>
<textarea id="noticeText" spellcheck="true"></textarea>
<p id="lookoutMeta">The wording controls which existing witnesses recognize the appeal.</p>
<div class="dialog-actions">
<button id="raiseLookoutButton" type="button">Publish Notice</button>
<button id="noticeCancelButton" type="button">Cancel</button>
</div>
</form>
</dialog>
<dialog id="witnessDialog" class="witness-dialog">
<section class="witness-interview-shell">
<header>
<div>
<small>Witness Interview</small>
<h2 id="witnessName">Witness</h2>
<p id="witnessProfile"></p>
</div>
<div class="interview-status"><span id="witnessConnection">Text ready</span><button id="witnessCloseButton" type="button">End Interview</button></div>
</header>
<div class="witness-summary" id="witnessSummary"></div>
<div class="witness-transcript" id="witnessTranscript" aria-live="polite"></div>
<footer>
<div class="speech-controls" hidden>
<button id="autoSpeechButton" type="button" hidden>Start Auto Speech</button>
<button id="pushToTalkButton" type="button" hidden>Hold to Talk</button>
<button id="stopAudioButton" type="button" hidden>Stop Voice</button>
<meter id="micLevel" min="0" max="1" value="0" hidden></meter>
</div>
<div class="text-chat-row">
<textarea id="witnessMessage" placeholder="Ask the witness a question..."></textarea>
<button id="sendWitnessMessage" type="button">Send</button>
</div>
</footer>
</section>
</dialog>
<dialog id="storyDialog" class="story-dialog">
<section class="story-reveal-shell">
<header><div><small>Private Case Record</small><h2>John Doe: Turn-by-Turn Story</h2></div><button id="storyCloseButton" type="button">Close</button></header>
<div id="storyTimeline" class="story-timeline"></div>
<footer id="storyFooter"></footer>
</section>
</dialog>
<script type="module" src="/static/app.js?v=26"></script>
</body>
</html>