| <!-- |
| Copyright 2025 The Chromium Authors |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| <!doctype html> |
| <meta charset="utf-8" />. |
| |
| <style> |
| html { |
| height: 100%; |
| background-color: #00ff00; |
| } |
| body { |
| background-color: white; |
| height: 100%; |
| width: 100%; |
| margin: 0; |
| overflow: hidden; |
| } |
| #onboarding { |
| background-color: white; |
| color: black; |
| height: 100%; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| #pageHeader.connected { |
| background-color: green; |
| } |
| .draggable { |
| -webkit-app-region: drag; |
| } |
| .not-draggable { |
| -webkit-app-region: no-drag; |
| } |
| #pageHeader { |
| background-color: blue; |
| color: white; |
| display: flex; |
| justify-content: space-between; |
| height: 2em; |
| margin: 0; |
| } |
| #pageHeader h1 { |
| margin: 0; |
| user-select: none; |
| } |
| .section h1 { |
| text-align: center; |
| font-size: 14pt; |
| user-select: none; |
| margin: 0.2em; |
| } |
| .section h2 { |
| font-size: 12pt; |
| user-select: none; |
| margin: 0.2em; |
| } |
| .section { |
| background-color: #eee; |
| box-shadow: 0 0 0.5em #999; |
| border: 1px solid black; |
| padding: 3px; |
| margin-top: 0.5em; |
| margin-bottom: 0.5em; |
| } |
| .permission-switch { |
| display: flex; |
| align-items: center; |
| margin-bottom: 0.3em; |
| } |
| .permission-switch label { |
| margin-right: 0.5em; |
| } |
| input[type="text"] { |
| width: 300px; |
| } |
| img { |
| border: thick double black; |
| background-image: linear-gradient(cyan, fuchsia); |
| } |
| .favicon { |
| width: 16px; |
| height: 16px; |
| } |
| #content { |
| overflow-y: scroll; |
| position: absolute; |
| top: 2em; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| } |
| #fileDrop { |
| border: double black; |
| } |
| #status { |
| font-size: smaller; |
| } |
| #successUI, |
| #localDenialUI, |
| #osDenialUI { |
| padding: 10px; |
| border: 1px solid #ccc; |
| margin-top: 10px; |
| } |
| |
| #localDenialUI { |
| border-color: orange; |
| } |
| |
| #osDenialUI { |
| border-color: red; |
| } |
| |
| #screenWakeLockStatus::before { |
| content: "Can sleep normally."; |
| } |
| #screenWakeLockStatus[lockStatus*="acquired"]::before { |
| content: "Locked in awake state!"; |
| background-color: fuchsia; |
| } |
| #screenWakeLockStatus[lockStatus*="unexpectedRelease"]::before { |
| content: "Awake lock was released unexpectedly."; |
| background-color: yellow; |
| } |
| #screenWakeLockStatus[lockStatus*="error"]::before { |
| content: "Error acquiring awake lock! (can still sleep)"; |
| background-color: red; |
| } |
| |
| #successUI { |
| border-color: green; |
| } |
| |
| *:disabled { |
| pointer-events: none; |
| opacity: 0.6; |
| } |
| fieldset:disabled :is(h1, h2, h3, h4, h5, h6) { |
| text-decoration: line-through; |
| } |
| |
| /* Dark Mode Styles */ |
| @media (prefers-color-scheme: dark) { |
| body { |
| background-color: #121212; |
| color: #eee; |
| } |
| |
| .section { |
| background-color: #2a2a2a; |
| box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.5); |
| border: 1px solid #444; |
| } |
| |
| a { |
| color: #99ccff; |
| } |
| |
| a:hover { |
| color: #66b3ff; |
| } |
| |
| a:visited { |
| color: #c8a2c8; |
| } |
| } |
| |
| .scrollToSelector { |
| margin-top: 16px; |
| } |
| |
| select.scrollToNodeSelect { |
| width: 300px; |
| margin-left: 4px; |
| } |
| |
| #additionalContextResult { |
| display: flex; |
| flex-direction: column; |
| width: 100%; |
| } |
| |
| #additionalContextResult > img { |
| max-width: 100%; |
| height: auto; |
| } |
| #captureRegionBtn[pressed] { |
| background-color: red; |
| color: white; |
| } |
| </style> |
| <div id="pageHeader" class="draggable"> |
| <h1>Test Web Client</h1> |
| <div class="permission-switch not-draggable"> |
| <label>Interaction Mode:</label> |
| <input type="radio" id="textmode" name="interactionMode" value="text" checked /> |
| <label for="textmode">Text</label> |
| <input type="radio" id="audiomode" name="interactionMode" value="audio" /> |
| <label for="audiomode">Audio</label> |
| </div> |
| <button id="refreshbn" class="not-draggable">🔄</button> |
| <button id="closebn" class="not-draggable">❌</button> |
| <button id="shutdownbn" class="not-draggable">⏻</button> |
| </div> |
| <div id="onboarding"> |
| <button id="onboardingbn" class="not-draggable">Continue</button> |
| </div> |
| <div id="content"> |
| <div class="section"> |
| <h1>Initialization</h1> |
| <label |
| >Fail Initialization next time: |
| <input id="failInitializationCheckbox" type="checkbox" /> |
| </label> |
| </div> |
| <div class="section"> |
| <h1>Closed Captions Setting</h1> |
| <div class="permission-switch"> |
| <label for="closedCaptioningSwitch">Closed Captioning:</label> |
| <input type="checkbox" id="closedCaptioningSwitch" disabled /> |
| <button id="setClosedCaptioningTrue" style="margin-left: 1em">Set True</button> |
| <button id="setClosedCaptioningFalse">Set False</button> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Chrome Permissions</h1> |
| <div class="permission-switch"> |
| <label for="microphoneSwitch">Microphone:</label> |
| <input type="checkbox" id="microphoneSwitch" disabled /> |
| </div> |
| <div class="permission-switch"> |
| <label for="geolocationSwitch">Geolocation:</label> |
| <input type="checkbox" id="geolocationSwitch" disabled /> |
| </div> |
| <div class="permission-switch"> |
| <label for="tabContextSwitch">Tab Context:</label> |
| <input type="checkbox" id="tabContextSwitch" disabled /> |
| </div> |
| <div class="permission-switch"> |
| <label for="defaultTabContextSwitch">Default Tab Context:</label> |
| <input type="checkbox" id="defaultTabContextSwitch" disabled /> |
| </div> |
| <div class="permission-switch"> |
| <label for="actuationOnWebSwitch">Actuation on Web:</label> |
| <input type="checkbox" id="actuationOnWebSwitch" disabled /> |
| </div> |
| <select id="permissionSelect"> |
| <option value="geolocation">Geolocation</option> |
| <option value="microphone">Microphone</option> |
| <option value="tabContext">Tab Context</option> |
| </select> |
| <select id="enabledSelect"> |
| <option value="true">True</option> |
| <option value="false">False</option> |
| </select> |
| <button id="testPermissionSwitch">Simulate External Permission Update</button> |
| </div> |
| <div class="section"> |
| <fieldset id="macOsPermissionsFieldset"> |
| <h1>MacOS Permissions</h1> |
| <button id="getOsMicrophonePermissionButton">Get OS Microphone permission state</button> |
| <span id="osMicrophonePermissionResult"></span> |
| <div> |
| <div class="permission-switch"> |
| <label for="osGeolocationPermissionSwitch">OS Geolocation:</label> |
| <input type="checkbox" id="osGeolocationPermissionSwitch" disabled /> |
| </div> |
| <button id="openOsLocationSettings">Open OS Location settings</button> |
| <button id="openOsMicrophoneSettings">Open OS Microphone settings</button> |
| </div> |
| </fieldset> |
| </div> |
| <div class="section"> |
| <h1>Glic Settings</h1> |
| <div> |
| <label for="osGlicHotkey">Current keyboard shortcut:</label> |
| <input type="text" id="osGlicHotkey" name="osGlicHotkey" readonly /> |
| <div> |
| <button id="openGlicSettings">Open Glic Settings</button> |
| <br /> |
| <label for="openGlicSettingsHighlight">And highlight field:</label> |
| <select id="openGlicSettingsHighlight"> |
| <option value="none">None</option> |
| <option value="osHotkey">Keyboard shortcut</option> |
| <option value="osEntrypointToggle">OS Entrypoint</option> |
| </select> |
| </div> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Sizing</h1> |
| <button id="enableTestSizingMode">Test Sizing</button> |
| <label><input id="enableDragResizeCheckbox" type="checkbox" />Enable drag resizing</label> |
| </div> |
| <div class="section"> |
| <h1>State</h1> |
| <div> |
| <label |
| >panelActive |
| <input id="panelActiveCheckbox" type="checkbox" disabled /> |
| </label> |
| <br /> |
| <label |
| >Enable tab context access indicator UI: |
| <input id="contextAccessIndicator" type="checkbox" /> |
| </label> |
| </div> |
| <div> |
| <h4>Focused Tab V1</h4> |
| <label>Focused Tab Url: </label><input id="focusedUrl" type="text" /> <br /> |
| <label>Favicon: </label><img class="favicon" id="focusedFavicon" /> |
| </div> |
| <div> |
| <h4>Focused Tab V2</h4> |
| <label>Focused Tab Url: </label><input id="focusedUrlV2" type="text" /> <br /> |
| <label>Favicon: </label><img class="favicon" id="focusedFaviconV2" /> <br /> |
| <span id="focusedTabLogsV2"></span> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Test createTab()</h1> |
| <label>Url: </label><input id="URL" type="text" value="https://news.ycombinator.com" /> |
| <br /> |
| <label |
| >In Background? |
| <input id="createTabInBackground" type="checkbox" /> |
| </label> |
| <br /> |
| <button id="newtabbn">Create New Tab</button> |
| <br /> |
| </div> |
| <div class="section"> |
| <fieldset id="attachmentControlsFieldset"> |
| <h1>Attachment Controls</h1> |
| <div> |
| <label |
| >Can Attach: |
| <input id="canAttachCheckbox" type="checkbox" disabled /> |
| </label> |
| </div> |
| <button id="attachpanelbn">Attach Panel</button> |
| |
| <button id="detachpanelbn">Detach Panel</button> |
| </fieldset> |
| </div> |
| <div class="section"> |
| <h1>Test getContextFromFocusedTab()</h1> |
| <div> |
| <button id="getpagecontext">Get page Context</button> |
| <div> |
| <label> <input type="checkbox" id="innerTextCheckbox" /> Inner Text </label> |
| <br /> |
| <label> innerTextBytesLimit: <input id="innerTextBytesLimit" value="100" /> </label> |
| </div> |
| <label> <input type="checkbox" id="viewportScreenshotCheckbox" /> Viewport Screenshot </label> |
| <label> <input type="checkbox" id="pdfDataCheckbox" /> PDF Data </label> |
| <label> |
| <input type="checkbox" id="annotatedPageContentCheckbox" /> |
| Annotated Page Content |
| </label> |
| </div> |
| <div> |
| <img class="favicon" id="faviconImg" /> |
| </div> |
| <div id="screenshot"> |
| <img id="screenshotImg" /> |
| </div> |
| <span id="getPageContextResult"></span> |
| <span id="getPageContextStatus"></span> |
| </div> |
| <div class="section" id="pageMetadataSection"> |
| <h1>Page Metadata</h1> |
| <label |
| >Available Tabs: |
| <select id="pageMetadataTabsList"></select |
| ></label> |
| <button id="pageMetadataRefreshTabs">Refresh Tabs</button> |
| <button id="pageMetadataOpenTestPage">Open Test Page</button> |
| <br /> |
| <label |
| >Names (comma-separated): |
| <input type="text" id="pageMetadataNames" value="author,description" |
| /></label> |
| <br /> |
| <button id="pageMetadataSubscribe">Subscribe</button> |
| <button id="pageMetadataUnsubscribe">Unsubscribe</button> |
| <p id="pageMetadataStatus"></p> |
| <textarea id="pageMetadataResult" rows="10" cols="80" readonly></textarea> |
| </div> |
| <div class="section"> |
| <h1>Annotated Page Content</h1> |
| <div> |
| <button id="copyAPCToClipboardBtn">Copy APC to Clipboard</button> |
| </div> |
| <span id="APCResult"></span> |
| </div> |
| <div class="section"> |
| <h1>Test Location Access</h1> |
| <button id="getlocation">Get Location</button> <br /> |
| <div id="location"></div> |
| <div id="locationStatus"></div> |
| <div id="locationOsErrorUI" style="display: none"> |
| <button id="openOsLocationSettingsButton">Open OS Settings</button> |
| </div> |
| <div id="locationGlicErrorUI" style="display: none"> |
| <button id="openGlicLocationSettingsButton">Open Glic Settings</button> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Test Link Out Behavior</h1> |
| <a href="https://www.example.com" id="link" target="_blank" rel="noopener noreferrer" |
| >https://www.example.com |
| </a> |
| </div> |
| <div class="section"> |
| <h1>UserProfile</h1> |
| <div> |
| <button id="getUserProfileInfoBn">getUserProfileInfo()</button> Status: |
| <span id="getUserProfileInfoStatus"></span> |
| <img id="getUserProfileInfoImg" /> |
| </div> |
| <div> |
| <button id="changeProfileBn">showProfilePicker()</button> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Misc</h1> |
| <button id="syncCookiesBn">refreshSignInCookies()</button> Status: |
| <span id="syncCookieStatus"></span> |
| <button id="testLogsBn">test logs</button> |
| <button id="testClipboardSave">Save Junk To Clipboard</button> |
| <button id="maybeRefreshUserStatusBn">maybeRefreshUserStatus()</button> |
| </div> |
| <div class="section"> |
| <h1>Demo Test Utils</h1> |
| <button id="reloadpage">Reload page</button> |
| <div> |
| Navigate webview to |
| <input id="navigateWebviewUrl" type="text" value="https://www.google.com" /> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>File</h1> |
| <div id="fileDrop"> |
| <button id="showDirectoryPicker">show directory picker</button> |
| <p>Drag files here</p> |
| <div id="fileDropList"></div> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Audio Output</h1> |
| <audio controls src="./bear-opus.ogg"></audio> |
| <div> |
| <button id="audioDuckingOn">Ducking On</button> |
| <button id="audioDuckingOff">Ducking Off</button> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Audio Capture</h1> |
| <button id="audioCapStart">START</button> |
| <button id="audioCapStop">STOP</button> |
| |
| After pressing STOP, this will play recorded sound. |
| <br /> |
| <audio id="mic" controls></audio><br /> |
| <div></div> |
| <div id="audioStatus"></div> |
| </div> |
| <div class="section"> |
| <h1>Screen Wake Lock</h1> |
| <label for="screenWakeLockSwitch" |
| >Keep screen awake: |
| <input type="checkbox" id="screenWakeLockSwitch" /> |
| </label> |
| <br /> |
| <label id="screenWakeLockStatus" lockStatus="released" /> |
| </div> |
| <div class="section"> |
| <h1>Desktop Screenshot</h1> |
| <button id="desktopScreenshot">Capture</button> |
| <button id="panelScreenshot">Capture Panel</button> |
| <span id="desktopScreenshotErrorReason"></span> |
| <img id="desktopScreenshotImg" /> |
| </div> |
| <div class="section"> |
| <h1>Region Capture</h1> |
| <button id="captureRegionBtn">Capture Region</button> |
| <ul id="captureRegionResultList"></ul> |
| <label for="deleteCaptureRegion">Delete Region ID:</label> |
| <input type="text" id="deleteCaptureRegion" name="deleteCaptureRegion" /> |
| <button id="deleteCaptureRegionBtn">Delete Region</button> |
| </div> |
| <div class="section"> |
| <h1>Scroll-To and Highlight</h1> |
| <div> |
| <button id="scrollToFetchAPCBn">Fetch AnnotatedPageContent</button> |
| <div style="margin-top: 4px">Document ID: <span id="scrollToDocumentId">null</span></div> |
| <div>URL: <span id="scrollToURL">null</span></div> |
| </div> |
| <div class="scrollToSelector"> |
| <h2>Exact Text Selector</h2> |
| <input |
| id="scrollToExactText" |
| type="text" |
| placeholder="Add exact text to scroll to and highlight" |
| /> |
| <div> |
| <label>Search Range Start Node</label |
| ><select disabled id="scrollToExactTextSearchStart" class="scrollToNodeSelect"></select> |
| </div> |
| </div> |
| <div class="scrollToSelector"> |
| <h2>Text Fragment Selector</h2> |
| <input id="scrollToTextFragmentTextStart" type="text" placeholder="textStart" /> |
| <input id="scrollToTextFragmentTextEnd" type="text" placeholder="textEnd" /> |
| <div> |
| <label>Search Range Start Node</label |
| ><select disabled id="scrollToTextFragmentSearchStart" class="scrollToNodeSelect"></select> |
| </div> |
| </div> |
| <div class="scrollToSelector"> |
| <h2>Node Selector</h2> |
| <div> |
| <label>Node</label |
| ><select disabled id="scrollToNode" class="scrollToNodeSelect"></select> |
| </div> |
| </div> |
| <button id="scrollToBn" style="margin-top: 12px">Scroll!</button> |
| <div> |
| <h2>Test dropScrollToHighlight()</h2> |
| <button id="dropScrollToHighlightBtn">Drop Highlight</button> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Set Synthetic Experiment</h1> |
| <label for="trialName">Trial Name:</label> |
| <input type="text" id="trialName" name="trialName" /><br /><br /> |
| <label for="groupName">Group Name:</label> |
| <input type="text" id="groupName" name="groupName" /><br /><br /> |
| <button id="setExperiment">Set Experiment</button> |
| <span id="setExperimentStatus"></span> |
| </div> |
| <div class="section"> |
| <h1>Responsiveness Check</h1> |
| <label for="hangDuration">Hang Duration (seconds):</label> |
| <input type="number" id="hangDuration" value="21" step="any" min="0" /> |
| <button id="hang">Hang</button> |
| </div> |
| <div class="section"> |
| <h1>Mic Start Permissions Flow Demo</h1> |
| <button id="startMic">Start Mic</button> |
| <!-- Initially hidden UI elements --> |
| <div id="successUI" style="display: none"> |
| <p>Microphone access granted!</p> |
| </div> |
| |
| <div id="localDenialUI" style="display: none"> |
| <p>Microphone access denied locally. Please enable in settings.</p> |
| <button id="openLocalSettingsButton">Open Local Settings</button> |
| </div> |
| |
| <div id="osDenialUI" style="display: none"> |
| <p>Microphone access denied by OS. Please enable in OS settings.</p> |
| <button id="openOsSettingsButton">Open OS Settings</button> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Test performActions()</h1> |
| <div> |
| <label for="actorTaskId">Task ID</label> |
| <input id="actorTaskId" type="number" style="width: 8ch" /> |
| <button id="createActorTask">New</button> |
| <button id="stopActorTask">Stop</button> |
| </div> |
| <div> |
| <label> |
| <input type="text" id="actionProtoEncodedText" /> |
| Action Proto, base64-encoded from http://shortn/_pPzGXm1TZR |
| </label> |
| <button id="executeAction">Perform Actions</button> |
| </div> |
| <span id="actionStatus"></span> |
| <div id="credentialSelection" style="display: none"> |
| <label for="selectCredential">Select credential:</label> |
| <select id="selectCredential"></select> |
| <button id="credentialOnce">Allow once</button> |
| <button id="credentialAlways">Allow always</button> |
| <div> |
| <p>Credential details:</p> |
| <p>username: <span id="selectedCredentialUsername"></span></p> |
| <p>sourceSiteOrApp: <span id="selectedCredentialSource"></span></p> |
| <p>requestOrigin: <span id="selectedCredentialRequestOrigin"></span></p> |
| <p>icon: <img id="selectedCredentialIcon"></img></p> |
| <p>type: <span id="selectedCredentialType"></span></p> |
| <p>accountPicture: <img id="selectedCredentialAccountPicture"></img></p> |
| </div> |
| </div> |
| </div> |
| <div class="section" id="autofillSuggestionsDialogSection"> |
| <h1>Autofill Suggestion Dialog</h1> |
| <span id="autofillSuggestionsDialogStatus"></span> |
| <ul id="autofillSuggestionsList"></ul> |
| <div> |
| <label for="selectedAutofillSuggestionId">Selected ID:</label> |
| <input id="selectedAutofillSuggestionId" type="text" style="width: 8ch" /> |
| <button id="sendAutofillSuggestionsResponse">Send Response</button> |
| <button id="cancelAutofillSuggestionsDialog">Send Empty Response</button> |
| </div> |
| </div> |
| <div class="section" id="multiTabSection"> |
| <h1>Multi-Tab</h1> |
| <button id="pinFocusedTab">Share Current</button> |
| <button id="fetchPinned">⬆️</button> |
| <label>Fetch Screenshot?<input id="multiTabFetchScreenshot" type="checkbox" /></label> |
| <button id="unpin">❌</button> |
| <p>Shared</p> |
| <ul id="pinnedTabs"> |
| <li> |
| <span>some url</span> |
| <img class="favicon" /> |
| <span>some count</span> |
| <button>❌</button> |
| <button>⬆️</button> |
| </li> |
| </ul> |
| <p>Candidates</p> |
| <label>Fetch Candidates?<input type="checkbox" id="enableShareCandidates" /></label> |
| <div> |
| <input type="text" id="shareCandidateQuery" /> |
| <ul id="shareCandidates"></ul> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>MQLS Client ID</h1> |
| <button id="mqlsClientIdBtn">Get and log ID</button> |
| </div> |
| <div class="section"> |
| <h1>Invocation Log</h1> |
| <button id="clearInvocationLog">Clear</button> |
| <div id="invocationLog" style="max-height: 200px; overflow-y: auto; white-space: pre-wrap; font-family: monospace;"></div> |
| </div> |
| <div class="section"> |
| <h1>Skills</h1> |
| <ul id="skillsList"> |
| </ul> |
| <div> |
| <label>Skill ID: <input id="skillIdInput" type="text" /></label> |
| </div> |
| <div> |
| <label>Skill Name: <input id="skillNameInput" type="text" /></label> |
| </div> |
| <div> |
| <label>Skill Icon: <input id="skillIconInput" type="text" /></label> |
| </div> |
| <div> |
| <!-- LINT.IfChange(SkillSource) --> |
| <label>Skill Source: |
| <select id="skillSourceSelect"> |
| <option value="0">Unknown</option> |
| <option value="1" selected>First Party</option> |
| <option value="2">User Created</option> |
| <option value="3">Derived from First Party</option> |
| </select> |
| </label> |
| <!-- LINT.ThenChange(//components/skills/public/skill.mojom:SkillSource) --> |
| </div> |
| <div> |
| <label>Skill Prompt: <input id="skillPromptInput" type="text" /></label> |
| </div> |
| <div> |
| <button id="createSkillBtn">Create Skill</button> |
| <button id="updateSkillBtn">Update Skill</button> |
| <button id="getSkillBtn">Get Skill</button> |
| <button id="manageSkillsBtn">Manage Skills</button> |
| <button id="browseSkillsBtn">Browse Skills</button> |
| </div> |
| </div> |
| <div class="section"> |
| <h1>Additional Context</h1> |
| <div id="additionalContextResult"></div> |
| </div> |
| <div class="section"> |
| <h1>Conversations</h1> |
| <label>Conversation ID: <input id="conversationIdInput" type="text" /></label> |
| <label>Conversation Title: <input id="conversationTitleInput" type="text" /></label> |
| <button id="switchConversationBtn">Switch Conversation</button> |
| <button id="registerConversationBtn">Register Conversation</button> |
| <br /> |
| <label>Current Conversation ID: <input id="conversationId" type="text" readonly /></label> |
| </div> |
| <div class="section"> |
| <h1>Stress</h1> |
| <button id="stressTestEngageBtn">Engage</button> |
| <button id="stressTestEngageRetainBtn">Engage with retaining memory</button> |
| </div> |
| <br /> |
| <div id="status"></div> |
| </div> |
| <script src="./test_client.js" type="module"></script> |