File size: 23,738 Bytes
f25362a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="color-scheme" content="dark light" />
  <title>JackAILocal</title>
  <link rel="stylesheet" href="styles.css" />
</head>
<body>
  <div id="app" class="app-shell">
    <aside class="sidebar" aria-label="JackAILocal navigation">
      <div class="brand-card">
        <div class="brand-mark">J</div>
        <div>
          <div class="brand-name">JackAILocal</div>
          <div class="brand-subtitle" data-i18n="brandSubtitle">Offline local AI</div>
        </div>
      </div>

      <div class="language-switch" aria-label="Language">
        <button type="button" class="lang-btn active" data-lang="en">EN</button>
        <button type="button" class="lang-btn" data-lang="fr">FR</button>
      </div>

      <div id="statusBox" class="status-card state-loading" role="status" aria-live="polite">
        <div class="status-header">
          <span class="status-dot"></span>
          <span id="runtimeStatus" class="status-title" data-i18n="statusStarting">Starting</span>
        </div>
        <div id="hardwareLine" class="status-subtitle" data-i18n="statusLocalScan">Local scan...</div>
      </div>

      <nav class="nav-list">
        <button class="nav active" data-view="dashboard"><span>⌂</span><span data-i18n="navDashboard">Dashboard</span></button>
        <button class="nav" data-view="chat"><span>◌</span><span data-i18n="navChat">Chat</span></button>
        <button class="nav" data-view="models"><span>▣</span><span data-i18n="navModels">Models</span></button>
        <button class="nav" data-view="documents"><span>□</span><span data-i18n="navDocuments">Documents</span></button>
        <button class="nav" data-view="benchmark"><span>◇</span><span data-i18n="navBenchmark">Benchmark</span></button>
        <button class="nav" data-view="settings"><span>⚙</span><span data-i18n="navSettings">Settings</span></button>
        <button class="nav" data-view="support"><span>?</span><span data-i18n="navSupport">Support</span></button>
        <button class="nav" data-view="scout"><span>V</span><span data-i18n="navScout">SCOUT Vision</span></button>
        <button class="nav" data-view="voice"><span>A</span><span data-i18n="navVoice">Voice</span></button>
        <button class="nav" data-view="field-manual"><span>F</span><span data-i18n="navFieldManual">Field Manual</span></button>
        <button class="nav" data-view="transfers"><span>X</span><span data-i18n="navTransfers">Import / Export</span></button>
        <button class="nav" data-view="phone"><span>P</span><span data-i18n="navPhone">Phone Access</span></button>
      </nav>

      <div class="sidebar-footer">
        <div class="mini-label" data-i18n="networkMode">Network mode</div>
        <div class="security-pill">127.0.0.1</div>
        <div class="mini-copy" data-i18n="loopbackOnly">Local access only</div>
        <div id="licenseWarning" class="security-pill availability-off hidden"></div>
      </div>
    </aside>

    <main class="main">
      <div class="app-topbar">
        <div>
          <div class="eyebrow" data-i18n="eyebrow">Portable private AI</div>
          <h1 id="viewTitle">Dashboard</h1>
        </div>
        <div class="top-actions">
          <button id="refreshAll" class="secondary" data-i18n="refreshButton">Refresh</button>
          <button id="openSupport" class="secondary" data-i18n="supportButton">Support</button>
        </div>
      </div>

      <div id="errorBanner" class="banner hidden" role="status"></div>

      <section id="view-dashboard" class="view active">
        <div class="hero-panel">
          <div>
            <div class="hero-kicker" data-i18n="heroKicker">Ready for offline work</div>
            <h2 data-i18n="heroTitle">Local AI, loaded from your USB drive.</h2>
            <p data-i18n="heroSubtitle">JackAILocal runs the launcher, runtime, models, documents, and UI locally. No cloud is required after preparation.</p>
          </div>
          <div class="hero-actions">
            <button class="primary" data-action-view="chat" data-i18n="startChatButton">Start chat</button>
            <button class="secondary" data-action-view="models" data-i18n="checkModelsButton">Check models</button>
          </div>
        </div>

        <div class="metric-grid">
          <article class="metric-card">
            <span class="metric-label" data-i18n="runtimeCard">Runtime</span>
            <strong id="runtimeMetric">...</strong>
            <small id="backendMetric">...</small>
          </article>
          <article class="metric-card">
            <span class="metric-label" data-i18n="hardwareCard">Hardware</span>
            <strong id="hardwareMetric">...</strong>
            <small id="gpuMetric">...</small>
          </article>
          <article class="metric-card">
            <span class="metric-label" data-i18n="modelCard">Recommended model</span>
            <strong id="recommendedMetric">Auto</strong>
            <small data-i18n="autoSelectHint">Selected by local policy</small>
          </article>
          <article class="metric-card">
            <span class="metric-label" data-i18n="privacyCard">Privacy</span>
            <strong data-i18n="offlineLocked">Offline locked</strong>
            <small data-i18n="localOnlyHint">Loopback only</small>
          </article>
        </div>

        <div class="split-hero">
          <div class="panel">
            <h2 data-i18n="setupTitle">Preparation state</h2>
            <div class="checklist">
              <div class="check-row" id="checkRuntime"><span></span><div><strong data-i18n="checkRuntimeTitle">Local API</strong><small data-i18n="checkRuntimeText">jackailocald must be running.</small></div></div>
              <div class="check-row" id="checkModels"><span></span><div><strong data-i18n="checkModelsTitle">Model catalog</strong><small data-i18n="checkModelsText">The USB must contain configured model weights.</small></div></div>
              <div class="check-row" id="checkSecurity"><span></span><div><strong data-i18n="checkSecurityTitle">Security mode</strong><small data-i18n="checkSecurityText">Shell tools and LAN exposure remain disabled.</small></div></div>
            </div>
          </div>
          <div class="panel">
            <h2 data-i18n="quickActionsTitle">Quick actions</h2>
            <div class="quick-actions">
              <button class="secondary" data-action-view="benchmark" data-i18n="runBenchmarkQuick">Run benchmark</button>
              <button class="secondary" data-action-view="documents" data-i18n="openDocsQuick">Open documents</button>
              <button class="secondary" id="supportBundleQuick" data-i18n="createSupportQuick">Create support bundle</button>
            </div>
            <pre id="dashboardOut" class="terminal small-terminal"></pre>
          </div>
        </div>
      </section>

      <section id="view-chat" class="view">
        <div class="chat-workspace">
          <aside class="thread-panel">
            <div class="thread-header">
              <h2 data-i18n="threadsTitle">Threads</h2>
              <button id="newThread" class="primary compact" data-i18n="newThreadButton">New</button>
            </div>
            <input id="threadFilter" data-i18n-placeholder="threadFilterPlaceholder" placeholder="Filter threads" />
            <div id="threadList" class="thread-list"></div>
          </aside>
          <div class="chat-panel">
            <div class="chat-thread-bar">
              <strong id="activeThreadTitle" data-i18n="newConversation">New conversation</strong>
              <div>
                <button id="renameThread" class="secondary compact" data-i18n="renameButton">Rename</button>
                <button id="deleteThread" class="secondary compact danger-button" data-i18n="deleteButton">Delete</button>
              </div>
            </div>
            <div id="chatLog" class="chat-log" aria-live="polite"></div>
            <div class="composer-card">
              <textarea id="prompt" spellcheck="true" data-i18n-placeholder="promptPlaceholder" placeholder="Write your request here. Enter = send, Shift+Enter = new line."></textarea>
              <div class="composer-actions">
                <label class="select-label"><span data-i18n="modelLabel">Model</span><select id="modelSelect"><option value="auto">Auto</option></select></label>
                <button id="clearChat" class="secondary" data-i18n="clearButton">Clear</button>
                <button id="sendBtn" class="primary" data-i18n="sendButton">Send</button>
              </div>
            </div>
          </div>
          <aside class="side-panel">
            <h2 data-i18n="chatHelperTitle">Assistant mode</h2>
            <p data-i18n="chatHelperText">Use Auto unless you need a specific installed model. Threads are stored on this JackAILocal drive.</p>
            <div id="modelChips" class="chip-list"></div>
            <div class="hint-box"><strong data-i18n="offlineNoteTitle">Offline note</strong><span data-i18n="offlineNoteText">Answers come from local models. Do not expect cloud frontier quality.</span></div>
          </aside>
        </div>
      </section>

      <section id="view-scout" class="view">
        <div class="section-header">
          <div><h2 data-i18n="scoutTitle">SCOUT Vision</h2><p data-i18n="scoutSubtitle">Analyze an image with an installed local vision model.</p></div>
        </div>
        <div class="split">
          <div class="panel">
            <input id="scoutImage" type="file" accept="image/*" />
            <img id="scoutPreview" class="image-preview hidden" alt="Selected image preview" />
            <textarea id="scoutPrompt" class="doc-textarea" data-i18n-placeholder="scoutPromptPlaceholder" placeholder="Describe the image and extract readable text."></textarea>
            <button id="runScout" class="primary" data-i18n="analyzeButton">Analyze locally</button>
          </div>
          <div class="panel">
            <h2 data-i18n="scoutResultTitle">Analysis result</h2>
            <pre id="scoutOut" class="terminal"></pre>
          </div>
        </div>
      </section>

      <section id="view-voice" class="view">
        <div class="section-header">
          <div><h2 data-i18n="voiceTitle">Voice Mode</h2><p data-i18n="voiceSubtitle">Local speech-to-text with whisper.cpp and text-to-speech with Piper.</p></div>
          <button id="refreshVoice" class="secondary" data-i18n="refreshButton">Refresh</button>
        </div>
        <div class="split">
          <div class="panel">
            <h2 data-i18n="sttTitle">Speech to text</h2>
            <p id="sttStatus" class="muted"></p>
            <div class="button-row">
              <button id="startRecording" class="primary" data-i18n="recordButton">Record</button>
              <button id="stopRecording" class="secondary" disabled data-i18n="stopButton">Stop</button>
            </div>
            <textarea id="voiceTranscript" class="doc-textarea" data-i18n-placeholder="transcriptPlaceholder" placeholder="Transcript"></textarea>
            <button id="sendTranscript" class="secondary" data-i18n="sendToChatButton">Send to chat</button>
          </div>
          <div class="panel">
            <h2 data-i18n="ttsTitle">Text to speech</h2>
            <p id="ttsStatus" class="muted"></p>
            <textarea id="ttsText" class="doc-textarea" data-i18n-placeholder="ttsPlaceholder" placeholder="Text to speak"></textarea>
            <button id="speakText" class="primary" data-i18n="speakButton">Speak locally</button>
            <audio id="ttsAudio" controls class="audio-player hidden"></audio>
            <pre id="voiceOut" class="terminal small-terminal"></pre>
          </div>
        </div>
      </section>

      <section id="view-field-manual" class="view">
        <div class="section-header">
          <div><h2 data-i18n="fieldManualTitle">Field Manual</h2><p data-i18n="fieldManualSubtitle">Offline reference cards with an Ask AI action.</p></div>
        </div>
        <div id="fieldManualWarning" class="banner hidden"></div>
        <div id="fieldManualCards" class="cards"></div>
      </section>

      <section id="view-models" class="view">
        <div class="section-header">
          <div><h2 data-i18n="modelsTitle">Models</h2><p data-i18n="modelsSubtitle">The catalog shows what is configured. Model weights must be present in models/.</p></div>
          <button id="refreshModels" class="secondary" data-i18n="refreshButton">Refresh</button>
        </div>
        <div id="modelCards" class="cards"></div>
        <div class="panel safe-panel">
          <h2 data-i18n="agentTitle">LLM Config Agent</h2>
          <p data-i18n="agentSubtitle">A real installed local LLM can review hardware, model selection, and client configuration. Preferred model: gemma4:12b.</p>
          <div class="form-grid">
            <label class="select-label"><span data-i18n="agentModelLabel">Agent model</span><select id="agentModelSelect"><option value="auto">Auto</option></select></label>
            <textarea id="agentNotes" class="doc-textarea" data-i18n-placeholder="agentNotesPlaceholder" placeholder="Client constraints, use case, budget, privacy needs..."></textarea>
            <button id="runAgentReview" class="primary" data-i18n="agentRunButton">Run local agent review</button>
          </div>
          <pre id="agentOut" class="terminal small-terminal"></pre>
        </div>
        <div class="panel safe-panel">
          <h2 data-i18n="addModelTitle">Add a model to the catalog</h2>
          <p data-i18n="addModelSubtitle">This registers a model. It does not download weights and it does not bypass builder validation.</p>
          <div class="form-grid">
            <input id="addModelId" data-i18n-placeholder="addModelIdPlaceholder" placeholder="local id, example: qwen-custom" />
            <input id="addModelLabel" data-i18n-placeholder="addModelLabelPlaceholder" placeholder="display name" />
            <button id="addModelBtn" class="primary" data-i18n="addButton">Add</button>
          </div>
          <pre id="addModelOut" class="terminal small-terminal"></pre>
        </div>
      </section>

      <section id="view-documents" class="view">
        <div class="section-header">
          <div><h2 data-i18n="documentsTitle">Documents</h2><p data-i18n="documentsSubtitle">Local storage in workspace/documents. No arbitrary disk access.</p></div>
          <button id="reloadDocs" class="secondary" data-i18n="refreshButton">Refresh</button>
        </div>
        <div class="split">
          <div class="panel">
            <h2 data-i18n="createNoteTitle">Create a note</h2>
            <input id="docName" data-i18n-placeholder="docNamePlaceholder" placeholder="document-name.txt" />
            <textarea id="docContent" data-i18n-placeholder="docContentPlaceholder" placeholder="Text, notes, procedures, prompts..." class="doc-textarea"></textarea>
            <button id="saveDoc" class="primary" data-i18n="saveLocalButton">Save locally</button>
            <pre id="docOut" class="terminal small-terminal"></pre>
          </div>
          <div class="panel">
            <h2 data-i18n="localDocsTitle">Local documents</h2>
            <div id="docList" class="doc-list"></div>
          </div>
        </div>
      </section>

      <section id="view-transfers" class="view">
        <div class="section-header">
          <div><h2 data-i18n="transfersTitle">Encrypted Import / Export</h2><p data-i18n="transfersSubtitle">Transfer threads and documents with an AES-256-GCM encrypted pack.</p></div>
        </div>
        <div class="split">
          <div class="panel">
            <h2 data-i18n="exportTitle">Export</h2>
            <input id="exportPassphrase" type="password" data-i18n-placeholder="passphrasePlaceholder" placeholder="Passphrase, minimum 8 characters" />
            <div class="option-stack compact-stack">
              <label class="option-card"><input id="exportThreads" type="checkbox" checked /><span class="option-copy"><strong data-i18n="includeThreads">Include threads</strong></span></label>
              <label class="option-card"><input id="exportDocuments" type="checkbox" checked /><span class="option-copy"><strong data-i18n="includeDocuments">Include documents</strong></span></label>
              <label class="option-card"><input id="exportSettings" type="checkbox" /><span class="option-copy"><strong data-i18n="includeSettings">Include safe settings</strong></span></label>
            </div>
            <button id="exportPack" class="primary" data-i18n="exportButton">Export encrypted pack</button>
          </div>
          <div class="panel">
            <h2 data-i18n="importTitle">Import</h2>
            <input id="importPackFile" type="file" accept=".jackaipack,application/json" />
            <input id="importPassphrase" type="password" data-i18n-placeholder="passphrasePlaceholder" placeholder="Passphrase, minimum 8 characters" />
            <label class="select-label"><span data-i18n="importMode">Import mode</span><select id="importMode"><option value="merge">Merge</option><option value="replace">Replace threads</option></select></label>
            <button id="importPack" class="primary" data-i18n="importButton">Import encrypted pack</button>
          </div>
        </div>
        <pre id="transferOut" class="terminal small-terminal"></pre>
      </section>

      <section id="view-phone" class="view">
        <div class="section-header">
          <div><h2 data-i18n="phoneTitle">Phone Access</h2><p data-i18n="phoneSubtitle">Explicit local-network access with a pairing token. Disabled by default.</p></div>
          <button id="refreshPhone" class="secondary" data-i18n="refreshButton">Refresh</button>
        </div>
        <div class="split">
          <div class="panel">
            <div id="phoneState" class="terminal small-terminal"></div>
            <div class="button-row">
              <button id="enablePhone" class="primary" data-i18n="enablePhoneButton">Enable Phone Access</button>
              <button id="disablePhone" class="secondary" data-i18n="disablePhoneButton">Disable Phone Access</button>
              <button id="restartRuntime" class="secondary" data-i18n="restartRuntimeButton">Restart runtime</button>
            </div>
            <p class="muted" data-i18n="phoneRestartNote">A runtime restart is required after changing this mode.</p>
          </div>
          <div class="panel qr-panel">
            <div id="phoneQr"></div>
          </div>
        </div>
      </section>

      <section id="view-benchmark" class="view">
        <div class="section-header">
          <div><h2 data-i18n="benchmarkTitle">Benchmark</h2><p data-i18n="benchmarkSubtitle">Short test to validate loading, latency, and a real response.</p></div>
          <button id="runBenchmark" class="primary" data-i18n="runTestButton">Run test</button>
        </div>
        <pre id="benchmarkOut" class="terminal"></pre>
      </section>

      <section id="view-settings" class="view">
        <div class="section-header">
          <div>
            <h2 data-i18n="settingsTitle">Settings</h2>
            <p data-i18n="settingsSubtitle">Options stay simple. Dangerous features remain disabled.</p>
          </div>
        </div>
        <div class="settings-grid">
          <div class="panel settings-panel">
            <h2 data-i18n="modeTitle">Mode</h2>
            <p class="muted" data-i18n="settingsModeHint">Simple mode is the default for non-technical users. Advanced mode keeps unsafe tools blocked.</p>
            <div class="option-stack">
              <label class="option-card">
                <input type="radio" name="mode" value="simple" checked />
                <span class="option-copy">
                  <strong data-i18n="simpleMode">Simple</strong>
                  <small data-i18n="simpleModeDesc">Recommended default. Minimal choices and automatic model selection.</small>
                </span>
              </label>
              <label class="option-card">
                <input type="radio" name="mode" value="advanced" />
                <span class="option-copy">
                  <strong data-i18n="advancedMode">Advanced</strong>
                  <small data-i18n="advancedModeDesc">Shows more controls without enabling shell or system tools.</small>
                </span>
              </label>
              <label class="option-card">
                <input type="checkbox" id="offlineLock" checked />
                <span class="option-copy">
                  <strong data-i18n="offlineLock">Offline lock</strong>
                  <small data-i18n="offlineLockDesc">Keeps JackAILocal bound to the local machine by default.</small>
                </span>
              </label>
              <label class="option-card">
                <input type="checkbox" id="toolsLock" checked />
                <span class="option-copy">
                  <strong data-i18n="toolsLock">Block shell/system tools</strong>
                  <small data-i18n="toolsLockDesc">Prevents agent-style system actions in the consumer build.</small>
                </span>
              </label>
            </div>
            <div class="settings-actions">
              <button id="saveSettings" class="primary" data-i18n="saveButton">Save</button>
            </div>
            <pre id="settingsOut" class="terminal small-terminal"></pre>
          </div>
          <div class="panel settings-panel">
            <h2 data-i18n="activeFeaturesTitle">Active features</h2>
            <p class="muted" data-i18n="activeFeaturesSubtitle">Runtime capabilities are read from the local API when it is running.</p>
            <div id="featureList" class="feature-list"></div>
          </div>
          <div class="panel settings-panel">
            <h2 data-i18n="licenseTitle">License</h2>
            <p class="muted" data-i18n="licenseSubtitle">License status of this installation. Verified locally, no internet required.</p>
            <div id="licenseInfo" class="feature-list"></div>
            <div class="license-install">
              <input id="licenseFile" type="file" accept=".json,application/json" />
              <button id="installLicense" class="secondary" data-i18n="installLicenseButton">Install license file</button>
              <div id="licenseMessage" class="muted small"></div>
            </div>
            <p class="muted small" data-i18n="legalDocsHint">Legal documents are in the legal/ and licenses/ folders of this installation.</p>
          </div>
        </div>
      </section>

      <section id="view-support" class="view">
        <div class="section-header">
          <div><h2 data-i18n="supportTitle">Support</h2><p data-i18n="supportSubtitle">Local diagnostics without conversations by default.</p></div>
          <button id="supportBundle" class="primary" data-i18n="prepareReportButton">Prepare report</button>
        </div>
        <pre id="supportOut" class="terminal"></pre>
      </section>
    </main>
  </div>

  <div id="eulaOverlay" class="eula-overlay hidden" role="dialog" aria-modal="true" aria-labelledby="eulaTitle">
    <div class="panel eula-modal">
      <h2 id="eulaTitle" data-i18n="eulaTitle">End User License Agreement</h2>
      <div id="eulaText" class="eula-text" tabindex="0"></div>
      <div class="eula-actions">
        <button id="eulaAccept" class="primary" data-i18n="eulaAcceptButton">I accept</button>
      </div>
    </div>
  </div>

  <script src="app-v15.js"></script>
</body>
</html>